diff --git a/AppKit/AppKit.j b/AppKit/AppKit.j
index 1feb31525..9241c6398 100644
--- a/AppKit/AppKit.j
+++ b/AppKit/AppKit.j
@@ -36,8 +36,8 @@
@import "CPCibLoading.j"
@import "CPCibOutletConnector.j"
@import "CPClipView.j"
-@import "CPCollectionViewItem.j"
@import "CPCollectionView.j"
+@import "CPCollectionViewItem.j"
@import "CPColor.j"
@import "CPColorPanel.j"
@import "CPColorWell.j"
@@ -53,8 +53,10 @@
@import "CPFont.j"
@import "CPFontManager.j"
@import "CPGeometry.j"
+@import "CPGraphics.j"
@import "CPImage.j"
@import "CPImageView.j"
+@import "CPKeyBinding.j"
@import "CPMenu.j"
@import "CPMenuItem.j"
@import "CPOpenPanel.j"
@@ -65,17 +67,17 @@
@import "CPProgressIndicator.j"
@import "CPRadio.j"
@import "CPResponder.j"
-@import "CPSearchField.j"
-@import "CPScrollView.j"
@import "CPScroller.j"
+@import "CPScrollView.j"
+@import "CPSearchField.j"
@import "CPSecureTextField.j"
@import "CPSegmentedControl.j"
@import "CPShadow.j"
@import "CPSlider.j"
@import "CPSplitView.j"
-@import "CPTabView.j"
@import "CPTableColumn.j"
@import "CPTableView.j"
+@import "CPTabView.j"
@import "CPText.j"
@import "CPTextField.j"
@import "CPToolbar.j"
@@ -87,3 +89,4 @@
@import "CPWebView.j"
@import "CPWindow.j"
@import "CPWindowController.j"
+@import "CPArrayController.j"
diff --git a/AppKit/CPAlert.j b/AppKit/CPAlert.j
index 2d1b11ea1..24180055d 100644
--- a/AppKit/CPAlert.j
+++ b/AppKit/CPAlert.j
@@ -39,26 +39,21 @@
@global
@group CPAlertStyle
*/
-CPWarningAlertStyle = 0;
+CPWarningAlertStyle = 0;
/*
@global
@group CPAlertStyle
*/
-CPInformationalAlertStyle = 1;
+CPInformationalAlertStyle = 1;
/*
@global
@group CPAlertStyle
*/
-CPCriticalAlertStyle = 2;
-
-
-var CPAlertWarningImage,
- CPAlertInformationImage,
- CPAlertErrorImage;
+CPCriticalAlertStyle = 2;
/*!
@ingroup appkit
-
+
CPAlert is an alert panel that can be displayed modally to present the
user with a message and one or more options.
@@ -74,41 +69,51 @@ var CPAlertWarningImage,
@delegate -(void)alertDidEnd:(CPAlert)theAlert returnCode:(int)returnCode;
Called when the user dismisses the alert by clicking one of the buttons.
@param theAlert the alert panel that the user dismissed
- @param returnCode the index of the button that the user clicked (starting from 0,
+ @param returnCode the index of the button that the user clicked (starting from 0,
representing the first button added to the alert which appears on the
right, 1 representing the next button to the left and so on)
*/
-@implementation CPAlert : CPObject
+@implementation CPAlert : CPView
{
CPPanel _alertPanel;
CPTextField _messageLabel;
+ CPTextField _informativeLabel;
CPImageView _alertImageView;
CPAlertStyle _alertStyle;
CPString _windowTitle;
int _windowStyle;
- int _buttonCount;
CPArray _buttons;
id _delegate;
+ SEL _didEndSelector;
+ id _modalDelegate;
}
-+ (void)initialize
++ (CPString)themeClass
{
- if (self != CPAlert)
- return;
+ return @"alert";
+}
- var bundle = [CPBundle bundleForClass:[self class]];
-
- CPAlertWarningImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-warning.png"]
- size:CGSizeMake(32.0, 32.0)];
-
- CPAlertInformationImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-information.png"]
- size:CGSizeMake(32.0, 32.0)];
-
- CPAlertErrorImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-error.png"]
- size:CGSizeMake(32.0, 32.0)];
++ (id)themeAttributes
+{
+ return [CPDictionary dictionaryWithObjects:[CGSizeMake(400.0, 110.0), CGInsetMake(15, 15, 15, 50), 6, 10,
+ CPJustifiedTextAlignment, [CPColor blackColor], [CPFont boldSystemFontOfSize:13.0], [CPNull null], CGSizeMakeZero(),
+ CPJustifiedTextAlignment, [CPColor blackColor], [CPFont systemFontOfSize:12.0], [CPNull null], CGSizeMakeZero(),
+ CGPointMake(15, 12),
+ [CPNull null],
+ [CPNull null],
+ [CPNull null]
+ ]
+ forKeys:[@"size", @"content-inset", @"informative-offset", @"button-offset",
+ @"message-text-alignment", @"message-text-color", @"message-text-font", @"message-text-shadow-color", @"message-text-shadow-offset",
+ @"informative-text-alignment", @"informative-text-color", @"informative-text-font", @"informative-text-shadow-color", @"informative-text-shadow-offset",
+ @"image-offset",
+ @"information-image",
+ @"warning-image",
+ @"error-image"
+ ]];
}
/*!
@@ -118,57 +123,56 @@ var CPAlertWarningImage,
{
if (self = [super init])
{
- _buttonCount = 0;
_buttons = [CPArray array];
_alertStyle = CPWarningAlertStyle;
+ _alertPanel = nil;
+ _windowStyle = nil;
+ _didEndSelector = nil;
- [self setWindowStyle:nil];
+ _messageLabel = [CPTextField labelWithTitle:@"Alert"];
+ _alertImageView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
+ _informativeLabel = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
}
-
+
return self;
}
/*!
- Sets the window appearance.
+ Sets the window appearance. If CPHUDBackgroundWindowMask is set, the default HUD theme
+ will be activated.
+
@param styleMask - Either CPHUDBackgroundWindowMask or nil for standard.
*/
- (void)setWindowStyle:(int)styleMask
{
_windowStyle = styleMask;
-
- _alertPanel = [[CPPanel alloc] initWithContentRect:CGRectMake(0.0, 0.0, 400.0, 110.0) styleMask:styleMask ? styleMask | CPTitledWindowMask : CPTitledWindowMask];
- [_alertPanel setFloatingPanel:YES];
- [_alertPanel center];
- [_messageLabel setTextColor:(styleMask & CPHUDBackgroundWindowMask) ? [CPColor whiteColor] : [CPColor blackColor]];
+ [self setTheme:(_windowStyle & CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
- var count = [_buttons count];
- for(var i=0; i < count; i++)
+ // We'll need to recreate the panel to get the new window style.
+ _alertPanel = nil;
+}
+
+- (void)_createPanel
+{
+ var frame = CGRectMakeZero();
+ frame.size = [self currentValueForThemeAttribute:@"size"];
+ _alertPanel = [[CPPanel alloc] initWithContentRect:frame styleMask:_windowStyle ? _windowStyle | CPTitledWindowMask : CPTitledWindowMask];
+
+ var contentView = [_alertPanel contentView],
+ count = [_buttons count];
+
+ if (count)
{
- var button = _buttons[i];
-
- [button setFrameSize:CGSizeMake([button frame].size.width, (styleMask == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)];
-
- [button setTheme:(_windowStyle === CPHUDBackgroundWindowMask) ? [CPTheme themeNamed:"Aristo-HUD"] : [CPTheme defaultTheme]];
-
- [[_alertPanel contentView] addSubview:button];
+ while (count--)
+ [contentView addSubview:_buttons[count]];
}
-
- if (!_messageLabel)
- {
- var bounds = [[_alertPanel contentView] bounds];
+ else
+ [self addButtonWithTitle:@"OK"];
- _messageLabel = [[CPTextField alloc] initWithFrame:CGRectMake(57.0, 10.0, CGRectGetWidth(bounds) - 73.0, 62.0)];
- [_messageLabel setFont:[CPFont boldSystemFontOfSize:13.0]];
- [_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
- [_messageLabel setAlignment:CPJustifiedTextAlignment];
- [_messageLabel setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
-
- _alertImageView = [[CPImageView alloc] initWithFrame:CGRectMake(15.0, 12.0, 32.0, 32.0)];
- }
-
- [[_alertPanel contentView] addSubview:_messageLabel];
- [[_alertPanel contentView] addSubview:_alertImageView];
+ [contentView addSubview:_messageLabel];
+ [contentView addSubview:_alertImageView];
+ [contentView addSubview:_informativeLabel];
}
/*!
@@ -231,7 +235,7 @@ var CPAlertWarningImage,
}
/*!
- Set’s the receiver’s message text, or title, to a given text.
+ Sets the receiver’s message text, or title, to a given text.
@param messageText - Message text for the alert.
*/
- (void)setMessageText:(CPString)messageText
@@ -239,14 +243,31 @@ var CPAlertWarningImage,
[_messageLabel setStringValue:messageText];
}
-/*!
- Return's the receiver's message text body.
+/*!
+ Returns the receiver's message text body.
*/
- (CPString)messageText
{
return [_messageLabel stringValue];
}
+/*!
+ Sets the receiver's informative text, shown below the message text.
+ @param informativeText - The informative text.
+*/
+- (void)setInformativeText:(CPString)informativeText
+{
+ [_informativeLabel setStringValue:informativeText];
+}
+
+/*!
+ Returns the receiver's informative text.
+*/
+- (CPString)informativeText
+{
+ return [_informativeLabel stringValue];
+}
+
/*!
Adds a button with a given title to the receiver.
Buttons will be added starting from the right hand side of the \c CPAlert panel.
@@ -260,25 +281,119 @@ var CPAlertWarningImage,
- (void)addButtonWithTitle:(CPString)title
{
var bounds = [[_alertPanel contentView] bounds],
- button = [[CPButton alloc] initWithFrame:CGRectMake(CGRectGetWidth(bounds) - ((_buttonCount + 1) * 90.0), CGRectGetHeight(bounds) - 34.0, 80.0, (_windowStyle == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)];
-
+ button = [[CPButton alloc] initWithFrame:CGRectMakeZero()],
+ _buttonCount = [_buttons count];
+
[button setTitle:title];
[button setTarget:self];
[button setTag:_buttonCount];
- [button setAction:@selector(_notifyDelegate:)];
-
- [button setTheme:(_windowStyle === CPHUDBackgroundWindowMask) ? [CPTheme themeNamed:"Aristo-HUD"] : [CPTheme defaultTheme]];
- [button setAutoresizingMask:CPViewMinXMargin | CPViewMinYMargin];
+ [button setAction:@selector(_dismissAlert:)];
[[_alertPanel contentView] addSubview:button];
-
+
if (_buttonCount == 0)
- [_alertPanel setDefaultButton:button];
+ [button setKeyEquivalent:CPCarriageReturnCharacter];
else if ([title lowercaseString] === "cancel")
[button setKeyEquivalent:CPEscapeFunctionKey];
+ else
+ [button setKeyEquivalent:nil];
- _buttonCount++;
- [_buttons addObject:button];
+ [_buttons insertObject:button atIndex:0];
+}
+
+- (void)layoutPanel
+{
+ if (!_alertPanel)
+ [self _createPanel];
+
+ var inset = [self currentValueForThemeAttribute:@"content-inset"],
+ iconOffset = [self currentValueForThemeAttribute:@"image-offset"],
+ theTitle,
+ theImage;
+
+ switch (_alertStyle)
+ {
+ case CPWarningAlertStyle: theImage = [self currentValueForThemeAttribute:@"warning-image"];
+ theTitle = @"Warning";
+ break;
+ case CPInformationalAlertStyle: theImage = [self currentValueForThemeAttribute:@"information-image"];
+ theTitle = @"Information";
+ break;
+ case CPCriticalAlertStyle: theImage = [self currentValueForThemeAttribute:@"error-image"];
+ theTitle = @"Error";
+ break;
+ }
+
+ [_alertImageView setImage:theImage];
+
+ var imageSize = theImage ? [theImage size] : CGSizeMakeZero();
+ [_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)];
+
+ [_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle];
+ [_alertPanel setFloatingPanel:YES];
+ [_alertPanel center];
+
+ [_messageLabel setTextColor:[self currentValueForThemeAttribute:@"message-text-color"]];
+ [_messageLabel setFont:[self currentValueForThemeAttribute:@"message-text-font"]];
+ [_messageLabel setTextShadowColor:[self currentValueForThemeAttribute:@"message-text-shadow-color"]];
+ [_messageLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"message-text-shadow-offset"]];
+ [_messageLabel setAlignment:[self currentValueForThemeAttribute:@"message-text-alignment"]];
+ [_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
+
+ [_informativeLabel setTextColor:[self currentValueForThemeAttribute:@"informative-text-color"]];
+ [_informativeLabel setFont:[self currentValueForThemeAttribute:@"informative-text-font"]];
+ [_informativeLabel setTextShadowColor:[self currentValueForThemeAttribute:@"informative-text-shadow-color"]];
+ [_informativeLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
+ [_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping];
+
+ // FIXME sizeWithFontCorrection shouldn't be needed.
+ var bounds = [[_alertPanel contentView] bounds],
+ offsetX = CGRectGetWidth(bounds) - inset.right,
+ informativeOffset = [self currentValueForThemeAttribute:@"informative-offset"],
+ buttonOffset = [self currentValueForThemeAttribute:@"button-offset"],
+
+ textWidth = offsetX - inset.left,
+ messageSize = [([_messageLabel stringValue] || " ") sizeWithFont:[_messageLabel font] inWidth:textWidth],
+ informationString = [_informativeLabel stringValue],
+ informativeSize = [(informationString || " ") sizeWithFont:[_informativeLabel font] inWidth:textWidth],
+ sizeWithFontCorrection = 6.0;
+
+ [_messageLabel setFrame:CGRectMake(inset.left, inset.top, textWidth, messageSize.height + sizeWithFontCorrection)];
+ [_informativeLabel setFrame:CGRectMake(inset.left, CGRectGetMaxY([_messageLabel frame]) + informativeOffset, textWidth, informativeSize.height + sizeWithFontCorrection)];
+ // Don't let an empty informative label partially cover the buttons.
+ [_informativeLabel setHidden:!informationString];
+
+ var aRepresentativeButton = _buttons[0],
+ buttonY = MAX(CGRectGetMaxY([_alertImageView frame]), CGRectGetMaxY(informationString ? [_informativeLabel frame] : [_messageLabel frame])) + buttonOffset; // the lower of the bottom of the text and the bottom of the icon.
+
+ [aRepresentativeButton setTheme:[self theme]];
+ [aRepresentativeButton sizeToFit];
+
+ // Make the window just tall enough to fit everything. Bit of a hack really.
+ var minimumSize = [self currentValueForThemeAttribute:@"size"],
+ desiredHeight = MAX(minimumSize.height, buttonY + CGRectGetHeight([aRepresentativeButton bounds]) + inset.bottom),
+ deltaY = desiredHeight - CGRectGetHeight(bounds),
+ frameSize = CGSizeMakeCopy([_alertPanel frame].size);
+
+ frameSize.height += deltaY;
+ [_alertPanel setFrameSize:frameSize];
+
+ var count = [_buttons count];
+
+ while (count--)
+ {
+ var button = _buttons[count];
+ [button setTheme:[self theme]];
+ [button sizeToFit];
+
+ var buttonBounds = [button bounds],
+ width = MAX(80.0, CGRectGetWidth(buttonBounds)),
+ height = CGRectGetHeight(buttonBounds);
+
+ offsetX -= width;
+ [button setFrame:CGRectMake(offsetX, buttonY, width, height)];
+ offsetX -= 10;
+ }
}
/*!
@@ -288,34 +403,68 @@ var CPAlertWarningImage,
*/
- (void)runModal
{
- var theTitle;
-
- switch (_alertStyle)
- {
- case CPWarningAlertStyle: [_alertImageView setImage:CPAlertWarningImage];
- theTitle = @"Warning";
- break;
- case CPInformationalAlertStyle: [_alertImageView setImage:CPAlertInformationImage];
- theTitle = @"Information";
- break;
- case CPCriticalAlertStyle: [_alertImageView setImage:CPAlertErrorImage];
- theTitle = @"Error";
- break;
- }
-
- [_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle];
-
+ [self layoutPanel];
[CPApp runModalForWindow:_alertPanel];
}
-/* @ignore */
-- (void)_notifyDelegate:(id)button
-{
- [CPApp abortModal];
- [_alertPanel close];
+/*!
+ Runs the receiver modally as an alert sheet attached to a specified window.
- if (_delegate && [_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
- [_delegate alertDidEnd:self returnCode:[button tag]];
+ @param window The parent window for the sheet.
+ @param modalDelegate The delegate for the modal-dialog session.
+ @param alertDidEndSelector Message the alert sends to modalDelegate after the sheet is dismissed.
+ @param contextInfo Contextual data passed to modalDelegate in didEndSelector message.
+*/
+- (void)beginSheetModalForWindow:(CPWindow)window modalDelegate:(id)modalDelegate didEndSelector:(SEL)alertDidEndSelector contextInfo:(void)contextInfo
+{
+ if (!(_windowStyle & CPDocModalWindowMask))
+ [self setWindowStyle:CPDocModalWindowMask];
+ [self layoutPanel];
+
+ _didEndSelector = alertDidEndSelector;
+ _modalDelegate = modalDelegate;
+
+ [CPApp beginSheet:_alertPanel modalForWindow:window modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:contextInfo];
+}
+
+/*!
+ Runs the receiver modally as an alert sheet attached to a specified window.
+
+ @param window The parent window for the sheet.
+*/
+- (void)beginSheetModalForWindow:(CPWindow)window
+{
+ if (!(_windowStyle & CPDocModalWindowMask))
+ [self setWindowStyle:CPDocModalWindowMask];
+ [self layoutPanel];
+
+ [CPApp beginSheet:_alertPanel modalForWindow:window modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:nil];
+}
+
+- (void)_alertDidEnd:(CPWindow)aSheet returnCode:(CPInteger)returnCode contextInfo:(id)contextInfo
+{
+ if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
+ [_delegate alertDidEnd:self returnCode:returnCode];
+
+ if (_didEndSelector)
+ objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
+
+ _didEndSelector = nil;
+ _modalDelegate = nil;
+}
+
+/* @ignore */
+- (void)_dismissAlert:(CPButton)button
+{
+ if ([_alertPanel isSheet])
+ [CPApp endSheet:_alertPanel returnCode:[button tag]];
+ else
+ {
+ [CPApp abortModal];
+ [_alertPanel close];
+
+ [self _alertDidEnd:nil returnCode:[button tag] contextInfo:nil];
+ }
}
@end
diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j
index 41d8d6dee..6ac7d44ac 100644
--- a/AppKit/CPApplication.j
+++ b/AppKit/CPApplication.j
@@ -108,6 +108,8 @@ CPRunContinuesResponse = -1002;
CPImage _applicationIconImage;
CPPanel _aboutPanel;
+
+ CPThemeBlend _themeBlend @accessors(property=themeBlend);
}
/*!
@@ -325,7 +327,9 @@ CPRunContinuesResponse = -1002;
applicationVersion = [options objectForKey:@"ApplicationVersion"] || [mainInfo objectForKey:@"CPBundleShortVersionString"],
copyright = [options objectForKey:@"Copyright"] || [mainInfo objectForKey:@"CPHumanReadableCopyright"];
- var aboutPanelController = [[CPWindowController alloc] initWithWindowCibName:@"AboutPanel"],
+ var aboutPanelPath = [[CPBundle bundleForClass:[CPWindowController class]] pathForResource:@"AboutPanel.cib"],
+ aboutPanelController = [CPWindowController alloc],
+ aboutPanelController = [aboutPanelController initWithWindowCibPath:aboutPanelPath owner:aboutPanelController],
aboutPanel = [aboutPanelController window],
contentView = [aboutPanel contentView],
imageView = [contentView viewWithTag:1],
@@ -859,6 +863,7 @@ CPRunContinuesResponse = -1002;
}
[aWindow orderFront:self];
+ [aSheet setPlatformWindow:[aWindow platformWindow]];
[aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo];
}
@@ -1016,7 +1021,6 @@ CPRunContinuesResponse = -1002;
+ (CPString)defaultThemeName
{
- // FIXME: don't hardcode
return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo");
}
@@ -1032,7 +1036,8 @@ var _CPEventListenerMake = function(anEventMask, aCallback)
return { _mask:anEventMask, _callback:aCallback };
}
-var _CPRunModalLoop = function(anEvent)
+// Make this a global for use in CPPlatformWindow+DOM.j.
+_CPRunModalLoop = function(anEvent)
{
[CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
@@ -1110,8 +1115,15 @@ var _CPAppBootstrapperActions = nil;
+ (BOOL)loadDefaultTheme
{
- var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[[CPBundle bundleForClass:[CPApplication class]] pathForResource:[CPApplication defaultThemeName] + ".blend"]];
+ var defaultThemeName = [CPApplication defaultThemeName],
+ themeURL = nil;
+ if (defaultThemeName === @"Aristo")
+ themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:defaultThemeName + @".blend"];
+ else
+ themeURL = [[CPBundle mainBundle] pathForResource:defaultThemeName + @".blend"];
+
+ var blend = [[CPThemeBlend alloc] initWithContentsOfURL:themeURL];
[blend loadWithDelegate:self];
return YES;
@@ -1119,6 +1131,7 @@ var _CPAppBootstrapperActions = nil;
+ (void)blendDidFinishLoading:(CPThemeBlend)aThemeBlend
{
+ [[CPApplication sharedApplication] setThemeBlend:aThemeBlend];
[CPTheme setDefaultTheme:[CPTheme themeNamed:[CPApplication defaultThemeName]]];
[self performActions];
@@ -1216,7 +1229,7 @@ var _CPAppBootstrapperActions = nil;
+ (void)reset
{
- _CPAppBootstrapperActions = nil;
+ _CPAppBootstrapperActions = nil;
}
@end
diff --git a/AppKit/CPArrayController.j b/AppKit/CPArrayController.j
index 5fefb58c8..934314707 100644
--- a/AppKit/CPArrayController.j
+++ b/AppKit/CPArrayController.j
@@ -58,22 +58,30 @@
+ (CPSet)keyPathsForValuesAffectingArrangedObjects
{
- return [CPSet setWithObjects:"content", "contentArray", "contentSet", "filterPredicate", "sortDescriptors"];
+ return [CPSet setWithObjects:"content", "filterPredicate", "sortDescriptors"];
}
+ (CPSet)keyPathsForValuesAffectingSelection
{
- return [CPSet setWithObjects:"content", "contentArray", "contentSet", "selectionIndexes"];
+ return [CPSet setWithObjects:"selectionIndexes"];
}
+ (CPSet)keyPathsForValuesAffectingSelectionIndex
{
- return [CPSet setWithObjects:"content", "contentArray", "contentSet", "selectionIndexes", "selection"];
+ return [CPSet setWithObjects:"selectionIndexes"];
+}
+
++ (CPSet)keyPathsForValuesAffectingSelectionIndexes
+{
+ // When the arranged objects change, selection preservation may cause the indexes
+ // to change.
+ return [CPSet setWithObjects:"arrangedObjects"];
}
+ (CPSet)keyPathsForValuesAffectingSelectedObjects
{
- return [CPSet setWithObjects:"content", "contentArray", "contentSet", "selectionIndexes", "selection"];
+ // Don't need to depend on arrangedObjects here because selectionIndexes already does.
+ return [CPSet setWithObjects:"selectionIndexes"];
}
+ (CPSet)keyPathsForValuesAffectingCanRemove
@@ -91,16 +99,6 @@
return [CPSet setWithObjects:"selectionIndexes"];
}
-+ (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey
-{
- if (![super automaticallyNotifiesObserversForKey:aKey])
- return NO;
- if (aKey === @"selectionIndexes")
- return NO;
-
- return YES;
-}
-
- (id)init
{
self = [super init];
@@ -153,31 +151,50 @@
if(![value isKindOfClass:[CPArray class]])
value = [value];
- var oldSelection = nil,
- oldSelectionIndexes = [[self selectionIndexes] copy];
+ var oldSelectedObjects = nil,
+ oldSelectionIndexes = nil;
if ([self preservesSelection])
- oldSelection = [self selectedObjects];
+ oldSelectedObjects = [self selectedObjects];
+ else
+ oldSelectionIndexes = [self selectionIndexes];
- // Avoid out of bounds selections.
- _selectionIndexes = [CPIndexSet indexSet];
- //FIXME: copy?
- [super setContent:value];
+ /*
+ When the contents are changed, the selected indexes may no longer refer to the
+ same items. This would cause problems when setSelectedObjects is called below.
+ Any KVO observation would try to retrieve the 'before' value which could be
+ wrong or even throw an exception for no longer existing indexes.
+
+ To avoid that, use the internal __setSelectedObjects which fires no notifications.
+ The selectionIndexes notifications will fire later since they depend on the
+ content key. This pattern is also applied for many other methods throughout this
+ class.
+ */
+
+ if (_clearsFilterPredicateOnInsertion)
+ [self willChangeValueForKey:@"filterPredicate"];
+
+ // Don't use [super setContent:] as that would fire the contentObject change.
+ // We need to be in control of when notifications fire.
+ _contentObject = value;
if(_clearsFilterPredicateOnInsertion)
- [self setFilterPredicate:nil];
-
- [self rearrangeObjects];
-
- if (oldSelection)
- [self setSelectedObjects:oldSelection];
+ [self __setFilterPredicate:nil]; // Causes a _rearrangeObjects.
else
- [self setSelectionIndexes:oldSelectionIndexes];
+ [self _rearrangeObjects];
+
+ if ([self preservesSelection])
+ [self __setSelectedObjects:oldSelectedObjects];
+ else
+ [self __setSelectionIndexes:oldSelectionIndexes];
+
+ if (_clearsFilterPredicateOnInsertion)
+ [self didChangeValueForKey:@"filterPredicate"];
}
- (void)_setContentArray:(id)anArray
{
- [self setContent:anArray];
+ [self setContent:anArray];
}
- (void)_setContentSet:(id)aSet
@@ -197,42 +214,57 @@
- (CPArray)arrangeObjects:(CPArray)objects
{
- var sortedObjects = objects;
+ var filterPredicate = [self filterPredicate],
+ sortDescriptors = [self sortDescriptors];
- if ([self filterPredicate])
- sortedObjects = [sortedObjects filteredArrayUsingPredicate:[self filterPredicate]];
- if ([self sortDescriptors])
- sortedObjects = [sortedObjects sortedArrayUsingDescriptors:[self sortDescriptors]];
+ if (filterPredicate && sortDescriptors)
+ {
+ var sortedObjects = [objects filteredArrayUsingPredicate:filterPredicate];
+ [sortedObjects sortUsingDescriptors:sortDescriptors];
+ return sortedObjects;
+ }
+ else if (filterPredicate)
+ return [objects filteredArrayUsingPredicate:filterPredicate];
+ else if (sortDescriptors)
+ return [objects sortedArrayUsingDescriptors:sortDescriptors];
- return sortedObjects;
+ return [objects copy];
}
- (void)rearrangeObjects
{
- // Rearranging reapplies the selection criteria and may cause objects to disappear,
- // so take care of the selection.
- //
- // Sometimes rearrangeObjects is called by setContent which may cause two rounds of
- // selection preservation. This is okay because setContent temporarily clears the
- // selection and so this code below ends up preserving nothing in that case.
- var oldSelection = nil,
- oldSelectionIndexes = [[self selectionIndexes] copy];
-
- if ([self preservesSelection])
- oldSelection = [self selectedObjects];
-
- // Avoid out of bounds selections.
- _selectionIndexes = [CPIndexSet indexSet];
-
- [self _setArrangedObjects:[self arrangeObjects:[self contentArray]]];
-
- if (oldSelection)
- [self setSelectedObjects:oldSelection];
- else
- [self setSelectionIndexes:oldSelectionIndexes];
+ [self willChangeValueForKey:@"arrangedObjects"];
+ [self _rearrangeObjects];
+ [self didChangeValueForKey:@"arrangedObjects"];
}
-- (void)_setArrangedObjects:(id)value
+/*
+ Like rearrangeObjects but don't fire any change notifications.
+ @ignore
+*/
+- (void)_rearrangeObjects
+{
+ /*
+ Rearranging reapplies the selection criteria and may cause objects to disappear,
+ so take care of the selection.
+ */
+ var oldSelectedObjects = nil,
+ oldSelectionIndexes = nil;
+
+ if ([self preservesSelection])
+ oldSelectedObjects = [self selectedObjects];
+ else
+ oldSelectionIndexes = [self selectionIndexes];
+
+ [self __setArrangedObjects:[self arrangeObjects:[self contentArray]]];
+
+ if ([self preservesSelection])
+ [self __setSelectedObjects:oldSelectedObjects];
+ else
+ [self __setSelectionIndexes:oldSelectionIndexes];
+}
+
+- (void)__setArrangedObjects:(id)value
{
if (_arrangedObjects === value)
return;
@@ -245,7 +277,6 @@
return _arrangedObjects;
}
-
- (CPArray)sortDescriptors
{
return _sortDescriptors;
@@ -257,7 +288,9 @@
return;
_sortDescriptors = [value copy];
- [self rearrangeObjects];
+ // Use the non-notification version since arrangedObjects already depends
+ // on sortDescriptors.
+ [self _rearrangeObjects];
}
- (CPPredicate)filterPredicate
@@ -266,12 +299,23 @@
}
- (void)setFilterPredicate:(CPPredicate)value
+{
+ [self __setFilterPredicate:value];
+}
+
+/*
+ Like setFilterPredicate but don't fire any change notifications.
+ @ignore
+*/
+- (void)__setFilterPredicate:(CPPredicate)value
{
if (_filterPredicate === value)
return;
_filterPredicate = value;
- [self rearrangeObjects];
+ // Use the non-notification version since arrangedObjects already depends
+ // on filterPredicate.
+ [self _rearrangeObjects];
}
- (BOOL)alwaysUsesMultipleValuesMarker
@@ -298,8 +342,26 @@
- (BOOL)setSelectionIndexes:(CPIndexSet)indexes
{
- if ([_selectionIndexes isEqualToIndexSet:indexes])
- return NO;
+ [self __setSelectionIndexes:indexes];
+}
+
+/*
+ Like setSelectionIndex but don't fire any change notifications.
+ @ignore
+*/
+- (BOOL)__setSelectionIndex:(int)theIndex
+{
+ [self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:theIndex]];
+}
+
+/*
+ Like setSelectionIndexes but don't fire any change notifications.
+ @ignore
+*/
+- (BOOL)__setSelectionIndexes:(CPIndexSet)indexes
+{
+ if (!indexes)
+ indexes = [CPIndexSet indexSet];
if (![indexes count])
{
@@ -316,14 +378,11 @@
indexes = [CPIndexSet indexSetWithIndex:objectsCount-1];
}
- [self willChangeValueForKey:@"selectionIndexes"];
- [self _selectionWillChange];
+ if ([_selectionIndexes isEqualToIndexSet:indexes])
+ return NO;
_selectionIndexes = [indexes copy];
- [self _selectionDidChange];
- [self didChangeValueForKey:@"selectionIndexes"];
-
// Push back the new selection to the model for selectionIndexes if we have one.
// There won't be an infinite loop because of the equality check above.
[[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
@@ -335,10 +394,25 @@
{
var objects = [[self arrangedObjects] objectsAtIndexes:[self selectionIndexes]];
- return objects || [_CPObservableArray array];
+ return [_CPObservableArray arrayWithArray:(objects || [])];
}
- (BOOL)setSelectedObjects:(CPArray)objects
+{
+ [self willChangeValueForKey:@"selectionIndexes"];
+ [self _selectionWillChange];
+
+ [self __setSelectedObjects:objects];
+
+ [self didChangeValueForKey:@"selectionIndexes"];
+ [self _selectionDidChange];
+}
+
+/*
+ Like setSelectedObjects but don't fire any change notifications.
+ @ignore
+*/
+- (BOOL)__setSelectedObjects:(CPArray)objects
{
var set = [CPIndexSet indexSet],
count = [objects count],
@@ -352,35 +426,35 @@
[set addIndex:index];
}
- [self setSelectionIndexes:set];
+ [self __setSelectionIndexes:set];
return YES;
}
//Moving selection
--(BOOL)canSelectPrevious
+- (BOOL)canSelectPrevious
{
return [[self selectionIndexes] firstIndex] > 0
}
--(BOOL)canSelectNext
+-(void)selectPrevious:(id)sender
{
- return [[self selectionIndexes] firstIndex] < [[self arrangedObjects] count] -1;
-}
+ var index = [[self selectionIndexes] firstIndex] - 1;
--(void)selectNext:(id)sender
-{
- var index = [[self selectionIndexes] firstIndex] + 1 || 0;
-
- if (index < [[self arrangedObjects] count])
+ if (index >= 0)
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
}
--(void)selectPrevious:(id)sender
+- (BOOL)canSelectNext
{
- var index = [[self selectionIndexes] firstIndex] - 1 || [[self arrangedObjects] count] - 1;
+ return [[self selectionIndexes] firstIndex] < [[self arrangedObjects] count] - 1;
+}
- if (index >= 0)
+- (void)selectNext:(id)sender
+{
+ var index = [[self selectionIndexes] firstIndex] + 1;
+
+ if (index < [[self arrangedObjects] count])
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
}
@@ -391,30 +465,63 @@
if (![self canAdd])
return;
+ if (_clearsFilterPredicateOnInsertion)
+ [self willChangeValueForKey:@"filterPredicate"];
+
[self willChangeValueForKey:@"content"];
[_contentObject addObject:object];
- [self didChangeValueForKey:@"content"];
if (_clearsFilterPredicateOnInsertion)
- [self setFilterPredicate:nil];
+ [self __setFilterPredicate:nil];
if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
{
var pos = [_arrangedObjects insertObject:object inArraySortedByDescriptors:_sortDescriptors];
+ // selectionIndexes change notification will be fired as a result of the
+ // content change. Don't fire manually.
if (_selectsInsertedObjects)
- {
- [self setSelectionIndex:pos];
- }
+ [self __setSelectionIndex:pos];
else
- {
- [self willChangeValueForKey:@"selectionIndexes"];
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:1];
- [self didChangeValueForKey:@"selectionIndexes"];
- }
}
else
- [self rearrangeObjects];
+ [self _rearrangeObjects];
+
+ [self didChangeValueForKey:@"content"];
+ if (_clearsFilterPredicateOnInsertion)
+ [self didChangeValueForKey:@"filterPredicate"];
+}
+
+- (void)insertObject:(id)anObject atArrangedObjectIndex:(int)anIndex
+{
+ if (![self canAdd])
+ return;
+
+ if (_clearsFilterPredicateOnInsertion)
+ [self willChangeValueForKey:@"filterPredicate"];
+
+ [self willChangeValueForKey:@"content"];
+ [_contentObject insertObject:anObject atIndex:anIndex];
+
+ if (_clearsFilterPredicateOnInsertion)
+ [self __setFilterPredicate:nil];
+
+ [[self arrangedObjects] insertObject:anObject atIndex:anIndex];
+
+ // selectionIndexes change notification will be fired as a result of the
+ // content change. Don't fire manually.
+ if ([self selectsInsertedObjects])
+ [self __setSelectionIndex:anIndex];
+ else
+ [[self selectionIndexes] shiftIndexesStartingAtIndex:anIndex by:1];
+
+ if ([self avoidsEmptySelection] && [[self selectionIndexes] count] <= 0 && [_contentObject count] > 0)
+ [self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]];
+
+ [self didChangeValueForKey:@"content"];
+ if (_clearsFilterPredicateOnInsertion)
+ [self didChangeValueForKey:@"filterPredicate"];
}
- (void)removeObject:(id)object
@@ -424,16 +531,18 @@
[self willChangeValueForKey:@"content"];
[_contentObject removeObject:object];
- [self didChangeValueForKey:@"content"];
- if ([_filterPredicate evaluateWithObject:object])
+ if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
{
- [self willChangeValueForKey:@"selectionIndexes"];
+ // selectionIndexes change notification will be fired as a result of the
+ // content change. Don't fire manually.
var pos = [_arrangedObjects indexOfObject:object];
+ [_arrangedObjects removeObjectAtIndex:pos];
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:-1];
- [self didChangeValueForKey:@"selectionIndexes"];
}
+
+ [self didChangeValueForKey:@"content"];
}
-(void)add:(id)sender
@@ -489,13 +598,33 @@
- (void)_removeObjects:(CPArray)objects
{
- var contentArray = [self contentArray],
- count = [objects count];
+ [self willChangeValueForKey:@"content"];
+ [_contentObject removeObjectsInArray:objects];
- for (var i=0; i= objectsCount)
+ selectionIndexes = [CPIndexSet indexSetWithIndex:objectsCount - 1];
+ }
+
+ _selectionIndexes = selectionIndexes;
+
+ [self didChangeValueForKey:@"content"];
}
- (BOOL)canInsert
diff --git a/AppKit/CPBox.j b/AppKit/CPBox.j
index 05500b550..a96f10c85 100644
--- a/AppKit/CPBox.j
+++ b/AppKit/CPBox.j
@@ -19,25 +19,25 @@
* 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"
// CPBorderType
CPNoBorder = 0;
-CPLineBorder = 1;
+CPLineBorder = 1;
CPBezelBorder = 2;
CPGrooveBorder = 3;
@implementation CPBox : CPView
{
CPBorderType _borderType;
-
+
CPColor _borderColor;
CPColor _fillColor;
-
+
float _cornerRadius;
float _borderWidth;
-
+
CPSize _contentMargin;
CPView _contentView;
}
@@ -50,18 +50,19 @@ CPGrooveBorder = 3;
[box setFrameFromContentFrame:[aView frame]];
[enclosingView replaceSubview:aView with:box];
-
+
[box setContentView:aView];
-
+
return box;
}
- (id)initWithFrame:(CPRect)frameRect
{
self = [super initWithFrame:frameRect];
-
+
if (self)
{
+ _borderType = CPBezelBorder;
_fillColor = [CPColor clearColor];
_borderColor = [CPColor blackColor];
@@ -69,7 +70,9 @@ CPGrooveBorder = 3;
_contentMargin = CGSizeMake(0.0, 0.0);
_contentView = [[CPView alloc] initWithFrame:[self bounds]];
+ [_contentView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
+ [self setAutoresizesSubviews:YES];
[self addSubview:_contentView];
}
@@ -161,9 +164,10 @@ CPGrooveBorder = 3;
return;
[aView setFrame:CGRectInset([self bounds], _contentMargin.width + _borderWidth, _contentMargin.height + _borderWidth)];
+ [aView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
[self replaceSubview:_contentView with:aView];
-
- _contentView = aView;
+
+ _contentView = aView;
}
- (CPSize)contentViewMargins
@@ -175,7 +179,7 @@ CPGrooveBorder = 3;
{
if(size.width < 0 || size.height < 0)
[CPException raise:CPGenericException reason:@"Margins must be positive"];
-
+
_contentMargin = CGSizeMakeCopy(size);
[self setNeedsDisplay:YES];
}
@@ -189,56 +193,97 @@ CPGrooveBorder = 3;
- (void)sizeToFit
{
var contentFrame = [_contentView frame];
-
- [self setFrameSize:CGSizeMake(contentFrame.size.width + _contentMargin.width * 2,
+
+ [self setFrameSize:CGSizeMake(contentFrame.size.width + _contentMargin.width * 2,
contentFrame.size.height + _contentMargin.height * 2)];
-
+
[_contentView setFrameOrigin:CGPointMake(_contentMargin.width, _contentMargin.height)];
}
- (void)drawRect:(CPRect)rect
{
+ if (_borderType === CPNoBorder)
+ return;
+
var bounds = [self bounds],
- aContext = [[CPGraphicsContext currentContext] graphicsPort],
- border2 = _borderWidth/2,
+ context = [[CPGraphicsContext currentContext] graphicsPort];
- strokeRect = CGRectMake(bounds.origin.x + border2,
- bounds.origin.y + border2,
- bounds.size.width - _borderWidth,
- bounds.size.height - _borderWidth),
-
- fillRect = CGRectMake(bounds.origin.x + border2,
- bounds.origin.y + border2,
- bounds.size.width - _borderWidth,
- bounds.size.height - _borderWidth);
+ CGContextSetFillColor(context, [self fillColor]);
- CGContextSetFillColor(aContext, [self fillColor]);
- CGContextSetStrokeColor(aContext, [self borderColor]);
- CGContextSetLineWidth(aContext, _borderWidth);
-
- switch(_borderType)
+ switch (_borderType)
{
- case CPLineBorder: CGContextFillRoundedRectangleInRect(aContext, fillRect, _cornerRadius, YES, YES, YES, YES);
- CGContextStrokeRoundedRectangleInRect(aContext, strokeRect, _cornerRadius, YES, YES, YES, YES);
- break;
+ case CPBezelBorder:
+ var sides = [CPMinYEdge, CPMaxXEdge, CPMaxYEdge, CPMinXEdge],
+ sideGray = 190.0 / 255.0,
+ grays = [142.0 / 255.0, sideGray, sideGray, sideGray],
+ borderWidth = _borderWidth;
- case CPBezelBorder: CGContextFillRoundedRectangleInRect(aContext, fillRect, _cornerRadius, YES, YES, YES, YES);
- CGContextSetStrokeColor(aContext, [CPColor colorWithWhite:190.0/255.0 alpha:1.0]);
- CGContextBeginPath(aContext);
- CGContextMoveToPoint(aContext, strokeRect.origin.x, strokeRect.origin.y);
- CGContextAddLineToPoint(aContext, CGRectGetMinX(strokeRect), CGRectGetMaxY(strokeRect)),
- CGContextAddLineToPoint(aContext, CGRectGetMaxX(strokeRect), CGRectGetMaxY(strokeRect)),
- CGContextAddLineToPoint(aContext, CGRectGetMaxX(strokeRect), CGRectGetMinY(strokeRect)),
- CGContextStrokePath(aContext);
- CGContextSetStrokeColor(aContext, [CPColor colorWithWhite:142.0/255.0 alpha:1.0]);
- CGContextBeginPath(aContext);
- CGContextMoveToPoint(aContext, bounds.origin.x, strokeRect.origin.y);
- CGContextAddLineToPoint(aContext, CGRectGetMaxX(bounds), CGRectGetMinY(strokeRect));
- CGContextStrokePath(aContext);
- break;
+ while (borderWidth--)
+ bounds = CPDrawTiledRects(bounds, bounds, sides, grays);
- default: break;
+ CGContextFillRect(context, bounds);
+ break;
+
+ default:
+ bounds = CGRectInset(bounds, _borderWidth / 2.0, _borderWidth / 2.0);
+
+ CGContextSetStrokeColor(context, [self borderColor]);
+ CGContextSetLineWidth(context, _borderWidth);
+ CGContextFillRoundedRectangleInRect(context, bounds, _cornerRadius, YES, YES, YES, YES);
+ CGContextStrokeRoundedRectangleInRect(context, bounds, _cornerRadius, YES, YES, YES, YES);
+ break;
}
}
@end
+
+var CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
+ CPBoxBorderColorKey = @"CPBoxBorderColorKey",
+ CPBoxFillColorKey = @"CPBoxFillColorKey",
+ CPBoxCornerRadiusKey = @"CPBoxCornerRadiusKey",
+ CPBoxBorderWidthKey = @"CPBoxBorderWidthKey",
+ CPBoxContentMarginKey = @"CPBoxContentMarginKey";
+
+@implementation CPBox (CPCoding)
+
+- (id)initWithCoder:(CPCoder)aCoder
+{
+ self = [super initWithCoder:aCoder];
+
+ if (self)
+ {
+ _borderType = [aCoder decodeIntForKey:CPBoxBorderTypeKey];
+
+ _borderColor = [aCoder decodeObjectForKey:CPBoxBorderColorKey];
+ _fillColor = [aCoder decodeObjectForKey:CPBoxFillColorKey];
+
+ _cornerRadius = [aCoder decodeFloatForKey:CPBoxCornerRadiusKey];
+ _borderWidth = [aCoder decodeFloatForKey:CPBoxBorderWidthKey];
+
+ _contentMargin = [aCoder decodeSizeForKey:CPBoxContentMarginKey];
+
+ _contentView = [self subviews][0];
+
+ [self setAutoresizesSubviews:YES];
+ [_contentView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(CPCoder)aCoder
+{
+ [super encodeWithCoder:aCoder];
+
+ [aCoder encodeInt:_borderType forKey:CPBoxBorderTypeKey];
+
+ [aCoder encodeObject:_borderColor forKey:CPBoxBorderColorKey];
+ [aCoder encodeObject:_fillColor forKey:CPBoxFillColorKey];
+
+ [aCoder encodeFloat:_cornerRadius forKey:CPBoxCornerRadiusKey];
+ [aCoder encodeFloat:_borderWidth forKey:CPBoxBorderWidthKey];
+
+ [aCoder encodeSize:_contentMargin forKey:CPBoxContentMarginKey];
+}
+
+@end
diff --git a/AppKit/CPButton.j b/AppKit/CPButton.j
index b8997986c..dcd0c8315 100644
--- a/AppKit/CPButton.j
+++ b/AppKit/CPButton.j
@@ -74,6 +74,9 @@ CPChangeBackgroundCellMask = CPBackgroundButtonMask;
CPButtonStateMixed = CPThemeState("mixed");
+CPButtonDefaultHeight = 24.0;
+CPButtonImageOffset = 3.0;
+
/*!
@ingroup appkit
@class CPButton
@@ -89,9 +92,6 @@ CPButtonStateMixed = CPThemeState("mixed");
CPString _title;
CPString _alternateTitle;
- CPImage _image;
- CPImage _alternateImage;
-
CPInteger _showsStateBy;
CPInteger _highlightsBy;
BOOL _imageDimsWhenDisabled;
@@ -127,8 +127,8 @@ CPButtonStateMixed = CPThemeState("mixed");
+ (id)themeAttributes
{
- return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null]]
- forKeys:[@"bezel-inset", @"content-inset", @"bezel-color"]];
+ return [CPDictionary dictionaryWithObjects:[[CPNull null], 0.0, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null]]
+ forKeys:[@"image", @"image-offset", @"bezel-inset", @"content-inset", @"bezel-color"]];
}
- (id)initWithFrame:(CGRect)aFrame
@@ -145,7 +145,7 @@ CPButtonStateMixed = CPThemeState("mixed");
_controlSize = CPRegularControlSize;
- _keyEquivalent = "";
+ _keyEquivalent = @"";
_keyEquivalentModifierMask = 0;
// [self setBezelStyle:CPRoundRectBezelStyle];
@@ -284,18 +284,12 @@ CPButtonStateMixed = CPThemeState("mixed");
- (void)setImage:(CPImage)anImage
{
- if (_image === anImage)
- return;
-
- _image = anImage;
-
- [self setNeedsLayout];
- [self setNeedsDisplay:YES];
+ [self setValue:anImage forThemeAttribute:@"image"];
}
- (CPImage)image
{
- return _image;
+ return [self valueForThemeAttribute:@"image" inState:CPThemeStateNormal];
}
/*!
@@ -304,13 +298,7 @@ CPButtonStateMixed = CPThemeState("mixed");
*/
- (void)setAlternateImage:(CPImage)anImage
{
- if (_alternateImage === anImage)
- return;
-
- _alternateImage = anImage;
-
- [self setNeedsLayout];
- [self setNeedsDisplay:YES];
+ [self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
}
/*!
@@ -318,7 +306,17 @@ CPButtonStateMixed = CPThemeState("mixed");
*/
- (CPImage)alternateImage
{
- return _alternateImage;
+ return [self valueForThemeAttribute:@"image" inState:CPThemeStateHighlighted];
+}
+
+- (void)setImageOffset:(float)theImageOffset
+{
+ [self setValue:theImageOffset forThemeAttribute:@"image-offset"];
+}
+
+- (float)imageOffset
+{
+ return [self valueForThemeAttribute:@"image-offset"];
}
- (void)setShowsStateBy:(CPInteger)aMask
@@ -468,8 +466,20 @@ CPButtonStateMixed = CPThemeState("mixed");
*/
- (void)sizeToFit
{
- var size = [([self title] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]],
- contentInset = [self currentValueForThemeAttribute:@"content-inset"],
+ [self layoutSubviews];
+
+ var size,
+ contentView = [self ephemeralSubviewNamed:@"content-view"];
+
+ if (contentView)
+ {
+ [contentView sizeToFit];
+ size = [contentView frameSize];
+ }
+ else
+ size = [([self title] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]];
+
+ var contentInset = [self currentValueForThemeAttribute:@"content-inset"],
minSize = [self currentValueForThemeAttribute:@"min-size"],
maxSize = [self currentValueForThemeAttribute:@"max-size"];
@@ -525,7 +535,8 @@ CPButtonStateMixed = CPThemeState("mixed");
if (contentView)
{
[contentView setText:([self hasThemeState:CPThemeStateHighlighted] && _alternateTitle) ? _alternateTitle : _title];
- [contentView setImage:([self hasThemeState:CPThemeStateHighlighted] && _alternateImage) ? _alternateImage : _image];
+ [contentView setImage:[self currentValueForThemeAttribute:@"image"]];
+ [contentView setImageOffset:[self currentValueForThemeAttribute:@"image-offset"]];
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
[contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
@@ -540,14 +551,6 @@ CPButtonStateMixed = CPThemeState("mixed");
}
}
-- (void)setDefaultButton:(BOOL)shouldBeDefaultButton
-{
- if (shouldBeDefaultButton)
- [self setThemeState:CPThemeStateDefault];
- else
- [self unsetThemeState:CPThemeStateDefault];
-}
-
- (void)setBordered:(BOOL)shouldBeBordered
{
if (shouldBeBordered)
@@ -570,6 +573,27 @@ CPButtonStateMixed = CPThemeState("mixed");
- (void)setKeyEquivalent:(CPString)aString
{
_keyEquivalent = aString || @"";
+
+ // Check if the key equivalent is the enter key
+ // Treat \r and \n as the same key equivalent. See issue #710.
+ if (aString === CPNewlineCharacter || aString === CPCarriageReturnCharacter)
+ [self setThemeState:CPThemeStateDefault];
+ else
+ [self unsetThemeState:CPThemeStateDefault];
+}
+
+- (void)viewWillMoveToWindow:(CPWindow)aWindow
+{
+ var selfWindow = [self window];
+
+ if (selfWindow === aWindow || aWindow === nil)
+ return;
+
+ if ([selfWindow defaultButton] === self)
+ [selfWindow setDefaultButton:nil];
+
+ if ([self keyEquivalent] === CPNewlineCharacter || [self keyEquivalent] === CPCarriageReturnCharacter)
+ [aWindow setDefaultButton:self];
}
/*!
@@ -602,6 +626,10 @@ CPButtonStateMixed = CPThemeState("mixed");
*/
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
{
+ // Don't handle the key equivalent for the default window because the window will handle it for us
+ if ([[self window] defaultButton] === self)
+ return NO;
+
if (![anEvent _triggersKeyEquivalent:[self keyEquivalent] withModifierMask:[self keyEquivalentModifierMask]])
return NO;
@@ -629,7 +657,11 @@ var CPButtonImageKey = @"CPButtonImageKey",
CPButtonTitleKey = @"CPButtonTitleKey",
CPButtonAlternateTitleKey = @"CPButtonAlternateTitleKey",
CPButtonIsBorderedKey = @"CPButtonIsBorderedKey",
- CPButtonImageDimsWhenDisabledKey = @"CPButtonImageDimsWhenDisabledKey";
+ CPButtonAllowsMixedStateKey = @"CPButtonAllowsMixedStateKey",
+ CPButtonImageDimsWhenDisabledKey = @"CPButtonImageDimsWhenDisabledKey",
+ CPButtonImagePositionKey = @"CPButtonImagePositionKey",
+ CPButtonKeyEquivalentKey = @"CPButtonKeyEquivalentKey",
+ CPButtonKeyEquivalentMaskKey = @"CPButtonKeyEquivalentMaskKey";
@implementation CPButton (CPCoding)
@@ -645,14 +677,22 @@ var CPButtonImageKey = @"CPButtonImageKey",
{
_controlSize = CPRegularControlSize;
- [self setImage:[aCoder decodeObjectForKey:CPButtonImageKey]];
- [self setAlternateImage:[aCoder decodeObjectForKey:CPButtonAlternateImageKey]];
+ _title = [aCoder decodeObjectForKey:CPButtonTitleKey];
+ _alternateTitle = [aCoder decodeObjectForKey:CPButtonAlternateTitleKey];
- [self setTitle:[aCoder decodeObjectForKey:CPButtonTitleKey]];
- [self setAlternateTitle:[aCoder decodeObjectForKey:CPButtonAlternateTitleKey]];
+ if ([aCoder containsValueForKey:CPButtonAllowsMixedStateKey])
+ _allowsMixedState = [aCoder decodeBoolForKey:CPButtonAllowsMixedStateKey];
[self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]];
+ if ([aCoder containsValueForKey:CPButtonImagePositionKey])
+ [self setImagePosition:[aCoder decodeIntForKey:CPButtonImagePositionKey]];
+
+ if ([aCoder containsValueForKey:CPButtonKeyEquivalentKey])
+ [self setKeyEquivalent:CFData.decodeBase64ToUtf16String([aCoder decodeObjectForKey:CPButtonKeyEquivalentKey])];
+
+ _keyEquivalentModifierMask = [aCoder decodeIntForKey:CPButtonKeyEquivalentMaskKey];
+
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
@@ -668,13 +708,18 @@ var CPButtonImageKey = @"CPButtonImageKey",
{
[super encodeWithCoder:aCoder];
- [aCoder encodeObject:_image forKey:CPButtonImageKey];
- [aCoder encodeObject:_alternateImage forKey:CPButtonAlternateImageKey];
-
[aCoder encodeObject:_title forKey:CPButtonTitleKey];
[aCoder encodeObject:_alternateTitle forKey:CPButtonAlternateTitleKey];
- [aCoder encodeObject:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey];
+ [aCoder encodeBool:_allowsMixedState forKey:CPButtonAllowsMixedStateKey];
+
+ [aCoder encodeBool:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey];
+ [aCoder encodeInt:[self imagePosition] forKey:CPButtonImagePositionKey];
+
+ if (_keyEquivalent)
+ [aCoder encodeObject:CFData.encodeBase64Utf16String(_keyEquivalent) forKey:CPButtonKeyEquivalentKey];
+
+ [aCoder encodeInt:_keyEquivalentModifierMask forKey:CPButtonKeyEquivalentMaskKey];
}
@end
diff --git a/AppKit/CPCheckBox.j b/AppKit/CPCheckBox.j
index eec4de33b..8db4b492d 100644
--- a/AppKit/CPCheckBox.j
+++ b/AppKit/CPCheckBox.j
@@ -22,6 +22,7 @@
@import "CPButton.j"
+CPCheckBoxImageOffset = 4.0;
@implementation CPCheckBox : CPButton
{
@@ -55,7 +56,7 @@
[self setImagePosition:CPImageLeft];
[self setAlignment:CPLeftTextAlignment];
- [self setBordered:YES];
+ [self setBordered:NO];
}
return self;
diff --git a/AppKit/CPCollectionView.j b/AppKit/CPCollectionView.j
index 11d4b2ed6..33685e18c 100644
--- a/AppKit/CPCollectionView.j
+++ b/AppKit/CPCollectionView.j
@@ -297,7 +297,7 @@
*/
- (void)setSelectionIndexes:(CPIndexSet)anIndexSet
{
- if (_selectionIndexes == anIndexSet || !_isSelectable)
+ if ([_selectionIndexes isEqual:anIndexSet] || !_isSelectable)
return;
var index = CPNotFound;
@@ -312,8 +312,10 @@
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
[_items[index] setSelected:YES];
+ [[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
+
if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)])
- [_delegate collectionViewDidChangeSelection:self]
+ [_delegate collectionViewDidChangeSelection:self];
}
/*!
@@ -737,9 +739,12 @@
@end
@implementation CPCollectionView (KeyboardInteraction)
-- (CPIndexSet)_selectionForEvent:(CPEvent)anEvent withNewIndex:(int)anIndex direction:(int)aDirection
+
+- (void)_modifySelectionWithNewIndex:(int)anIndex direction:(int)aDirection expand:(BOOL)shouldExpand
{
- if (_allowsMultipleSelection && [anEvent modifierFlags] & CPShiftKeyMask)
+ anIndex = MIN(MAX(anIndex, 0), [[self items] count]-1);
+
+ if (_allowsMultipleSelection && shouldExpand)
{
var indexes = [_selectionIndexes copy],
bottomAnchor = [indexes firstIndex],
@@ -754,7 +759,8 @@
else
indexes = [CPIndexSet indexSetWithIndex:anIndex];
- return indexes;
+ [self setSelectionIndexes:indexes];
+ [self _scrollToSelection];
}
- (void)_scrollToSelection
@@ -771,26 +777,36 @@
if (index === CPNotFound)
index = [[self items] count];
- index = MAX(index - 1, 0);
+ [self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:NO];
+}
- [self setSelectionIndexes:[self _selectionForEvent:[CPApp currentEvent] withNewIndex:index direction:-1]];
- [self _scrollToSelection];
+- (void)moveLeftAndModifySelection:(id)sender
+{
+ var index = [[self selectionIndexes] firstIndex];
+ if (index === CPNotFound)
+ index = [[self items] count];
+
+ [self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:YES];
}
- (void)moveRight:(id)sender
{
- var index = MIN([[self selectionIndexes] lastIndex] + 1, [[self items] count]-1);
+ [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:NO];
+}
- [self setSelectionIndexes:[self _selectionForEvent:[CPApp currentEvent] withNewIndex:index direction:1]];
- [self _scrollToSelection];
+- (void)moveRightAndModifySelection:(id)sender
+{
+ [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:YES];
}
- (void)moveDown:(id)sender
{
- var index = MIN([[self selectionIndexes] lastIndex] + [self numberOfColumns], [[self items] count]-1);
+ [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:NO];
+}
- [self setSelectionIndexes:[self _selectionForEvent:[CPApp currentEvent] withNewIndex:index direction:1]];
- [self _scrollToSelection];
+- (void)moveDownAndModifySelection:(id)sender
+{
+ [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:YES];
}
- (void)moveUp:(id)sender
@@ -799,10 +815,16 @@
if (index == CPNotFound)
index = [[self items] count];
- index = MAX(0, index - [self numberOfColumns]);
+ [self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:NO];
+}
- [self setSelectionIndexes:[self _selectionForEvent:[CPApp currentEvent] withNewIndex:index direction:-1]];
- [self _scrollToSelection];
+- (void)moveUpAndModifySelection:(id)sender
+{
+ var index = [[self selectionIndexes] firstIndex];
+ if (index == CPNotFound)
+ index = [[self items] count];
+
+ [self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:YES];
}
- (void)deleteBackward:(id)sender
@@ -847,11 +869,13 @@
@end
-var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
- CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey",
- CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey",
- CPCollectionViewSelectableKey = @"CPCollectionViewSelectableKey",
- CPCollectionViewBackgroundColorsKey = @"CPCollectionViewBackgroundColorsKey";
+var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
+ CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey",
+ CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey",
+ CPCollectionViewMaxNumberOfRowsKey = @"CPCollectionViewMaxNumberOfRowsKey",
+ CPCollectionViewMaxNumberOfColumnsKey = @"CPCollectionViewMaxNumberOfColumnsKey",
+ CPCollectionViewSelectableKey = @"CPCollectionViewSelectableKey",
+ CPCollectionViewBackgroundColorsKey = @"CPCollectionViewBackgroundColorsKey";
@implementation CPCollectionView (CPCoding)
@@ -872,6 +896,9 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
_minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey] || CGSizeMakeZero();
_maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey] || CGSizeMakeZero();
+ _maxNumberOfRows = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfRowsKey] || 0;
+ _maxNumberOfColumns = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfColumnsKey] || 0;
+
_verticalMargin = [aCoder decodeFloatForKey:CPCollectionViewVerticalMarginKey];
_isSelectable = [aCoder decodeBoolForKey:CPCollectionViewSelectableKey];
@@ -898,6 +925,9 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
if (!CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
[aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
+ [aCoder encodeInt:_maxNumberOfRows forKey:CPCollectionViewMaxNumberOfRowsKey];
+ [aCoder encodeInt:_maxNumberOfColumns forKey:CPCollectionViewMaxNumberOfColumnsKey];
+
[aCoder encodeBool:_isSelectable forKey:CPCollectionViewSelectableKey];
[aCoder encodeFloat:_verticalMargin forKey:CPCollectionViewVerticalMarginKey];
diff --git a/AppKit/CPColor.j b/AppKit/CPColor.j
index b6eda360c..dca6843e1 100644
--- a/AppKit/CPColor.j
+++ b/AppKit/CPColor.j
@@ -56,18 +56,12 @@ var cachedBlackColor,
/*!
@ingroup appkit
- @code CPColor
\c CPColor can be used to represent color
in an RGB or HSB model with an optional transparency value.
It also provides some class helper methods that
returns instances of commonly used colors.
-
- The class does not have a \c -set: method
- like NextStep based frameworks to change the color of
- the current context. To change the color of the current
- context, use CGContextSetFillColor().
*/
@implementation CPColor : CPObject
{
@@ -82,12 +76,12 @@ var cachedBlackColor,
Each component should be between the range of 0.0 to 1.0. For
the alpha component, a value of 1.0 is opaque, and 0.0 means
completely transparent.
-
+
@param red the red component of the color
@param green the green component of the color
@param blue the blue component of the color
@param alpha the alpha component
-
+
@return a color initialized to the values specified
*/
+ (CPColor)colorWithRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
@@ -97,17 +91,17 @@ var cachedBlackColor,
/*!
@deprecated in favor of colorWithRed:green:blue:alpha:
-
+
Creates a color in the RGB color space, with an alpha value.
Each component should be between the range of 0.0 to 1.0. For
the alpha component, a value of 1.0 is opaque, and 0.0 means
completely transparent.
-
+
@param red the red component of the color
@param green the green component of the color
@param blue the blue component of the color
@param alpha the alpha component
-
+
@return a color initialized to the values specified
*/
+ (CPColor)colorWithCalibratedRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
@@ -119,10 +113,10 @@ var cachedBlackColor,
/*!
Creates a new color object with \c white for the RGB components.
For the alpha component, a value of 1.0 is opaque, and 0.0 means completely transparent.
-
+
@param white a float between 0.0 and 1.0
@param alpha the alpha component between 0.0 and 1.0
-
+
@return a color initialized to the values specified
*/
+ (CPColor)colorWithWhite:(float)white alpha:(float)alpha
@@ -132,13 +126,13 @@ var cachedBlackColor,
/*!
@deprecated in favor of colorWithWhite:apha:
-
+
Creates a new color object with \c white for the RGB components.
For the alpha component, a value of 1.0 is opaque, and 0.0 means completely transparent.
-
+
@param white a float between 0.0 and 1.0
@param alpha the alpha component between 0.0 and 1.0
-
+
@return a color initialized to the values specified
*/
+ (CPColor)colorWithCalibratedWhite:(float)white alpha:(float)alpha
@@ -148,11 +142,11 @@ var cachedBlackColor,
/*!
Creates a new color in HSB space.
-
+
@param hue the hue value
@param saturation the saturation value
@param brightness the brightness value
-
+
@return the initialized color
*/
+ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness
@@ -162,16 +156,16 @@ var cachedBlackColor,
+ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
{
- if(saturation === 0.0)
+ if (saturation === 0.0)
return [CPColor colorWithCalibratedWhite:brightness / 100.0 alpha:alpha];
-
+
var f = hue % 60,
p = (brightness * (100 - saturation)) / 10000,
q = (brightness * (6000 - saturation * f)) / 600000,
t = (brightness * (6000 - saturation * (60 -f))) / 600000,
b = brightness / 100.0;
-
- switch(FLOOR(hue / 60))
+
+ switch (FLOOR(hue / 60))
{
case 0: return [CPColor colorWithCalibratedRed: b green: t blue: p alpha: alpha];
case 1: return [CPColor colorWithCalibratedRed: q green: b blue: p alpha: alpha];
@@ -194,7 +188,8 @@ var cachedBlackColor,
*/
+ (CPColor)colorWithHexString:(string)hex
{
- return [[CPColor alloc] _initWithRGBA: hexToRGB(hex)];
+ var rgba = hexToRGB(hex);
+ return rgba ? [[CPColor alloc] _initWithRGBA: rgba] : null;
}
/*!
@@ -397,7 +392,7 @@ var cachedBlackColor,
/*!
Creates a CPColor from a valid CSS RGB string. Example, "rgb(32,64,129)".
-
+
@param aString a CSS color string
@return a color initialized to the value in the css string
*/
@@ -409,23 +404,23 @@ var cachedBlackColor,
/* @ignore */
- (id)_initWithCSSString:(CPString)aString
{
- if(aString.indexOf("rgb") == CPNotFound)
+ if (aString.indexOf("rgb") == CPNotFound)
return nil;
-
+
self = [super init];
-
+
var startingIndex = aString.indexOf("(");
var parts = aString.substring(startingIndex+1).split(',');
-
+
_components = [
parseInt(parts[0], 10) / 255.0,
parseInt(parts[1], 10) / 255.0,
parseInt(parts[2], 10) / 255.0,
parts[3] ? parseInt(parts[3], 10) / 255.0 : 1.0
- ]
-
+ ];
+
_cssString = aString;
-
+
return self;
}
@@ -433,7 +428,7 @@ var cachedBlackColor,
- (id)_initWithRGBA:(CPArray)components
{
self = [super init];
-
+
if (self)
{
_components = components;
@@ -454,14 +449,14 @@ var cachedBlackColor,
- (id)_initWithPatternImage:(CPImage)anImage
{
self = [super init];
-
+
if (self)
{
_patternImage = anImage;
_cssString = "url(\"" + [_patternImage filename] + "\")";
_components = [0.0, 0.0, 0.0, 1.0];
}
-
+
return self;
}
@@ -523,17 +518,17 @@ var cachedBlackColor,
/*!
Returns a new color with the same RGB as the receiver but a new alpha component.
-
+
@param anAlphaComponent the alpha component for the new color
-
+
@return a new color object
*/
- (CPColor)colorWithAlphaComponent:(float)anAlphaComponent
{
var components = _components.slice();
-
+
components[components.length - 1] = anAlphaComponent;
-
+
return [[[self class] alloc] _initWithRGBA:components];
}
@@ -552,35 +547,38 @@ var cachedBlackColor,
var red = ROUND(_components[_redComponent] * 255.0),
green = ROUND(_components[_greenComponent] * 255.0),
blue = ROUND(_components[_blueComponent] * 255.0);
-
+
var max = MAX(red, green, blue),
min = MIN(red, green, blue),
delta = max - min;
-
+
var brightness = max / 255.0,
saturation = (max != 0) ? delta / max : 0;
-
+
var hue;
- if(saturation == 0)
+
+ if (saturation == 0)
+ {
hue = 0;
+ }
else
{
var rr = (max - red) / delta;
var gr = (max - green) / delta;
var br = (max - blue) / delta;
-
+
if (red == max)
hue = br - gr;
else if (green == max)
hue = 2 + rr - br;
else
hue = 4 + gr - rr;
-
+
hue /= 6;
if (hue < 0)
hue++;
}
-
+
return [
ROUND(hue * 360.0),
ROUND(saturation * 100.0),
@@ -699,10 +697,8 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
@end
-var hexCharacters = "0123456789ABCDEF";
-// HACK: prevent these from becoming globals. workaround for obj-j "function foo(){}" behavior
-var hexToRGB, rgbToHex, byteToHex;
+var hexCharacters = "0123456789ABCDEF";
/*!
Used for the CPColor \c +colorWithHexString: implementation
@@ -710,39 +706,39 @@ var hexToRGB, rgbToHex, byteToHex;
@class CPColor
@return an array of rgb components
*/
-function hexToRGB(hex)
+var hexToRGB = function(hex)
{
- if ( hex.length == 3 )
+ if (hex.length == 3)
hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
- if(hex.length != 6)
+
+ if (hex.length != 6)
return null;
hex = hex.toUpperCase();
- for(var i=0; i [self bounds].size.width - 1 || point.x < 1)
return NO;
@@ -535,21 +535,21 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
return NO;
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
-
+
var swatch = _swatches[FLOOR(point.x / 13)];
-
+
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
_dragColor = [[swatch subviews][0] backgroundColor];
-
+
var bounds = CPRectCreateCopy([swatch bounds]);
// 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)];
-
+
[dragView setBackgroundColor:[CPColor blackColor]];
[dragFillView setBackgroundColor:_dragColor];
-
+
[dragView addSubview:dragFillView];
[self dragView: dragView
@@ -568,14 +568,14 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
}
- (void)performDragOperation:(id )aSender
-{
+{
var location = [self convertPoint:[aSender draggingLocation] fromView:nil],
pasteboard = [aSender draggingPasteboard],
swatch = nil;
if(![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1)
return NO;
-
+
[self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex: FLOOR(location.x / 13)];
}
@@ -590,9 +590,9 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
- (id)initWithFrame:(CPRect)aFrame
{
self = [super initWithFrame:aFrame];
-
+
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
-
+
return self;
}
@@ -610,9 +610,9 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
{
var pasteboard = [aSender draggingPasteboard];
- if(![pasteboard availableTypeFromArray:[CPColorDragType]])
+ if (![pasteboard availableTypeFromArray:[CPColorDragType]])
return NO;
-
+
var color = [CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]];
[_colorPanel setColor:color updatePicker:YES];
}
@@ -626,19 +626,19 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
{
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
- [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self];
-
+ [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self];
+
var bounds = CPRectMake(0, 0, 15, 15);
-
+
// 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)];
-
+
[dragView setBackgroundColor:[CPColor blackColor]];
[dragFillView setBackgroundColor:[self backgroundColor]];
-
+
[dragView addSubview:dragFillView];
-
+
[self dragView: dragView
at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
offset: CPPointMake(0.0, 0.0)
@@ -650,7 +650,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
{
- if(aType == CPColorDragType)
+ if (aType == CPColorDragType)
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self backgroundColor]] forType:aType];
}
diff --git a/AppKit/CPColorWell.j b/AppKit/CPColorWell.j
index 040e5c548..891c3a331 100644
--- a/AppKit/CPColorWell.j
+++ b/AppKit/CPColorWell.j
@@ -278,7 +278,7 @@ var CPColorWellColorKey = "CPColorWellColorKey",
if (self)
{
_active = NO;
- _bordered = [aCoder decodeObjectForKey:CPColorWellBorderedKey];
+ _bordered = [aCoder decodeBoolForKey:CPColorWellBorderedKey];
_color = [aCoder decodeObjectForKey:CPColorWellColorKey];
[self drawBezelWithHighlight:NO];
diff --git a/AppKit/CPControl.j b/AppKit/CPControl.j
index 7de6719c4..1b6fd6e9f 100644
--- a/AppKit/CPControl.j
+++ b/AppKit/CPControl.j
@@ -280,7 +280,7 @@ var CPControlBlackColor = [CPColor blackColor];
- (void)trackMouse:(CPEvent)anEvent
{
var type = [anEvent type],
- currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil];
+ currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
if (type === CPLeftMouseUp)
@@ -289,7 +289,6 @@ var CPControlBlackColor = [CPColor blackColor];
_trackingMouseDownFlags = 0;
}
-
else
{
if (type === CPLeftMouseDown)
@@ -337,9 +336,19 @@ var CPControlBlackColor = [CPColor blackColor];
[self highlight:YES];
[self setState:[self nextState]];
- [self sendAction:[self action] to:[self target]];
- [CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unhighlightButtonTimerDidFinish:) userInfo:nil repeats:NO];
+ try
+ {
+ [self sendAction:[self action] to:[self target]];
+ }
+ catch (e)
+ {
+ throw e;
+ }
+ finally
+ {
+ [CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unhighlightButtonTimerDidFinish:) userInfo:nil repeats:NO];
+ }
}
- (void)unhighlightButtonTimerDidFinish:(id)sender
@@ -545,6 +554,7 @@ var CPControlBlackColor = [CPColor blackColor];
return;
[self _reverseSetBinding];
+
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
}
diff --git a/AppKit/CPCookie.j b/AppKit/CPCookie.j
index 5a5694a94..a34d6e59f 100644
--- a/AppKit/CPCookie.j
+++ b/AppKit/CPCookie.j
@@ -92,21 +92,28 @@
domain = "; domain="+domain;
else
domain = "";
-
- document.cookie = _cookieName+"="+value+expires+"; path=/"+domain;
+
+#if PLATFORM(DOM)
+ document.cookie = _cookieName+"="+value+expires+"; path=/"+domain;
+#else
+ _cookieValue = value;
+ _expires = expires;
+#endif
}
/* @ignore */
- (CPString)_readCookieValue
{
- var nameEQ = _cookieName + "=";
- var ca = document.cookie.split(';');
- for(var i=0;i < ca.length;i++) {
- var c = ca[i];
- while (c.charAt(0)==' ') c = c.substring(1,c.length);
- if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
- }
- return "";
+#if PLATFORM(DOM)
+ var nameEQ = _cookieName + "=";
+ var ca = document.cookie.split(';');
+ for(var i=0;i < ca.length;i++) {
+ var c = ca[i];
+ while (c.charAt(0)==' ') c = c.substring(1,c.length);
+ if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
+ }
+#endif
+ return "";
}
@end
diff --git a/AppKit/CPCursor.j b/AppKit/CPCursor.j
index 45b3f1ab8..d550fb10c 100755
--- a/AppKit/CPCursor.j
+++ b/AppKit/CPCursor.j
@@ -197,7 +197,7 @@ var currentCursor = nil,
+ (void)unhide
{
- [self _setCursorCSS:[currentCursor _cssString]]
+ [self _setCursorCSS:[currentCursor _cssString]];
}
+ (void)setHiddenUntilMouseMoves:(BOOL)flag
diff --git a/AppKit/CPDragServer.j b/AppKit/CPDragServer.j
index 9469f331f..adb529706 100644
--- a/AppKit/CPDragServer.j
+++ b/AppKit/CPDragServer.j
@@ -262,7 +262,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
{
var contentView = [scrollView contentView],
bounds = [contentView bounds],
- insetBounds = CGRectInset(bounds, 10, 10)
+ insetBounds = CGRectInset(bounds, 10, 10),
eventLocation = [contentView convertPoint:_draggingLocation fromView:nil],
deltaX = 0,
deltaY = 0;
@@ -304,7 +304,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
- (void)_sendPeriodicDraggingUpdate:(CPTimer)aTimer
{
var userInfo = [aTimer userInfo];
- _dragOperation = [self draggingUpdatedInPlatformWindow:[userInfo objectForKey:@"platformWindow"]
+ _dragOperation = [self draggingUpdatedInPlatformWindow:[userInfo objectForKey:@"platformWindow"]
location:[userInfo objectForKey:@"location"]];
}
@@ -328,8 +328,8 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
- (void)performDragOperationInPlatformWindow:(CPPlatformWindow)aPlatformWindow
{
- if (_draggingDestination &&
- (![_draggingDestination respondsToSelector:@selector(prepareForDragOperation:)] || [_draggingDestination prepareForDragOperation:CPDragServerDraggingInfo]) &&
+ if (_draggingDestination &&
+ (![_draggingDestination respondsToSelector:@selector(prepareForDragOperation:)] || [_draggingDestination prepareForDragOperation:CPDragServerDraggingInfo]) &&
(![_draggingDestination respondsToSelector:@selector(performDragOperation:)] || [_draggingDestination performDragOperation:CPDragServerDraggingInfo]) &&
[_draggingDestination respondsToSelector:@selector(concludeDragOperation:)])
[_draggingDestination concludeDragOperation:CPDragServerDraggingInfo];
diff --git a/AppKit/CPEvent.j b/AppKit/CPEvent.j
index 0fca38549..6c05c96d6 100644
--- a/AppKit/CPEvent.j
+++ b/AppKit/CPEvent.j
@@ -21,6 +21,7 @@
*/
@import
+@import "CPText.j"
#include "CoreGraphics/CGGeometry.h"
@@ -178,7 +179,8 @@ CPDOMEventTouchEnd = "touchend";
CPDOMEventTouchCancel = "touchcancel";
var _CPEventPeriodicEventPeriod = 0,
- _CPEventPeriodicEventTimer = nil;
+ _CPEventPeriodicEventTimer = nil,
+ _CPEventUpperCaseRegex = new RegExp("[A-Z]");
/*!
@ingroup appkit
@@ -521,10 +523,10 @@ var _CPEventPeriodicEventPeriod = 0,
- (BOOL)_triggersKeyEquivalent:(CPString)aKeyEquivalent withModifierMask:aKeyEquivalentModifierMask
{
- var characters = [self charactersIgnoringModifiers],
- modifierFlags = [self modifierFlags];
+ if (!aKeyEquivalent)
+ return NO;
- if (new RegExp("[A-Z]").test(aKeyEquivalent))
+ if (_CPEventUpperCaseRegex.test(aKeyEquivalent))
aKeyEquivalentModifierMask |= CPShiftKeyMask;
if (CPBrowserIsOperatingSystem(CPWindowsOperatingSystem) && (aKeyEquivalentModifierMask & CPCommandKeyMask))
@@ -533,35 +535,39 @@ var _CPEventPeriodicEventPeriod = 0,
aKeyEquivalentModifierMask &= ~CPCommandKeyMask;
}
- if ((modifierFlags & (CPShiftKeyMask | CPAlternateKeyMask | CPCommandKeyMask | CPControlKeyMask)) !== aKeyEquivalentModifierMask)
+ if ((_modifierFlags & (CPShiftKeyMask | CPAlternateKeyMask | CPCommandKeyMask | CPControlKeyMask)) !== aKeyEquivalentModifierMask)
return NO;
- return [characters caseInsensitiveCompare:aKeyEquivalent] === CPOrderedSame;
+ // Treat \r and \n as the same key equivalent. See issue #710.
+ if (_characters === CPNewlineCharacter || _characters === CPCarriageReturnCharacter)
+ return CPNewlineCharacter === aKeyEquivalent || CPCarriageReturnCharacter === aKeyEquivalent;
+
+ return [_characters caseInsensitiveCompare:aKeyEquivalent] === CPOrderedSame;
}
- (BOOL)_couldBeKeyEquivalent
{
- // FIXME: More cases? Space?
- return _type === CPKeyDown &&
- ((_modifierFlags & (CPCommandKeyMask | CPControlKeyMask) &&
- [_characters length] > 0) ||
- [self _hasActionCharacter]);
-}
+ if (_type !== CPKeyDown)
+ return NO;
-- (BOOL)_hasActionCharacter
-{
- var characters = [self characters],
- characterCount = [characters length];
+ var characterCount = _characters.length;
+
+ if (!characterCount)
+ return NO;
+
+ if (_modifierFlags & (CPCommandKeyMask | CPControlKeyMask))
+ return YES;
for(var i=0; i= CPRectGetMinX(aRect) &&
aPoint.y >= CPRectGetMinY(aRect) &&
- aPoint.x < CPRectGetMaxX(aRect) &&
- aPoint.y < CPRectGetMaxY(aRect);
+ aPoint.x < CPRectGetMaxX(aRect) &&
+ aPoint.y < CPRectGetMaxY(aRect);
}
/*!
@@ -261,7 +261,7 @@ function CPPointEqualToPoint(lhsPoint, rhsPoint)
*/
function CPRectEqualToRect(lhsRect, rhsRect)
{
- return CPPointEqualToPoint(lhsRect.origin, rhsRect.origin) &&
+ return CPPointEqualToPoint(lhsRect.origin, rhsRect.origin) &&
CPSizeEqualToSize(lhsRect.size, rhsRect.size);
}
@@ -380,6 +380,20 @@ 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
+ with a line that's parallel to the side of inRect specified by edge.
+ The size of slice is determined by amount, which specifies the distance from edge.
+
+ slice and rem must not be NULL.
+
+ @group CGRect
+*/
+function CPDivideRect(inRect, slice, rem, amount, edge)
+{
+ CGRectDivide(inRect, slice, rem, amount, edge);
+}
+
/*!
Returns \c YES if the two CGSizes are identical.
@group CGSize
@@ -434,7 +448,7 @@ function CPStringFromRect(aRect)
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) };
}
@@ -447,7 +461,7 @@ function CPPointFromString(aString)
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) };
}
@@ -460,7 +474,7 @@ function CPSizeFromString(aString)
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)) };
}
@@ -504,6 +518,6 @@ function CPPointMakeZero()
return CPPointMake(0, 0, 0);
}
-/*!
+/*!
@}
*/
diff --git a/AppKit/CPGraphics.j b/AppKit/CPGraphics.j
new file mode 100644
index 000000000..caf4c82aa
--- /dev/null
+++ b/AppKit/CPGraphics.j
@@ -0,0 +1,110 @@
+/*
+ * CPGraphics.j
+ * AppKit
+ *
+ * Created by Francisco Tolmasky.
+ * Copyright 2010, 280 North, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+@import "CPColor.j"
+@import "CPGraphicsContext.j"
+
+#include "CoreGraphics/CGGeometry.h"
+
+
+function CPDrawTiledRects(
+ /* CGRect */ boundsRect,
+ /* CGRect */ clipRect,
+ /* CPRectEdge[] */ sides,
+ /* float[] */ grays)
+{
+ if (sides.length != grays.length)
+ [CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and grays (length: " + grays.length + ") must have the same length."];
+
+ var colors = [];
+
+ for (var i = 0; i < grays.length; ++i)
+ colors.push([CPColor colorWithCalibratedWhite:grays[i] alpha:1.0]);
+
+ return CPDrawColorTiledRects(boundsRect, clipRect, sides, colors);
+}
+
+function CPDrawColorTiledRects(
+ /* CGRect */ boundsRect,
+ /* CGRect */ clipRect,
+ /* CPRectEdge[] */ sides,
+ /* CPColor[] */ colors)
+{
+ if (sides.length != colors.length)
+ [CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and colors (length: " + colors.length + ") must have the same length."];
+
+ var resultRect = _CGRectMakeCopy(boundsRect),
+ slice = _CGRectMakeZero(),
+ remainder = _CGRectMakeZero(),
+ context = [[CPGraphicsContext currentContext] graphicsPort];
+
+ CGContextSaveGState(context);
+ CGContextSetLineWidth(context, 1.0);
+
+ for (var sideIndex = 0; sideIndex < sides.length; ++sideIndex)
+ {
+ var side = sides[sideIndex];
+
+ CGRectDivide(resultRect, slice, remainder, 1.0, side);
+ resultRect = remainder;
+ slice = CGRectIntersection(slice, clipRect);
+
+ // Cocoa docs say that only slices that are within the clipRect are actually drawn
+ if (_CGRectIsEmpty(slice))
+ continue;
+
+ var minX, maxX, minY, maxY;
+
+ if (side == CPMinXEdge || side == CPMaxXEdge)
+ {
+ // Make sure we have at least 1 pixel to draw a line
+ if (_CGRectGetWidth(slice) < 1.0)
+ continue;
+
+ minX = _CGRectGetMinX(slice) + 0.5;
+ maxX = minX;
+ minY = _CGRectGetMinY(slice);
+ maxY = _CGRectGetMaxY(slice);
+ }
+ else // CPMinYEdge || CPMaxYEdge
+ {
+ // Make sure we have at least 1 pixel to draw a line
+ if (_CGRectGetHeight(slice) < 1.0)
+ continue;
+
+ minX = _CGRectGetMinX(slice);
+ maxX = _CGRectGetMaxX(slice);
+ minY = _CGRectGetMinY(slice) + 0.5;
+ maxY = minY;
+ }
+
+ CGContextBeginPath(context);
+ CGContextMoveToPoint(context, minX, minY);
+ CGContextAddLineToPoint(context, maxX, maxY);
+ CGContextSetStrokeColor(context, colors[sideIndex]);
+ CGContextStrokePath(context);
+ }
+
+ CGContextRestoreGState(context);
+
+ return resultRect;
+}
\ No newline at end of file
diff --git a/AppKit/CPImage.j b/AppKit/CPImage.j
index 0bc69ba60..53a3f1937 100644
--- a/AppKit/CPImage.j
+++ b/AppKit/CPImage.j
@@ -193,6 +193,9 @@ function CPAppKitImage(aFilename, aSize)
var imageOrSize = AppKitImageForNames[aName];
+ if (!imageOrSize)
+ return nil;
+
if (!imageOrSize.isa)
{
imageOrSize = CPAppKitImage("CPImage/" + aName + ".png", imageOrSize);
@@ -205,17 +208,19 @@ function CPAppKitImage(aFilename, aSize)
return imageOrSize;
}
-- (void)setName:(CPString)aName
+- (BOOL)setName:(CPString)aName
{
if (_name === aName)
- return;
+ return YES;
- if (imagesForNames[aName] === self)
- imagesForNames[aName] = nil;
+ if (imagesForNames[aName])
+ return NO;
_name = aName;
imagesForNames[aName] = self;
+
+ return YES;
}
- (CPString)name
diff --git a/AppKit/CPImageView.j b/AppKit/CPImageView.j
index 7d365f71f..25189100d 100644
--- a/AppKit/CPImageView.j
+++ b/AppKit/CPImageView.j
@@ -35,7 +35,18 @@ CPScaleProportionally = 0;
CPScaleToFit = 1;
CPScaleNone = 2;
-var CPImageViewShadowBackgroundColor = nil;
+CPImageAlignCenter = 0;
+CPImageAlignTop = 1;
+CPImageAlignTopLeft = 2;
+CPImageAlignTopRight = 3;
+CPImageAlignLeft = 4;
+CPImageAlignBottom = 5;
+CPImageAlignBottomLeft = 6;
+CPImageAlignBottomRight = 7;
+CPImageAlignRight = 8;
+
+var CPImageViewShadowBackgroundColor = nil,
+ CPImageViewEmptyPlaceholderImage = nil;
var LEFT_SHADOW_INSET = 3.0,
RIGHT_SHADOW_INSET = 3.0,
@@ -52,14 +63,22 @@ var LEFT_SHADOW_INSET = 3.0,
*/
@implementation CPImageView : CPControl
{
- DOMElement _DOMImageElement;
+ DOMElement _DOMImageElement;
- BOOL _hasShadow;
- CPView _shadowView;
+ BOOL _hasShadow;
+ CPView _shadowView;
- BOOL _isEditable;
+ BOOL _isEditable;
- CGRect _imageRect;
+ CGRect _imageRect;
+ CPImageAlignment _imageAlignment;
+}
+
++ (void)initialize
+{
+ var bundle = [CPBundle bundleForClass:[CPView class]];
+
+ CPImageViewEmptyPlaceholderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"empty.png"]];
}
- (id)initWithFrame:(CGRect)aFrame
@@ -120,7 +139,7 @@ var LEFT_SHADOW_INSET = 3.0,
var newImage = [self objectValue];
#if PLATFORM(DOM)
- _DOMImageElement.src = newImage ? [newImage filename] : "";
+ _DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
#endif
var size = [newImage size];
@@ -191,6 +210,30 @@ var LEFT_SHADOW_INSET = 3.0,
[self hideOrDisplayContents];
}
+/*!
+ Sets the type of image alignment that should be used to
+ render the image.
+ @param anImageAlignment the type of scaling to use
+*/
+- (void)setImageAlignment:(CPImageAlignment)anImageAlignment
+{
+ if (_imageAlignment == anImageAlignment)
+ return;
+
+ _imageAlignment = anImageAlignment;
+
+ if (![self image])
+ return;
+
+ [self setNeedsLayout];
+ [self setNeedsDisplay:YES];
+}
+
+- (unsigned)imageAlignment
+{
+ return _imageAlignment;
+}
+
/*!
Sets the type of image scaling that should be used to
render the image.
@@ -318,8 +361,45 @@ var LEFT_SHADOW_INSET = 3.0,
#endif
}
- var x = (boundsWidth - width) / 2.0,
- y = (boundsHeight - height) / 2.0;
+ var x, y;
+
+ switch (_imageAlignment)
+ {
+ case CPImageAlignLeft:
+ case CPImageAlignTopLeft:
+ case CPImageAlignBottomLeft:
+ x = 0.0;
+ break;
+
+ case CPImageAlignRight:
+ case CPImageAlignTopRight:
+ case CPImageAlignBottomRight:
+ x = boundsWidth - width;
+ break;
+
+ default:
+ x = (boundsWidth - width) / 2.0;
+ break;
+ }
+
+ switch (_imageAlignment)
+ {
+ case CPImageAlignTop:
+ case CPImageAlignTopLeft:
+ case CPImageAlignTopRight:
+ y = 0.0;
+ break;
+
+ case CPImageAlignBottom:
+ case CPImageAlignBottomLeft:
+ case CPImageAlignBottomRight:
+ y = boundsHeight - height;
+ break;
+
+ default:
+ y = (boundsHeight - height) / 2.0;
+ break;
+ }
#if PLATFORM(DOM)
CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, x, y);
@@ -380,10 +460,11 @@ var LEFT_SHADOW_INSET = 3.0,
@end
-var CPImageViewImageKey = @"CPImageViewImageKey",
- CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
- CPImageViewHasShadowKey = @"CPImageViewHasShadowKey",
- CPImageViewIsEditableKey = @"CPImageViewIsEditableKey";
+var CPImageViewImageKey = @"CPImageViewImageKey",
+ CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
+ CPImageViewImageAlignmentKey = @"CPImageViewImageAlignmentKey",
+ CPImageViewHasShadowKey = @"CPImageViewHasShadowKey",
+ CPImageViewIsEditableKey = @"CPImageViewIsEditableKey";
@implementation CPImageView (CPCoding)
@@ -416,6 +497,7 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
#endif
[self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]];
+ [self setImageAlignment:[aCoder decodeIntForKey:CPImageViewImageAlignmentKey]];
if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey] || NO)
[self setEditable:YES];
@@ -450,6 +532,7 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
_subviews = actualSubviews;
[aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey];
+ [aCoder encodeInt:_imageAlignment forKey:CPImageViewImageAlignmentKey];
if (_isEditable)
[aCoder encodeBool:_isEditable forKey:CPImageViewIsEditableKey];
diff --git a/AppKit/CPKeyBinding.j b/AppKit/CPKeyBinding.j
new file mode 100644
index 000000000..5cc46b422
--- /dev/null
+++ b/AppKit/CPKeyBinding.j
@@ -0,0 +1,255 @@
+/*
+ * CPKeyBinding.j
+ * AppKit
+ *
+ * Created by Nicholas Small.
+ * Copyright 2010, 280 North, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+@import
+
+
+CPStandardKeyBindings = {
+ @"@.": @"cancelOperation:",
+
+ @"^a": @"moveToBeginningOfParagraph:",
+ @"^$a": @"moveToBeginningOfParagraphAndModifySelection:",
+ @"^b": @"moveBackward:",
+ @"^$b": @"moveBackwardAndModifySelection:",
+ @"^~b": @"moveWordBackward:",
+ @"^~$b": @"moveWordBackwardAndModifySelection:",
+ @"^d": @"deleteForward:",
+ @"^e": @"moveToEndOfParagraph:",
+ @"^$e": @"moveToEndOfParagraphAndModifySelection:",
+ @"^f": @"moveForward:",
+ @"^$f": @"moveForwardAndModifySelection:",
+ @"^~f": @"moveWordForward:",
+ @"^~$f": @"moveWordForwardAndModifySelection:",
+ @"^h": @"deleteBackward:",
+ @"^k": @"deleteToEndOfParagraph:",
+ @"^l": @"centerSelectionInVisibleArea:",
+ @"^n": @"moveDown:",
+ @"^$n": @"moveDownAndModifySelection:",
+ @"^o": [@"insertNewlineIgnoringFieldEditor:", @"moveBackward:"],
+ @"^p": @"moveUp:",
+ @"^$p": @"moveUpAndModifySelection:",
+ @"^t": @"transpose:",
+ @"^v": @"pageDown:",
+ @"^$v": @"pageDownAndModifySelection:",
+ @"^y": @"yank:"
+};
+
+CPStandardKeyBindings[CPNewlineCharacter] = @"insertNewline:";
+CPStandardKeyBindings[CPCarriageReturnCharacter] = @"insertNewline:";
+CPStandardKeyBindings[CPEnterCharacter] = @"insertNewline:";
+CPStandardKeyBindings[@"~" + CPNewlineCharacter] = @"insertNewlineIgnoringFieldEditor:";
+CPStandardKeyBindings[@"~" + CPCarriageReturnCharacter] = @"insertNewlineIgnoringFieldEditor:";
+CPStandardKeyBindings[@"~" + CPEnterCharacter] = @"insertNewlineIgnoringFieldEditor:";
+CPStandardKeyBindings[@"^" + CPNewlineCharacter] = @"insertLineBreak:";
+CPStandardKeyBindings[@"^" + CPCarriageReturnCharacter] = @"insertLineBreak:";
+CPStandardKeyBindings[@"^" + CPEnterCharacter] = @"insertLineBreak:";
+
+CPStandardKeyBindings[CPBackspaceCharacter] = @"deleteBackward:";
+CPStandardKeyBindings[@"~" + CPBackspaceCharacter] = @"deleteWordBackward:";
+CPStandardKeyBindings[CPDeleteCharacter] = @"deleteBackward:";
+CPStandardKeyBindings[@"@" + CPDeleteCharacter] = @"deleteToBeginningOfLine:";
+CPStandardKeyBindings[@"~" + CPDeleteCharacter] = @"deleteWordBackward:";
+CPStandardKeyBindings[@"^" + CPDeleteCharacter] = @"deleteBackwardByDecomposingPreviousCharacter:";
+CPStandardKeyBindings[@"^~" + CPDeleteCharacter] = @"deleteWordBackward:";
+
+CPStandardKeyBindings[CPDeleteFunctionKey] = @"deleteForward:";
+CPStandardKeyBindings[@"~" + CPDeleteFunctionKey] = @"deleteWordForward:";
+
+CPStandardKeyBindings[CPTabCharacter] = @"insertTab:";
+CPStandardKeyBindings[@"~" + CPTabCharacter] = @"insertTabIgnoringFieldEditor:";
+CPStandardKeyBindings[@"^" + CPTabCharacter] = @"selectNextKeyView:";
+CPStandardKeyBindings[CPBackTabCharacter] = @"insertBacktab:";
+CPStandardKeyBindings[@"^" + CPBackTabCharacter] = @"selectPreviousKeyView:";
+
+CPStandardKeyBindings[CPEscapeFunctionKey] = @"cancelOperation:";
+CPStandardKeyBindings[@"~" + CPEscapeFunctionKey] = @"complete:";
+CPStandardKeyBindings[CPF5FunctionKey] = @"complete:";
+
+CPStandardKeyBindings[CPLeftArrowFunctionKey] = @"moveLeft:";
+CPStandardKeyBindings[@"~" + CPLeftArrowFunctionKey] = @"moveWordLeft:";
+CPStandardKeyBindings[@"^" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLine:";
+CPStandardKeyBindings[@"@" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLine:";
+CPStandardKeyBindings[@"$" + CPLeftArrowFunctionKey] = @"moveLeftAndModifySelection:";
+CPStandardKeyBindings[@"$~" + CPLeftArrowFunctionKey] = @"moveWordLeftAndModifySelection:";
+CPStandardKeyBindings[@"$^" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLineAndModifySelection:";
+CPStandardKeyBindings[@"$@" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLineAndModifySelection:";
+CPStandardKeyBindings[@"@^" + CPLeftArrowFunctionKey] = @"makeBaseWritingDirectionRightToLeft:";
+CPStandardKeyBindings[@"@^~" + CPLeftArrowFunctionKey] = @"makeTextWritingDirectionRightToLeft:";
+
+CPStandardKeyBindings[CPRightArrowFunctionKey] = @"moveRight:";
+CPStandardKeyBindings[@"~" + CPRightArrowFunctionKey] = @"moveWordRight:";
+CPStandardKeyBindings[@"^" + CPRightArrowFunctionKey] = @"moveToRightEndOfLine:";
+CPStandardKeyBindings[@"@" + CPRightArrowFunctionKey] = @"moveToRightEndOfLine:";
+CPStandardKeyBindings[@"$" + CPRightArrowFunctionKey] = @"moveRightAndModifySelection:";
+CPStandardKeyBindings[@"$~" + CPRightArrowFunctionKey] = @"moveWordRightAndModifySelection:";
+CPStandardKeyBindings[@"$^" + CPRightArrowFunctionKey] = @"moveToRightEndOfLineAndModifySelection:";
+CPStandardKeyBindings[@"$@" + CPRightArrowFunctionKey] = @"moveToRightEndOfLineAndModifySelection:";
+CPStandardKeyBindings[@"@^" + CPRightArrowFunctionKey] = @"makeBaseWritingDirectionLeftToRight:";
+CPStandardKeyBindings[@"@^~" + CPRightArrowFunctionKey] = @"makeTextWritingDirectionLeftToRight:";
+
+CPStandardKeyBindings[CPUpArrowFunctionKey] = @"moveUp:";
+CPStandardKeyBindings[@"~" + CPUpArrowFunctionKey] = [@"moveBackward:", @"moveToBeginningOfParagraph:"];
+CPStandardKeyBindings[@"^" + CPUpArrowFunctionKey] = @"scrollPageUp:";
+CPStandardKeyBindings[@"@" + CPUpArrowFunctionKey] = @"moveToBeginningOfDocument:";
+CPStandardKeyBindings[@"$" + CPUpArrowFunctionKey] = @"moveUpAndModifySelection:";
+CPStandardKeyBindings[@"$~" + CPUpArrowFunctionKey] = @"moveParagraphBackwardAndModifySelection:";
+CPStandardKeyBindings[@"$@" + CPUpArrowFunctionKey] = @"moveToBeginningOfDocumentAndModifySelection:";
+
+CPStandardKeyBindings[CPDownArrowFunctionKey] = @"moveDown:";
+CPStandardKeyBindings[@"~" + CPDownArrowFunctionKey] = [@"moveForward:", @"moveToEndOfParagraph:"];
+CPStandardKeyBindings[@"^" + CPDownArrowFunctionKey] = @"scrollPageDown:";
+CPStandardKeyBindings[@"@" + CPDownArrowFunctionKey] = @"moveToEndOfDocument:";
+CPStandardKeyBindings[@"$" + CPDownArrowFunctionKey] = @"moveDownAndModifySelection:";
+CPStandardKeyBindings[@"$~" + CPDownArrowFunctionKey] = @"moveParagraphForwardAndModifySelection:";
+CPStandardKeyBindings[@"$@" + CPDownArrowFunctionKey] = @"moveToEndOfDocumentAndModifySelection:";
+CPStandardKeyBindings[@"@^" + CPDownArrowFunctionKey] = @"makeBaseWritingDirectionNatural:";
+CPStandardKeyBindings[@"@^~" + CPDownArrowFunctionKey] = @"makeTextWritingDirectionNatural:";
+
+CPStandardKeyBindings[CPHomeFunctionKey] = @"scrollToBeginningOfDocument:";
+CPStandardKeyBindings[@"$" + CPHomeFunctionKey] = @"moveToBeginningOfDocumentAndModifySelection:";
+CPStandardKeyBindings[CPEndFunctionKey] = @"scrollToEndOfDocument:";
+CPStandardKeyBindings[@"$" + CPEndFunctionKey] = @"moveToEndOfDocumentAndModifySelection:";
+
+CPStandardKeyBindings[CPPageUpFunctionKey] = @"scrollPageUp:";
+CPStandardKeyBindings[@"~" + CPPageUpFunctionKey] = @"pageUp:";
+CPStandardKeyBindings[@"$" + CPPageUpFunctionKey] = @"pageUpAndModifySelection:";
+CPStandardKeyBindings[CPPageDownFunctionKey] = @"scrollPageDown:";
+CPStandardKeyBindings[@"~" + CPPageDownFunctionKey] = @"pageDown:";
+CPStandardKeyBindings[@"$" + CPPageDownFunctionKey] = @"pageDownAndModifySelection:";
+
+var CPKeyBindingCache = {};
+
+@implementation CPKeyBinding : CPObject
+{
+ CPString _key;
+ unsigned _modifierFlags;
+
+ CPArray _selectors;
+
+ CPString _cacheName;
+}
+
++ (void)initialize
+{
+ if ([self class] !== CPKeyBinding)
+ return;
+
+ [self createKeyBindingsFromJSObject:CPStandardKeyBindings];
+}
+
++ (void)createKeyBindingsFromJSObject:(JSObject)anObject
+{
+ var binding;
+ for (binding in anObject)
+ {
+ var components = binding.split(@""),
+ modifierFlags = ([components containsObject:@"$"] ? CPShiftKeyMask : 0) |
+ ([components containsObject:@"^"] ? CPControlKeyMask : 0) |
+ ([components containsObject:@"~"] ? CPAlternateKeyMask : 0) |
+ ([components containsObject:@"@"] ? CPCommandKeyMask : 0);
+
+ var selectors = anObject[binding];
+ if (![selectors isKindOfClass:CPArray])
+ selectors = [selectors];
+
+ var keyBinding = [[self alloc] initWithKey:[components lastObject] modifierFlags:modifierFlags selectors:selectors];
+ [self cacheKeyBinding:keyBinding];
+ }
+}
+
++ (void)cacheKeyBinding:(CPKeyBinding)aBinding
+{
+ if (!aBinding)
+ return;
+
+ CPKeyBindingCache[[aBinding _cacheName]] = aBinding;
+}
+
++ (CPKeyBinding)keyBindingForKey:(CPString)aKey modifierFlags:(unsigned)aFlag
+{
+ var tempBinding = [[self alloc] initWithKey:aKey modifierFlags:aFlag selectors:nil];
+ return CPKeyBindingCache[[tempBinding _cacheName]];
+}
+
++ (CPArray)selectorsForKey:(CPString)aKey modifierFlags:(unsigned)aFlag
+{
+ return [[self keyBindingForKey:aKey modifierFlags:aFlag] selectors];
+}
+
+- (id)initWithKey:(CPString)aKey modifierFlags:(unsigned)aFlag selectors:(CPArray)selectors
+{
+ self = [super init];
+
+ if (self)
+ {
+ _key = aKey;
+ _modifierFlags = aFlag;
+
+ _selectors = selectors;
+
+ // We normalize our key binding string in order to properly cache it.
+ // We want to ensure the modifiers are always in the same order.
+ var cacheName = [];
+
+ if (_modifierFlags & CPCommandKeyMask)
+ cacheName.push(@"@");
+ if (_modifierFlags & CPControlKeyMask)
+ cacheName.push(@"^");
+ if (_modifierFlags & CPAlternateKeyMask)
+ cacheName.push(@"~");
+ if (_modifierFlags & CPShiftKeyMask)
+ cacheName.push(@"$");
+
+ cacheName.push(_key);
+
+ _cacheName = cacheName.join(@"");
+ }
+
+ return self;
+}
+
+- (CPString)key
+{
+ return _key;
+}
+
+- (unsigned)modifierFlags
+{
+ return _modifierFlags;
+}
+
+- (CPArray)selectors
+{
+ return _selectors;
+}
+
+- (CPString)_cacheName
+{
+ return _cacheName;
+}
+
+- (BOOL)isEqual:(CPKeyBinding)rhs
+{
+ return _key === [rhs key] && _modifierFlags === [rhs modifierFlags];
+}
+
+@end
diff --git a/AppKit/CPKeyValueBinding.j b/AppKit/CPKeyValueBinding.j
index 09ae8a59e..1ea547e5d 100644
--- a/AppKit/CPKeyValueBinding.j
+++ b/AppKit/CPKeyValueBinding.j
@@ -108,7 +108,7 @@ var CPBindingOperationAnd = 0,
count = allKeys.length;
while (count--)
- [anObject unbind:[bindings objectForKey:allKeys[count]]]
+ [anObject unbind:[bindings objectForKey:allKeys[count]]];
[bindingsMap removeObjectForKey:[anObject hash]];
}
@@ -181,7 +181,19 @@ var CPBindingOperationAnd = 0,
valueTransformer;
if (valueTransformerName)
+ {
valueTransformer = [CPValueTransformer valueTransformerForName:valueTransformerName];
+
+ if (!valueTransformer)
+ {
+ var valueTransformerClass = CPClassFromString(valueTransformerName);
+ if (valueTransformerClass)
+ {
+ valueTransformer = [[valueTransformerClass alloc] init];
+ [valueTransformerClass setValueTransformer:valueTransformer forName:valueTransformerName];
+ }
+ }
+ }
else
valueTransformer = [options objectForKey:CPValueTransformerBindingOption];
@@ -263,7 +275,7 @@ var CPBindingOperationAnd = 0,
// CPLog.warn("No binding exposed on "+self+" for "+aBinding);
[self unbind:aBinding];
- [[CPKeyValueBinding alloc] initWithBinding:[anObject _replacementKeyPathForBinding:aBinding] name:aBinding to:anObject keyPath:aKeyPath options:options from:self];
+ [[CPKeyValueBinding alloc] initWithBinding:[self _replacementKeyPathForBinding:aBinding] name:aBinding to:anObject keyPath:aKeyPath options:options from:self];
}
- (CPDictionary)infoForBinding:(CPString)aBinding
diff --git a/AppKit/CPMenu/CPMenu.j b/AppKit/CPMenu/CPMenu.j
index 39203dfa7..4cdcd7a16 100644
--- a/AppKit/CPMenu/CPMenu.j
+++ b/AppKit/CPMenu/CPMenu.j
@@ -75,7 +75,7 @@ var _CPMenuBarVisible = NO,
id _delegate;
- CPMenuItem _highlightedIndex;
+ int _highlightedIndex;
_CPMenuWindow _menuWindow;
}
diff --git a/AppKit/CPMenu/_CPMenuManager.j b/AppKit/CPMenu/_CPMenuManager.j
index e022f8ddc..18d1021e1 100644
--- a/AppKit/CPMenu/_CPMenuManager.j
+++ b/AppKit/CPMenu/_CPMenuManager.j
@@ -96,7 +96,7 @@ var SharedMenuManager = nil;
// Close Menu Event.
if (type === CPAppKitDefined)
- return [self completeTracking]
+ return [self completeTracking];
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES];
diff --git a/AppKit/CPMenuItem/CPMenuItem.j b/AppKit/CPMenuItem/CPMenuItem.j
index fb352ed69..0f173c7da 100644
--- a/AppKit/CPMenuItem/CPMenuItem.j
+++ b/AppKit/CPMenuItem/CPMenuItem.j
@@ -591,7 +591,8 @@ CPControlKeyMask
return @"";
var string = _keyEquivalent.toUpperCase(),
- needsShift = _keyEquivalentModifierMask & CPShiftKeyMask || string === _keyEquivalent;
+ needsShift = _keyEquivalentModifierMask & CPShiftKeyMask ||
+ (string === _keyEquivalent && _keyEquivalent.toLowerCase() !== _keyEquivalent.toUpperCase());
if (CPBrowserIsOperatingSystem(CPMacOperatingSystem))
{
@@ -781,7 +782,48 @@ CPControlKeyMask
return [[self menu] highlightedItem] == self;
}
-//
+#pragma mark CPObject Overrides
+
+/*!
+ Returns a copy of the item. The copy does not belong If the item has a submenu, it is NOT copied.
+*/
+- (id)copy
+{
+ var item = [[CPMenuItem alloc] init];
+
+ // No point in going through accessors and doing lots of unnecessary state checking/updating
+ item._isSeparator = _isSeparator;
+
+ [item setTitle:_title];
+ [item setFont:_font];
+ [item setTarget:_target];
+ [item setAction:_action];
+ [item setEnabled:_isEnabled];
+ [item setHidden:_isHidden]
+ [item setTag:_tag];
+ [item setState:_state];
+ [item setImage:_image];
+ [item setAlternateImage:_alternateImage];
+ [item setOnStateImage:_onStateImage];
+ [item setOffStateImage:_offStateImage];
+ [item setMixedStateImage:_mixedStateImage];
+ [item setKeyEquivalent:_keyEquivalent];
+ [item setKeyEquivalentModifierMask:_keyEquivalentModifierMask];
+ [item setMnemonicLocation:_mnemonicLocation];
+ [item setAlternate:_isAlternate];
+ [item setIndentationLevel:_indentationLevel];
+ [item setToolTip:_toolTip];
+ [item setRepresentedObject:_representedObject];
+
+ return item;
+}
+
+- (id)mutableCopy
+{
+ return [self copy];
+}
+
+#pragma mark Internal
/*
@ignore
@@ -860,7 +902,6 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
_isHidden = DEFAULT_VALUE(CPMenuItemIsHiddenKey, NO);
_tag = DEFAULT_VALUE(CPMenuItemTagKey, 0);
_state = DEFAULT_VALUE(CPMenuItemStateKey, CPOffState);
-// int _state;
_image = DEFAULT_VALUE(CPMenuItemImageKey, nil);
_alternateImage = DEFAULT_VALUE(CPMenuItemAlternateImageKey, nil);
diff --git a/AppKit/CPMenuItem/_CPMenuItemMenuBarView.j b/AppKit/CPMenuItem/_CPMenuItemMenuBarView.j
index 8393dedd4..4490c669e 100644
--- a/AppKit/CPMenuItem/_CPMenuItemMenuBarView.j
+++ b/AppKit/CPMenuItem/_CPMenuItemMenuBarView.j
@@ -62,6 +62,7 @@ var SelectionColor = nil,
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(HORIZONTAL_MARGIN, 0.0, 0.0, 0.0)];
[_imageAndTextView setImagePosition:CPImageLeft];
+ [_imageAndTextView setImageOffset:3.0];
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
[_imageAndTextView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
diff --git a/AppKit/CPMenuItem/_CPMenuItemStandardView.j b/AppKit/CPMenuItem/_CPMenuItemStandardView.j
index db15e5417..f41a42150 100644
--- a/AppKit/CPMenuItem/_CPMenuItemStandardView.j
+++ b/AppKit/CPMenuItem/_CPMenuItemStandardView.j
@@ -39,7 +39,7 @@ var SUBMENU_INDICATOR_COLOR = nil,
SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
- _CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0]
+ _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];
@@ -257,10 +257,13 @@ var SUBMENU_INDICATOR_COLOR = nil,
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
}
- if (shouldHighlight)
- [_stateView setImage:_CPMenuItemDefaultStateHighlightedImages[[_menuItem state]] || nil];
- else
- [_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
+ if ([[_menuItem menu] showsStateColumn])
+ {
+ if (shouldHighlight)
+ [_stateView setImage:_CPMenuItemDefaultStateHighlightedImages[[_menuItem state]] || nil];
+ else
+ [_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
+ }
}
@end
diff --git a/AppKit/CPMenuItem/_CPMenuItemView.j b/AppKit/CPMenuItem/_CPMenuItemView.j
index d4ef798f0..7b5438a17 100644
--- a/AppKit/CPMenuItem/_CPMenuItemView.j
+++ b/AppKit/CPMenuItem/_CPMenuItemView.j
@@ -43,7 +43,7 @@ var _CPMenuItemSelectionColor = nil,
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]
+ _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];
diff --git a/AppKit/CPObjectController.j b/AppKit/CPObjectController.j
index 65815caf2..fe12d1224 100644
--- a/AppKit/CPObjectController.j
+++ b/AppKit/CPObjectController.j
@@ -51,15 +51,20 @@
return [CPSet setWithObjects:"editable", "selection"];
}
+- (id)init
+{
+ return [self initWithContent:nil];
+}
+
- (id)initWithContent:(id)aContent
{
- self = [super init];
-
- if (self)
+ if (self = [super init])
{
[self setContent:aContent];
[self setEditable:YES];
[self setObjectClass:[CPMutableDictionary class]];
+
+ _observedKeys = [[CPCountedSet alloc] init];
}
return self;
@@ -452,7 +457,12 @@ var CPObjectControllerObjectClassNameKey = @"CPObjectControllerOb
[self didChangeValueForKey:keyPath];
}
- [self removeObjectAtIndex:anIndex];
+ [super removeObjectAtIndex:anIndex];
+}
+
+- (_CPObservableArray)objectsAtIndexes:(CPIndexSet)theIndexes
+{
+ return [_CPObservableArray arrayWithArray:[super objectsAtIndexes:theIndexes]];
}
- (void)addObject:(id)anObject
diff --git a/AppKit/CPOutlineView.j b/AppKit/CPOutlineView.j
index 06812521a..f50b63597 100644
--- a/AppKit/CPOutlineView.j
+++ b/AppKit/CPOutlineView.j
@@ -624,7 +624,7 @@ CPOutlineViewDropOnItemIndex = -1;
if (_dropItem)
{
[_dropOperationFeedbackView blink];
- [CPTimer scheduledTimerWithTimeInterval:.3 callback:objj_msgSend(self, "expandItem:", _dropItem) repeats:NO]; //[self expandItem:_dropItem];
+ [CPTimer scheduledTimerWithTimeInterval:.3 callback:objj_msgSend(self, "expandItem:", _dropItem) repeats:NO];
}
}
@@ -645,10 +645,18 @@ CPOutlineViewDropOnItemIndex = -1;
_shouldRetargetChildIndex = YES;
// set CPTableView's _retargetedDropRow based on retargetedItem and retargetedChildIndex
- var retargetedItemInfo = (_retargetedItem !== nil) ? _itemInfosForItems[[_retargetedItem UID]] : _rootItemInfo,
- retargetedChildItem = (_retargedChildIndex !== CPOutlineViewDropOnItemIndex) ? retargetedItemInfo.children[_retargedChildIndex] : _retargetedItem;
+ var retargetedItemInfo = (_retargetedItem !== nil) ? _itemInfosForItems[[_retargetedItem UID]] : _rootItemInfo;
- _retargetedDropRow = [self rowForItem:retargetedChildItem];
+ if (_retargedChildIndex === [retargetedItemInfo.children count])
+ {
+ var retargetedChildItem = [retargetedItemInfo.children lastObject];
+ _retargetedDropRow = [self rowForItem:retargetedChildItem] + 1;
+ }
+ else
+ {
+ var retargetedChildItem = (_retargedChildIndex !== CPOutlineViewDropOnItemIndex) ? retargetedItemInfo.children[_retargedChildIndex] : _retargetedItem;
+ _retargetedDropRow = [self rowForItem:retargetedChildItem];
+ }
}
- (void)_draggingEnded
@@ -1102,7 +1110,7 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt
_outlineView._shouldRetargetChildIndex = NO;
var location = [_outlineView convertPoint:[theInfo draggingLocation] fromView:nil],
- parentItem = [self _parentItemForDropOperation:theOperation row:theRow offset:location];
+ parentItem = [self _parentItemForDropOperation:theOperation row:theRow offset:location],
childIndex = [self _childIndexForDropOperation:theOperation row:theRow offset:location];
return [_outlineView._outlineViewDataSource outlineView:_outlineView validateDrop:theInfo proposedItem:parentItem proposedChildIndex:childIndex];
@@ -1247,11 +1255,82 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt
CGContextAddLineToPoint(context, 0.0, 0.0);
CGContextClosePath(context);
- var isHighlighted = [self hasThemeState:CPThemeStateHighlighted];
- var color = [self hasThemeState:CPThemeStateSelected] ? (isHighlighted ? [CPColor lightGrayColor] : [CPColor whiteColor]) : (isHighlighted ? [CPColor blackColor] : [CPColor grayColor]);
-
- CGContextSetFillColor(context, color);
+ CGContextSetFillColor(context,
+ colorForDisclosureTriangle([self hasThemeState:CPThemeStateSelected],
+ [self hasThemeState:CPThemeStateHighlighted]));
CGContextFillPath(context);
+
+
+ CGContextBeginPath(context);
+ CGContextMoveToPoint(context, 0.0, 0.0);
+ if(_angle === 0.0) {
+ CGContextAddLineToPoint(context, 4.5, 8.0);
+ CGContextAddLineToPoint(context, 9.0, 0.0);
+ } else {
+ CGContextAddLineToPoint(context, 4.5, 8.0);
+ }
+ CGContextSetStrokeColor(context, [CPColor colorWithCalibratedWhite:1.0 alpha: 0.8]);
+ CGContextStrokePath(context);
}
@end
+
+
+var CPOutlineViewIndentationPerLevelKey = @"CPOutlineViewIndentationPerLevelKey",
+ CPOutlineViewOutlineTableColumnKey = @"CPOutlineViewOutlineTableColumnKey",
+ CPOutlineViewDataSourceKey = @"CPOutlineViewDataSourceKey",
+ CPOutlineViewDelegateKey = @"CPOutlineViewDelegateKey";
+
+@implementation CPOutlineView (CPCoding)
+
+- (id)initWithCoder:(CPCoder)aCoder
+{
+ self = [super initWithCoder:aCoder];
+
+ if (self)
+ {
+ // The root item has weight "0", thus represents the weight solely of its descendants.
+ _rootItemInfo = { isExpanded:YES, isExpandable:NO, level:-1, row:-1, children:[], weight:0 };
+
+ _itemsForRows = [];
+ _itemInfosForItems = { };
+ _disclosureControlsForRows = [];
+
+ [self setIndentationMarkerFollowsDataView:YES];
+ [self setDisclosureControlPrototype:[[CPDisclosureButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 10.0)]];
+
+ _outlineTableColumn = [aCoder decodeObjectForKey:CPOutlineViewOutlineTableColumnKey];
+ _indentationPerLevel = [aCoder decodeFloatForKey:CPOutlineViewIndentationPerLevelKey];
+
+ _outlineViewDataSource = [aCoder decodeObjectForKey:CPOutlineViewDataSourceKey];
+ _outlineViewDelegate = [aCoder decodeObjectForKey:CPOutlineViewDelegateKey];
+
+ [super setDataSource:[[_CPOutlineViewTableViewDataSource alloc] initWithOutlineView:self]];
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(CPCoder)aCoder
+{
+ [super encodeWithCoder:aCoder];
+
+ [aCoder encodeObject:_outlineTableColumn forKey:CPOutlineViewOutlineTableColumnKey];
+ [aCoder encodeFloat:_indentationPerLevel forKey:CPOutlineViewIndentationPerLevelKey];
+
+ [aCoder encodeObject:_outlineViewDataSource forKey:CPOutlineViewDataSourceKey];
+ [aCoder encodeObject:_outlineViewDelegate forKey:CPOutlineViewDelegateKey];
+}
+
+@end
+
+
+var colorForDisclosureTriangle = function(isSelected, isHighlighted) {
+ return isSelected
+ ? (isHighlighted
+ ? [CPColor colorWithCalibratedWhite:0.9 alpha: 1.0]
+ : [CPColor colorWithCalibratedWhite:1.0 alpha: 1.0])
+ : (isHighlighted
+ ? [CPColor colorWithCalibratedWhite:0.4 alpha: 1.0]
+ : [CPColor colorWithCalibratedWhite:0.5 alpha: 1.0]);
+}
diff --git a/AppKit/CPPopUpButton.j b/AppKit/CPPopUpButton.j
index b5ac0e1e1..6072501dd 100644
--- a/AppKit/CPPopUpButton.j
+++ b/AppKit/CPPopUpButton.j
@@ -662,6 +662,9 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
location = CGPointMake(CGRectGetMinX(contentRect) - standardLeftMargin, 0.0);
minimumWidth += standardLeftMargin;
+
+ // To ensure the selected item is highlighted correctly, unset the highlighted item
+ [menu _highlightItemAtIndex:CPNotFound];
}
[menu setMinimumWidth:minimumWidth];
diff --git a/AppKit/CPRadio.j b/AppKit/CPRadio.j
index 551f0d8e4..773c317c7 100644
--- a/AppKit/CPRadio.j
+++ b/AppKit/CPRadio.j
@@ -27,26 +27,26 @@
/*!
@ingroup appkit
-
+
from this mailing list thread:
- http://groups.google.com/group/objectivej/browse_thread/thread/7c41cbd9cbee9ea3
-
+ http://groups.google.com/group/objectivej/browse_thread/thread/7c41cbd9cbee9ea3
+
-----------------------------------
-
+
Creating a checkbox is easy enough:
-
+
checkbox = [[CPCheckBox alloc] initWithFrame:aFrame];
-
+
That's basically all there is to it. Radio buttons are very similar,
the key difference is the introduction of a new class CPRadioGroup,
which defines which radio buttons are part of the same group:
-
+
[myRadioButton setRadioGroup:aRadioGroup];
-
+
Every radio button receives a unique radio group by default (so if you
do nothing further, they will all behave independently), but you can
use an existing radio button's group with other buttons as so:
-
+
button1 = [[CPRadio alloc] initWithFrame:aFrame];
...
button2 = [[CPRadio alloc] initWithFrame:aFrame radioGroup:[button1
@@ -55,13 +55,16 @@
button3 = [[CPRadio alloc] initWithFrame:aFrame radioGroup:[button1
radioGroup]];
...etc...
-
+
Here, all the radio buttons will act "together". [[button1 radioGroup]
allRadios] returns every button that's part of this group, and
[[button1 radioGroup] selectedRadio] returns the currently selected
option.
*/
+
+CPRadioImageOffset = 4.0;
+
@implementation CPRadio : CPButton
{
CPRadioGroup _radioGroup;
@@ -95,7 +98,7 @@
- (id)initWithFrame:(CGRect)aFrame radioGroup:(CPRadioGroup)aRadioGroup
{
self = [super initWithFrame:aFrame];
-
+
if (self)
{
[self setRadioGroup:aRadioGroup];
@@ -109,8 +112,8 @@
[self setBordered:YES];
}
-
- return self;
+
+ return self;
}
- (id)initWithFrame:(CGRect)aFrame
@@ -141,7 +144,7 @@
- (void)setObjectValue:(id)aValue
{
[super setObjectValue:aValue];
-
+
if ([self state] === CPOnState)
[_radioGroup _setSelectedRadio:self];
}
diff --git a/AppKit/CPResponder.j b/AppKit/CPResponder.j
index 746e4e992..08b27616e 100644
--- a/AppKit/CPResponder.j
+++ b/AppKit/CPResponder.j
@@ -22,7 +22,6 @@
@import
-
CPDeleteKeyCode = 8;
CPTabKeyCode = 9;
CPReturnKeyCode = 13;
@@ -39,7 +38,7 @@ CPDeleteForwardKeyCode = 46;
/*!
@ingroup appkit
@class CPResponder
-
+
Subclasses of CPResonder can be part of the responder chain.
*/
@implementation CPResponder : CPObject
@@ -105,42 +104,24 @@ CPDeleteForwardKeyCode = 46;
for (; index < count; ++index)
{
- var event = events[index];
+ var event = events[index],
+ modifierFlags = [event modifierFlags],
+ character = [event charactersIgnoringModifiers],
+ selectorNames = [CPKeyBinding selectorsForKey:character modifierFlags:modifierFlags];
- switch([event keyCode])
+ if (selectorNames)
{
- case CPPageUpKeyCode: [self doCommandBySelector:@selector(pageUp:)];
- break;
- case CPPageDownKeyCode: [self doCommandBySelector:@selector(pageDown:)];
- break;
- case CPLeftArrowKeyCode: [self doCommandBySelector:@selector(moveLeft:)];
- break;
- case CPRightArrowKeyCode: [self doCommandBySelector:@selector(moveRight:)];
- break;
- case CPUpArrowKeyCode: [self doCommandBySelector:@selector(moveUp:)];
- break;
- case CPDownArrowKeyCode: [self doCommandBySelector:@selector(moveDown:)];
- break;
- case CPDeleteKeyCode: [self doCommandBySelector:@selector(deleteBackward:)];
- break;
- case CPReturnKeyCode:
- case 3: [self doCommandBySelector:@selector(insertLineBreak:)];
- break;
-
- case CPEscapeKeyCode: [self doCommandBySelector:@selector(cancel:)];
- break;
+ for (var s = 0, scount = selectorNames.length; s < scount; s++)
+ {
+ var selector = selectorNames[s];
+ if (!selector)
+ continue;
- case CPTabKeyCode: var shift = [event modifierFlags] & CPShiftKeyMask;
-
- if (!shift)
- [self doCommandBySelector:@selector(insertTab:)];
- else
- [self doCommandBySelector:@selector(insertBackTab:)];
-
- break;
-
- default: [self insertText:[event characters]];
+ [self doCommandBySelector:CPSelectorFromString(selector)];
+ }
}
+ else if (!(modifierFlags & (CPCommandKeyMask | CPControlKeyMask)) && [self respondsToSelector:@selector(insertText:)])
+ [self insertText:[event characters]];
}
}
@@ -240,6 +221,15 @@ CPDeleteForwardKeyCode = 46;
[_nextResponder performSelector:_cmd withObject:anEvent];
}
+/*!
+ Notifies the receiver that the user has pressed or released a modifier key (Shift, Control, and so on).
+ @param anEvent information about the key press
+*/
+- (void)flagsChanged:(CPEvent)anEvent
+{
+ [_nextResponder performSelector:_cmd withObject:anEvent];
+}
+
/*
FIXME This description is bad.
Based on \c anEvent, the receiver should simulate the event.
@@ -316,7 +306,7 @@ CPDeleteForwardKeyCode = 46;
if([self respondsToSelector:aSelector])
{
[self performSelector:aSelector withObject:anObject];
-
+
return YES;
}
@@ -367,10 +357,10 @@ var CPResponderNextResponderKey = @"CPResponderNextResponderKey";
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super init];
-
+
if (self)
_nextResponder = [aCoder decodeObjectForKey:CPResponderNextResponderKey];
-
+
return self;
}
diff --git a/AppKit/CPScrollView.j b/AppKit/CPScrollView.j
index 051195b66..9aad1b8fd 100644
--- a/AppKit/CPScrollView.j
+++ b/AppKit/CPScrollView.j
@@ -37,23 +37,39 @@
*/
@implementation CPScrollView : CPView
{
- CPClipView _contentView;
- CPClipView _headerClipView;
- CPView _cornerView;
+ CPClipView _contentView;
+ CPClipView _headerClipView;
+ CPView _cornerView;
+ CPView _bottomCornerView;
- BOOL _hasVerticalScroller;
- BOOL _hasHorizontalScroller;
- BOOL _autohidesScrollers;
+ BOOL _hasVerticalScroller;
+ BOOL _hasHorizontalScroller;
+ BOOL _autohidesScrollers;
- CPScroller _verticalScroller;
- CPScroller _horizontalScroller;
+ CPScroller _verticalScroller;
+ CPScroller _horizontalScroller;
- int _recursionCount;
+ int _recursionCount;
- float _verticalLineScroll;
- float _verticalPageScroll;
- float _horizontalLineScroll;
- float _horizontalPageScroll;
+ float _verticalLineScroll;
+ float _verticalPageScroll;
+ float _horizontalLineScroll;
+ float _horizontalPageScroll;
+
+ CPBorderType _borderType;
+}
+
++ (CPString)themeClass
+{
+ return @"scrollview"
+}
+
++ (CPDictionary)themeAttributes
+{
+ return [CPDictionary dictionaryWithJSObject:{
+ @"bottom-corner-color": [CPColor whiteColor],
+ @"border-color": [CPColor blackColor]
+ }];
}
- (id)initWithFrame:(CGRect)aFrame
@@ -68,14 +84,18 @@
_horizontalLineScroll = 10.0;
_horizontalPageScroll = 10.0;
- _contentView = [[CPClipView alloc] initWithFrame:[self bounds]];
+ _borderType = CPNoBorder;
+
+ _contentView = [[CPClipView alloc] initWithFrame:[self _insetBounds]];
[self addSubview:_contentView];
_headerClipView = [[CPClipView alloc] init];
-
[self addSubview:_headerClipView];
+ _bottomCornerView = [[CPView alloc] init];
+ [self addSubview:_bottomCornerView];
+
[self setHasVerticalScroller:YES];
[self setHasHorizontalScroller:YES];
}
@@ -83,6 +103,65 @@
return self;
}
+// Calculating Layout
+
++ (CGSize)contentSizeForFrameSize:(CGSize)frameSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
+{
+ var bounds = [self _insetBounds:_CGRectMake(0.0, 0.0, frameSize.width, frameSize.height) borderType:borderType],
+ scrollerWidth = [CPScroller scrollerWidth];
+
+ if (hFlag)
+ bounds.size.height -= scrollerWidth;
+
+ if (vFlag)
+ bounds.size.width -= scrollerWidth;
+
+ return bounds.size;
+}
+
++ (CGSize)frameSizeForContentSize:(CGSize)contentSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
+{
+ var bounds = [self _insetBounds:_CGRectMake(0.0, 0.0, contentSize.width, contentSize.height) borderType:borderType],
+ widthInset = contentSize.width - bounds.size.width,
+ heightInset = contentSize.height - bounds.size.height,
+ frameSize = _CGSizeMake(contentSize.width + widthInset, contentSize.height + heightInset),
+ scrollerWidth = [CPScroller scrollerWidth];
+
+ if (hFlag)
+ frameSize.height -= scrollerWidth;
+
+ if (vFlag)
+ frameSize.width -= scrollerWidth;
+
+ return frameSize;
+}
+
++ (CGRect)_insetBounds:(CGRect)bounds borderType:(CPBorderType)borderType
+{
+ switch (borderType)
+ {
+ case CPLineBorder:
+ case CPBezelBorder:
+ return _CGRectInset(bounds, 1.0, 1.0);
+
+ case CPGrooveBorder:
+ bounds = _CGRectInset(bounds, 2.0, 2.0);
+ ++bounds.origin.y;
+ --bounds.size.height;
+
+ return bounds;
+
+ case CPNoBorder:
+ default:
+ return bounds;
+ }
+}
+
+- (CGRect)_insetBounds
+{
+ return [[self class] _insetBounds:[self bounds] borderType:_borderType];
+}
+
// Determining component sizes
/*!
Returns the size of the scroll view's content view.
@@ -176,7 +255,7 @@
// [_horizontalScroller setEnabled:NO];
}
- [_contentView setFrame:[self bounds]];
+ [_contentView setFrame:[self _insetBounds]];
[_headerClipView setFrame:_CGRectMakeZero()];
--_recursionCount;
@@ -185,7 +264,7 @@
}
var documentFrame = [documentView frame], // the size of the whole document
- contentFrame = [self bounds], // assume it takes up the entire size of the scrollview (no scrollers)
+ contentFrame = [self _insetBounds], // assume it takes up the entire size of the scrollview (no scrollers)
headerClipViewFrame = [self _headerClipViewFrame],
headerClipViewHeight = _CGRectGetHeight(headerClipViewFrame);
@@ -235,11 +314,10 @@
if (shouldShowVerticalScroller)
{
- var verticalScrollerY = MAX(_CGRectGetHeight([self _cornerViewFrame]), headerClipViewHeight),
- verticalScrollerHeight = _CGRectGetHeight([self bounds]) - verticalScrollerY;
+ var verticalScrollerY =
+ MAX(_CGRectGetMinY(contentFrame), MAX(_CGRectGetMaxY([self _cornerViewFrame]), _CGRectGetMaxY(headerClipViewFrame)));
- if (shouldShowHorizontalScroller)
- verticalScrollerHeight -= horizontalScrollerHeight;
+ var verticalScrollerHeight = _CGRectGetMaxY(contentFrame) - verticalScrollerY;
[_verticalScroller setFloatValue:(difference.height <= 0.0) ? 0.0 : scrollPoint.y / difference.height];
[_verticalScroller setKnobProportion:_CGRectGetHeight(contentFrame) / _CGRectGetHeight(documentFrame)];
@@ -255,7 +333,7 @@
{
[_horizontalScroller setFloatValue:(difference.width <= 0.0) ? 0.0 : scrollPoint.x / difference.width];
[_horizontalScroller setKnobProportion:_CGRectGetWidth(contentFrame) / _CGRectGetWidth(documentFrame)];
- [_horizontalScroller setFrame:_CGRectMake(0.0, _CGRectGetMaxY(contentFrame), _CGRectGetWidth(contentFrame), horizontalScrollerHeight)];
+ [_horizontalScroller setFrame:_CGRectMake(_CGRectGetMinX(contentFrame), _CGRectGetMaxY(contentFrame), _CGRectGetWidth(contentFrame), horizontalScrollerHeight)];
}
else if (wasShowingHorizontalScroller)
{
@@ -267,9 +345,36 @@
[_headerClipView setFrame:headerClipViewFrame];
[_cornerView setFrame:[self _cornerViewFrame]];
+ [[self bottomCornerView] setFrame:[self _bottomCornerViewFrame]];
+ [[self bottomCornerView] setBackgroundColor:[self currentValueForThemeAttribute:@"bottom-corner-color"]];
+
--_recursionCount;
}
+// Managing Graphics Attributes
+
+/*!
+ Sets the type of border to be drawn around the view.
+*/
+- (void)setBorderType:(CPBorderType)borderType
+{
+ if (_borderType == borderType)
+ return;
+
+ _borderType = borderType;
+
+ [self reflectScrolledClipView:_contentView];
+ [self setNeedsDisplay:YES];
+}
+
+/*!
+ Returns the border type drawn around the view.
+*/
+- (CPBorderType)borderType
+{
+ return _borderType;
+}
+
// Managing Scrollers
/*!
Sets the scroll view's horizontal scroller.
@@ -316,8 +421,10 @@
if (_hasHorizontalScroller && !_horizontalScroller)
{
- [self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, MAX(_CGRectGetWidth([self bounds]), [CPScroller scrollerWidth]+1), [CPScroller scrollerWidth])]];
- [[self horizontalScroller] setFrameSize:CGSizeMake(_CGRectGetWidth([self bounds]), [CPScroller scrollerWidth])];
+ var bounds = [self _insetBounds];
+
+ [self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, MAX(_CGRectGetWidth(bounds), [CPScroller scrollerWidth] + 1), [CPScroller scrollerWidth])]];
+ [[self horizontalScroller] setFrameSize:CGSizeMake(_CGRectGetWidth(bounds), [CPScroller scrollerWidth])];
}
[self reflectScrolledClipView:_contentView];
@@ -377,8 +484,10 @@
if (_hasVerticalScroller && !_verticalScroller)
{
- [self setVerticalScroller:[[CPScroller alloc] initWithFrame:_CGRectMake(0.0, 0.0, [CPScroller scrollerWidth], MAX(_CGRectGetHeight([self bounds]), [CPScroller scrollerWidth]+1))]];
- [[self verticalScroller] setFrameSize:CGSizeMake([CPScroller scrollerWidth], _CGRectGetHeight([self bounds]))];
+ var bounds = [self _insetBounds];
+
+ [self setVerticalScroller:[[CPScroller alloc] initWithFrame:_CGRectMake(0.0, 0.0, [CPScroller scrollerWidth], MAX(_CGRectGetHeight(bounds), [CPScroller scrollerWidth] + 1))]];
+ [[self verticalScroller] setFrameSize:CGSizeMake([CPScroller scrollerWidth], _CGRectGetHeight(bounds))];
}
[self reflectScrolledClipView:_contentView];
@@ -453,11 +562,11 @@
if (!_cornerView)
return _CGRectMakeZero();
- var bounds = [self bounds],
+ var bounds = [self _insetBounds],
frame = [_cornerView frame];
frame.origin.x = _CGRectGetMaxX(bounds) - _CGRectGetWidth(frame);
- frame.origin.y = 0;
+ frame.origin.y = _CGRectGetMinY(bounds);
return frame;
}
@@ -469,7 +578,7 @@
if (!headerView)
return _CGRectMakeZero();
- var frame = [self bounds];
+ var frame = [self _insetBounds];
frame.size.height = _CGRectGetHeight([headerView frame]);
frame.size.width -= _CGRectGetWidth([self _cornerViewFrame]);
@@ -477,6 +586,42 @@
return frame;
}
+- (CGRect)_bottomCornerViewFrame
+{
+ if ([[self horizontalScroller] isHidden] || [[self verticalScroller] isHidden])
+ return CGRectMakeZero();
+
+ var verticalFrame = [[self verticalScroller] frame],
+ bottomCornerFrame = CGRectMakeZero();
+
+ bottomCornerFrame.origin.x = CGRectGetMinX(verticalFrame);
+ bottomCornerFrame.origin.y = CGRectGetMaxY(verticalFrame);
+ bottomCornerFrame.size.width = [CPScroller scrollerWidth];
+ bottomCornerFrame.size.height = [CPScroller scrollerWidth];
+
+ return bottomCornerFrame;
+}
+
+- (void)setBottomCornerView:(CPView)aBottomCornerView
+{
+ if (_bottomCornerView === aBottomCornerView)
+ return;
+
+ [_bottomCornerView removeFromSuperview];
+
+ [aBottomCornerView setFrame:[self _bottomCornerViewFrame]];
+ [self addSubview:aBottomCornerView];
+
+ _bottomCornerView = aBottomCornerView;
+
+ [self _updateCornerAndHeaderView];
+}
+
+- (CPView)bottomCornerView
+{
+ return _bottomCornerView;
+}
+
/* @ignore */
- (void)_verticalScrollerDidScroll:(CPScroller)aScroller
{
@@ -662,14 +807,138 @@
return _verticalPageScroll;
}
+// CPView Overrides
+
+- (void)drawRect:(CPRect)aRect
+{
+ [super drawRect:aRect];
+
+ if (_borderType == CPNoBorder)
+ return;
+
+ var strokeRect = [self bounds],
+ context = [[CPGraphicsContext currentContext] graphicsPort];
+
+ CGContextSetLineWidth(context, 1);
+
+ switch (_borderType)
+ {
+ case CPLineBorder:
+ CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"border-color"]);
+ CGContextStrokeRect(context, _CGRectInset(strokeRect, 0.5, 0.5));
+ break;
+
+ case CPBezelBorder:
+ [self _drawGrayBezelInContext:context bounds:strokeRect];
+ break;
+
+ case CPGrooveBorder:
+ [self _drawGrooveInContext:context bounds:strokeRect];
+ break;
+
+ default:
+ break;
+ }
+}
+
+- (void)_drawGrayBezelInContext:(CGContext)context bounds:(CGRect)aRect
+{
+ CGContextBeginPath(context);
+ CGContextSetStrokeColor(context, [CPColor colorWithWhite:142.0/255.0 alpha:1.0]);
+
+ var y = _CGRectGetMinY(aRect) + 0.5;
+
+ CGContextMoveToPoint(context, _CGRectGetMinX(aRect), y);
+ CGContextAddLineToPoint(context, _CGRectGetMinX(aRect) + 1.0, y);
+ CGContextStrokePath(context);
+
+ CGContextBeginPath(context);
+ CGContextSetStrokeColor(context, [CPColor colorWithWhite:192.0/255.0 alpha:1.0]);
+ CGContextMoveToPoint(context, _CGRectGetMinX(aRect) + 1.0, y);
+ CGContextAddLineToPoint(context, _CGRectGetMaxX(aRect) - 1.0, y);
+ CGContextStrokePath(context);
+
+ CGContextBeginPath(context);
+ CGContextSetStrokeColor(context, [CPColor colorWithWhite:142.0/255.0 alpha:1.0]);
+ CGContextMoveToPoint(context, _CGRectGetMaxX(aRect) - 1.0, y);
+ CGContextAddLineToPoint(context, _CGRectGetMaxX(aRect), y);
+ CGContextStrokePath(context);
+
+ CGContextBeginPath(context);
+ CGContextSetStrokeColor(context, [CPColor colorWithWhite:190.0/255.0 alpha:1.0]);
+
+ var x = _CGRectGetMaxX(aRect) - 0.5;
+
+ CGContextMoveToPoint(context, x, _CGRectGetMinY(aRect) + 1.0);
+ CGContextAddLineToPoint(context, x, _CGRectGetMaxY(aRect));
+
+ CGContextMoveToPoint(context, x - 0.5, _CGRectGetMaxY(aRect) - 0.5);
+ CGContextAddLineToPoint(context, _CGRectGetMinX(aRect), _CGRectGetMaxY(aRect) - 0.5);
+
+ x = _CGRectGetMinX(aRect) + 0.5;
+
+ CGContextMoveToPoint(context, x, _CGRectGetMaxY(aRect));
+ CGContextAddLineToPoint(context, x, _CGRectGetMinY(aRect) + 1.0);
+
+ CGContextStrokePath(context);
+}
+
+- (void)_drawGrooveInContext:(CGContext)context bounds:(CGRect)aRect
+{
+ CGContextBeginPath(context);
+ CGContextSetStrokeColor(context, [CPColor colorWithWhite:159.0/255.0 alpha:1.0]);
+
+ var y = _CGRectGetMinY(aRect) + 0.5;
+
+ CGContextMoveToPoint(context, _CGRectGetMinX(aRect), y);
+ CGContextAddLineToPoint(context, _CGRectGetMaxX(aRect), y);
+
+ var x = _CGRectGetMaxX(aRect) - 1.5;
+
+ CGContextMoveToPoint(context, x, _CGRectGetMinY(aRect) + 2.0);
+ CGContextAddLineToPoint(context, x, _CGRectGetMaxY(aRect) - 1.0);
+
+ y = _CGRectGetMaxY(aRect) - 1.5;
+
+ CGContextMoveToPoint(context, _CGRectGetMaxX(aRect) - 1.0, y);
+ CGContextAddLineToPoint(context, _CGRectGetMinX(aRect) + 2.0, y);
+
+ x = _CGRectGetMinX(aRect) + 0.5;
+
+ CGContextMoveToPoint(context, x, _CGRectGetMaxY(aRect));
+ CGContextAddLineToPoint(context, x, _CGRectGetMinY(aRect));
+
+ CGContextStrokePath(context);
+
+ CGContextBeginPath(context);
+ CGContextSetStrokeColor(context, [CPColor whiteColor]);
+
+ var rect = _CGRectOffset(aRect, 1.0, 1.0);
+
+ rect.size.width -= 1.0;
+ rect.size.height -= 1.0;
+ CGContextStrokeRect(context, _CGRectInset(rect, 0.5, 0.5));
+
+ CGContextBeginPath(context);
+ CGContextSetStrokeColor(context, [CPColor colorWithWhite:192.0/255.0 alpha:1.0]);
+
+ y = _CGRectGetMinY(aRect) + 2.5;
+
+ CGContextMoveToPoint(context, _CGRectGetMinX(aRect) + 2.0, y);
+ CGContextAddLineToPoint(context, _CGRectGetMaxX(aRect) - 2.0, y);
+ CGContextStrokePath(context);
+}
+
+
+// CPResponder Overrides
+
/*!
Handles a scroll wheel event from the user.
@param anEvent the scroll wheel event
*/
- (void)scrollWheel:(CPEvent)anEvent
{
- [self _respondToScrollWheelEventWithDeltaX:[anEvent deltaX] * _horizontalLineScroll
- deltaY:[anEvent deltaY] * _verticalLineScroll];
+ [self _respondToScrollWheelEventWithDeltaX:[anEvent deltaX] deltaY:[anEvent deltaY]];
}
- (void)_respondToScrollWheelEventWithDeltaX:(float)deltaX deltaY:(float)deltaY
@@ -677,17 +946,15 @@
var documentFrame = [[self documentView] frame],
contentBounds = [_contentView bounds],
contentFrame = [_contentView frame],
- enclosingScrollView = [self enclosingScrollView],
- extraX = 0,
- extraY = 0;
+ enclosingScrollView = [self enclosingScrollView];
// We want integral bounds!
contentBounds.origin.x = ROUND(contentBounds.origin.x + deltaX);
contentBounds.origin.y = ROUND(contentBounds.origin.y + deltaY);
- var constrainedOrigin = [_contentView constrainScrollPoint:CGPointCreateCopy(contentBounds.origin)];
- extraX = ((contentBounds.origin.x - constrainedOrigin.x) / _horizontalLineScroll) * [enclosingScrollView horizontalLineScroll];
- extraY = ((contentBounds.origin.y - constrainedOrigin.y) / _verticalLineScroll) * [enclosingScrollView verticalLineScroll];
+ var constrainedOrigin = [_contentView constrainScrollPoint:CGPointCreateCopy(contentBounds.origin)],
+ extraX = contentBounds.origin.x - constrainedOrigin.x,
+ extraY = contentBounds.origin.y - constrainedOrigin.y;
[_contentView scrollToPoint:constrainedOrigin];
[_headerClipView scrollToPoint:CGPointMake(constrainedOrigin.x, 0.0)];
@@ -758,7 +1025,8 @@ var CPScrollViewContentViewKey = "CPScrollViewContentView",
CPScrollViewVScrollerKey = "CPScrollViewVScroller",
CPScrollViewHScrollerKey = "CPScrollViewHScroller",
CPScrollViewAutohidesScrollerKey = "CPScrollViewAutohidesScroller",
- CPScrollViewCornerViewKey = "CPScrollViewCornerViewKey";
+ CPScrollViewCornerViewKey = "CPScrollViewCornerViewKey",
+ CPScrollViewBorderTypeKey = "CPScrollViewBorderTypeKey";
@implementation CPScrollView (CPCoding)
@@ -781,6 +1049,9 @@ var CPScrollViewContentViewKey = "CPScrollViewContentView",
[self addSubview:_headerClipView];
}
+ _bottomCornerView = [[CPView alloc] init];
+ [self addSubview:_bottomCornerView];
+
_verticalScroller = [aCoder decodeObjectForKey:CPScrollViewVScrollerKey];
_horizontalScroller = [aCoder decodeObjectForKey:CPScrollViewHScrollerKey];
@@ -788,6 +1059,8 @@ var CPScrollViewContentViewKey = "CPScrollViewContentView",
_hasHorizontalScroller = [aCoder decodeBoolForKey:CPScrollViewHasHScrollerKey];
_autohidesScrollers = [aCoder decodeBoolForKey:CPScrollViewAutohidesScrollerKey];
+ _borderType = [aCoder decodeIntForKey:CPScrollViewBorderTypeKey];
+
_cornerView = [aCoder decodeObjectForKey:CPScrollViewCornerViewKey];
// Do to the anything goes nature of decoding, our subviews may not exist yet, so layout at the end of the run loop when we're sure everything is in a correct state.
@@ -817,6 +1090,8 @@ var CPScrollViewContentViewKey = "CPScrollViewContentView",
[aCoder encodeBool:_autohidesScrollers forKey:CPScrollViewAutohidesScrollerKey];
[aCoder encodeObject:_cornerView forKey:CPScrollViewCornerViewKey];
+
+ [aCoder encodeInt:_borderType forKey:CPScrollViewBorderTypeKey];
}
@end
diff --git a/AppKit/CPScroller.j b/AppKit/CPScroller.j
index 14e190507..85b75943c 100644
--- a/AppKit/CPScroller.j
+++ b/AppKit/CPScroller.j
@@ -41,9 +41,9 @@ CPNoScrollerParts = 0;
CPOnlyScrollerArrows = 1;
CPAllScrollerParts = 2;
-/*!
+/*!
@ingroup appkit
- @class CPScroller
+ @class CPScroller
*/
var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
@@ -79,17 +79,18 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
+ (id)themeAttributes
{
- return [CPDictionary dictionaryWithObjects:[ [CPNull null], [CPNull null], [CPNull null], [CPNull null],
- _CGSizeMakeZero(), _CGSizeMakeZero(), _CGInsetMakeZero(), _CGInsetMakeZero(), _CGSizeMakeZero()]
- forKeys:[ @"knob-slot-color",
- @"decrement-line-color",
- @"increment-line-color",
- @"knob-color",
- @"decrement-line-size",
- @"increment-line-size",
- @"track-inset",
- @"knob-inset",
- @"minimum-knob-length"]];
+ return [CPDictionary dictionaryWithJSObject:{
+ @"scroller-width": 15.0,
+ @"knob-slot-color": [CPColor lightGrayColor],
+ @"decrement-line-color": [CPNull null],
+ @"increment-line-color": [CPNull null],
+ @"knob-color": [CPColor grayColor],
+ @"decrement-line-size":_CGSizeMakeZero(),
+ @"increment-line-size":_CGSizeMakeZero(),
+ @"track-inset":_CGInsetMakeZero(),
+ @"knob-inset": _CGInsetMakeZero(),
+ @"minimum-knob-length":21.0
+ }]
}
@@ -98,7 +99,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
-
+
if (self)
{
_controlSize = CPRegularControlSize;
@@ -106,7 +107,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
[self setFloatValue:0.0];
[self setKnobProportion:1.0];
-
+
_hitPart = CPScrollerNoPart;
[self _calculateIsVertical];
@@ -121,7 +122,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
*/
+ (float)scrollerWidth
{
- return 15.0;//[self scrollerWidthForControlSize:CPRegularControlSize];
+ return [[[CPScroller alloc] init] currentValueForThemeAttribute:@"scroller-width"];
}
/*!
@@ -130,7 +131,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
*/
+ (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
{
- return 15.0;//_CPScrollerWidths[aControlSize];
+ return [self scrollerWidth];
}
/*!
@@ -180,10 +181,10 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
- (id)currentValueForThemeAttribute:(CPString)anAttributeName
{
var themeState = _themeState;
-
+
if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
themeState &= ~CPThemeStateHighlighted;
-
+
return [self valueForThemeAttribute:anAttributeName inState:themeState];
}
@@ -205,25 +206,25 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
- (CPScrollerPart)testPart:(CGPoint)aPoint
{
aPoint = [self convertPoint:aPoint fromView:nil];
-
- // The ordering of these tests is important. We check the knob and
+
+ // The ordering of these tests is important. We check the knob and
// page rects first since they may overlap with the arrows.
-
+
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
return CPScrollerKnob;
-
+
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementPage], aPoint))
return CPScrollerDecrementPage;
-
+
if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementPage], aPoint))
return CPScrollerIncrementPage;
-
+
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementLine], aPoint))
return CPScrollerDecrementLine;
-
+
if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementLine], aPoint))
return CPScrollerIncrementLine;
-
+
if (CGRectContainsPoint([self rectForPart:CPScrollerKnobSlot], aPoint))
return CPScrollerKnobSlot;
@@ -241,7 +242,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
if (_knobProportion === 1.0)
{
_usableParts = CPNoScrollerParts;
-
+
_partRects[CPScrollerDecrementPage] = CGRectMakeZero();
_partRects[CPScrollerKnob] = CGRectMakeZero();
_partRects[CPScrollerIncrementPage] = CGRectMakeZero();
@@ -250,7 +251,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
// In this case, the slot is the entirety of the scroller.
_partRects[CPScrollerKnobSlot] = CGRectMakeCopy(bounds);
-
+
return;
}
@@ -260,8 +261,8 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
var knobInset = [self currentValueForThemeAttribute:@"knob-inset"],
trackInset = [self currentValueForThemeAttribute:@"track-inset"],
width = _CGRectGetWidth(bounds),
- height = _CGRectGetHeight(bounds);
-
+ height = _CGRectGetHeight(bounds);
+
if ([self isVertical])
{
var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
@@ -280,10 +281,10 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
_partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, effectiveDecrementLineHeight, width - trackInset.left - trackInset.right, slotHeight);
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
_partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
-
+
if(height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom)
_partRects[CPScrollerKnob] = _CGRectMakeZero();
-
+
if(height < decrementLineSize.height + incrementLineSize.height - 2)
{
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
@@ -309,10 +310,10 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
_partRects[CPScrollerKnobSlot] = _CGRectMake(effectiveDecrementLineWidth, trackInset.top, slotWidth, height - trackInset.top - trackInset.bottom);
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
_partRects[CPScrollerIncrementLine] = _CGRectMake(width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height);
-
+
if(width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right)
_partRects[CPScrollerKnob] = _CGRectMakeZero();
-
+
if(width < decrementLineSize.width + incrementLineSize.width - 2)
{
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
@@ -358,9 +359,9 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
- (CPView)createViewForPart:(CPScrollerPart)aPart
{
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
-
+
[view setHitTests:NO];
-
+
return view;
}
@@ -374,7 +375,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[view setHitTests:NO];
-
+
return view;
}
@@ -388,12 +389,12 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
for (; index < count; ++index)
{
var part = PARTS_ARRANGEMENT[index];
-
+
if (index === 0)
view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowBelow relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index + 1]];
else
view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowAbove relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index - 1]];
-
+
if (view)
[view setBackgroundColor:[self currentValueForThemeAttribute:NAMES_FOR_PARTS[part] + "-color"]];
}
@@ -403,7 +404,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
Caches images for the scroll arrow and knob.
*/
- (void)drawParts
-{
+{
[self drawKnobSlot];
[self drawKnob];
[self drawArrow:CPScrollerDecrementArrow highlight:NO];
@@ -426,37 +427,37 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
- (void)trackKnob:(CPEvent)anEvent
{
var type = [anEvent type];
-
+
if (type === CPLeftMouseUp)
{
_hitPart = CPScrollerNoPart;
-
+
return;
}
-
+
if (type === CPLeftMouseDown)
{
_trackingFloatValue = [self floatValue];
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
}
-
+
else if (type === CPLeftMouseDragged)
{
var knobRect = [self rectForPart:CPScrollerKnob],
knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
remainder = ![self isVertical] ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect));
-
+
if (remainder <= 0)
[self setFloatValue:0.0];
else
{
- var location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
+ var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
delta = ![self isVertical] ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
[self setFloatValue:_trackingFloatValue + delta / remainder];
}
}
-
+
[CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
[self sendAction:[self action] to:[self target]];
@@ -474,26 +475,26 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
{
[self highlight:NO];
[CPEvent stopPeriodicEvents];
-
+
_hitPart = CPScrollerNoPart;
-
+
return;
}
-
+
if (type === CPLeftMouseDown)
{
_trackingPart = [self hitPart];
-
+
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
if ([anEvent modifierFlags] & CPAlternateKeyMask)
{
if (_trackingPart == CPScrollerDecrementLine)
_hitPart = CPScrollerDecrementPage;
-
+
else if (_trackingPart == CPScrollerIncrementLine)
_hitPart = CPScrollerIncrementPage;
-
+
else if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
{
var knobRect = [self rectForPart:CPScrollerKnob],
@@ -502,42 +503,42 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
remainder = (![self isVertical] ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
[self setFloatValue:((![self isVertical] ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
-
+
_hitPart = CPScrollerKnob;
-
- [self sendAction:[self action] to:[self target]];
-
+
+ [self sendAction:[self action] to:[self target]];
+
// Now just track the knob.
return [self trackKnob:anEvent];
}
}
-
+
[self highlight:YES];
[self sendAction:[self action] to:[self target]];
-
+
[CPEvent startPeriodicEventsAfterDelay:0.5 withPeriod:0.04];
}
-
+
else if (type === CPLeftMouseDragged)
{
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
-
+
if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
{
var hitPart = [self testPart:[anEvent locationInWindow]];
-
+
if (hitPart == CPScrollerDecrementPage || hitPart == CPScrollerIncrementPage)
{
_trackingPart = hitPart;
_hitPart = hitPart;
}
}
-
+
[self highlight:CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint)];
}
else if (type == CPPeriodic && CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint))
[self sendAction:[self action] to:[self target]];
-
+
[CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
}
@@ -569,15 +570,15 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
{
if (![self isEnabled])
return;
-
+
_hitPart = [self testPart:[anEvent locationInWindow]];
-
+
switch (_hitPart)
{
case CPScrollerKnob: return [self trackKnob:anEvent];
-
- case CPScrollerDecrementLine:
- case CPScrollerIncrementLine:
+
+ case CPScrollerDecrementLine:
+ case CPScrollerIncrementLine:
case CPScrollerDecrementPage:
case CPScrollerIncrementPage: return [self trackScrollButtons:anEvent];
}
@@ -603,19 +604,32 @@ var CPScrollerControlSizeKey = "CPScrollerControlSize",
_knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
_partRects = [];
-
+
_hitPart = CPScrollerNoPart;
[self _calculateIsVertical];
+
+ // Adjust the size of the scroller if the size from cib
+ // isn't equal to the scrollerWidth
+ var frame = [self frame],
+ scrollerWidth = [CPScroller scrollerWidth];
+
+ if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
+ frame.size.width = scrollerWidth;
+
+ if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
+ frame.size.height = scrollerWidth;
+
+ [self setFrame:frame];
}
-
+
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
-
+
[aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
[aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
}
diff --git a/AppKit/CPSearchField.j b/AppKit/CPSearchField.j
index f5fdea74a..1035110b9 100644
--- a/AppKit/CPSearchField.j
+++ b/AppKit/CPSearchField.j
@@ -22,18 +22,27 @@
@import "CPTextField.j"
+#include "CoreGraphics/CGGeometry.h"
#include "Platform/Platform.h"
CPSearchFieldRecentsTitleMenuItemTag = 1000;
CPSearchFieldRecentsMenuItemTag = 1001;
CPSearchFieldClearRecentsMenuItemTag = 1002;
CPSearchFieldNoRecentsMenuItemTag = 1003;
+CPSearchFieldSeparatorMenuItemTag = 1004;
var CPSearchFieldSearchImage = nil,
CPSearchFieldFindImage = nil,
CPSearchFieldCancelImage = nil,
CPSearchFieldCancelPressedImage = nil;
+var SEARCH_BUTTON_DEFAULT_WIDTH = 25.0,
+ CANCEL_BUTTON_DEFAULT_WIDTH = 22.0,
+ BUTTON_DEFAULT_HEIGHT = 22.0;
+
+var RECENT_SEARCH_PREFIX = @" ";
+
+
/*!
@ingroup appkit
@class CPSearchField
@@ -54,6 +63,7 @@ var CPSearchFieldSearchImage = nil,
int _maximumRecents;
BOOL _sendsWholeSearchString;
BOOL _sendsSearchStringImmediately;
+ BOOL _canResignFirstResponder;
CPTimer _partialStringTimer;
}
@@ -63,23 +73,22 @@ var CPSearchFieldSearchImage = nil,
return;
var bundle = [CPBundle bundleForClass:self];
- CPSearchFieldSearchImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldSearch.png"] size:CGSizeMake(25, 22)];
- CPSearchFieldFindImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldFind.png"] size:CGSizeMake(25, 22)];
- CPSearchFieldCancelImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancel.png"] size:CGSizeMake(22, 22)];
- CPSearchFieldCancelPressedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancelPressed.png"] size:CGSizeMake(22, 22)];
+ CPSearchFieldSearchImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldSearch.png"] size:_CGSizeMake(SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
+ CPSearchFieldFindImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldFind.png"] size:_CGSizeMake(SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
+ CPSearchFieldCancelImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancel.png"] size:_CGSizeMake(CANCEL_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
+ CPSearchFieldCancelPressedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancelPressed.png"] size:_CGSizeMake(CANCEL_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
- _recentSearches = [CPArray array];
_maximumRecents = 10;
_sendsWholeSearchString = NO;
_sendsSearchStringImmediately = NO;
_recentsAutosaveName = nil;
- [self _initWithFrame:frame];
+ [self _init];
#if PLATFORM(DOM)
_cancelButton._DOMElement.style.cursor = "default";
_searchButton._DOMElement.style.cursor = "default";
@@ -89,23 +98,28 @@ var CPSearchFieldSearchImage = nil,
return self;
}
-- (void)_initWithFrame:(CGRect)frame
+- (void)_init
{
+ _recentSearches = [CPArray array];
+
[self setBezeled:YES];
[self setBezelStyle:CPTextFieldRoundedBezel];
[self setBordered:YES];
[self setEditable:YES];
[self setDelegate:self];
+ [self setContinuous:YES];
- _cancelButton = [[CPButton alloc] initWithFrame:CGRectMake(frame.size.width - 27,(frame.size.height-22)/2,22,22)];
+ var bounds = [self bounds],
+ cancelButton = [[CPButton alloc] initWithFrame:[self cancelButtonRectForBounds:bounds]],
+ searchButton = [[CPButton alloc] initWithFrame:[self searchButtonRectForBounds:bounds]];
+
+ [self setCancelButton:cancelButton];
[self resetCancelButton];
- [_cancelButton setHidden:YES];
- [_cancelButton setAutoresizingMask:CPViewMinXMargin];
- [self addSubview:_cancelButton];
- _searchButton = [[CPButton alloc] initWithFrame:CGRectMake(5,(frame.size.height-25)/2,25,25)];
+ [self setSearchButton:searchButton];
[self resetSearchButton];
- [self addSubview:_searchButton];
+
+ _canResignFirstResponder = YES;
}
// Managing Buttons
@@ -115,7 +129,15 @@ var CPSearchFieldSearchImage = nil,
*/
- (void)setSearchButton:(CPButton)button
{
- _searchButton = button;
+ if (button != _searchButton)
+ {
+ [_searchButton removeFromSuperview];
+ _searchButton = button;
+
+ [_searchButton setFrame:[self searchButtonRectForBounds:[self bounds]]];
+ [_searchButton setAutoresizingMask:CPViewMaxXMargin];
+ [self addSubview:_searchButton];
+ }
}
/*!
@@ -133,30 +155,13 @@ var CPSearchFieldSearchImage = nil,
*/
- (void)resetSearchButton
{
- var searchButtonImage,
- action,
- target,
- button = [self searchButton];
-
- if (_searchMenuTemplate === nil)
- {
- searchButtonImage = CPSearchFieldSearchImage;
- action = @selector(_sendAction:);
- target = self;
- }
- else
- {
- searchButtonImage = CPSearchFieldFindImage;
- action = @selector(_showMenu:);
- target = self;
- }
+ var button = [self searchButton],
+ searchButtonImage = (_searchMenuTemplate === nil) ? CPSearchFieldSearchImage : CPSearchFieldFindImage;
[button setBordered:NO];
[button setImageScaling:CPScaleToFit];
[button setImage:searchButtonImage];
[button setAutoresizingMask:CPViewMaxXMargin];
- [button setTarget:target];
- [button setAction:action];
}
/*!
@@ -165,7 +170,18 @@ var CPSearchFieldSearchImage = nil,
*/
- (void)setCancelButton:(CPButton)button
{
- _cancelButton = button;
+ if (button != _cancelButton)
+ {
+ [_cancelButton removeFromSuperview];
+ _cancelButton = button;
+
+ [_cancelButton setFrame:[self cancelButtonRectForBounds:[self bounds]]];
+ [_cancelButton setAutoresizingMask:CPViewMinXMargin];
+ [_cancelButton setTarget:self];
+ [_cancelButton setAction:@selector(_searchFieldCancel:)];
+ [self _updateCancelButtonVisibility];
+ [self addSubview:_cancelButton];
+ }
}
/*!
@@ -200,23 +216,25 @@ var CPSearchFieldSearchImage = nil,
@return The updated bounding rectangle to use for the search text field. The default value is the value passed into the rect parameter.
Subclasses can override this method to return a new bounding rectangle for the text-field object. You might use this method to provide a custom layout for the search field control.
*/
-- (CPRect)searchTextRectForBounds:(CPRect)rect
+- (CGRect)searchTextRectForBounds:(CGRect)rect
{
- var leftOffset = 0, width = rect.size.width;
+ var leftOffset = 0,
+ width = _CGRectGetWidth(rect),
+ bounds = [self bounds];
if (_searchButton)
{
- var searchRect = [_searchButton frame];
- leftOffset = searchRect.origin.x + searchRect.size.width;
+ var searchBounds = [self searchButtonRectForBounds:bounds];
+ leftOffset = _CGRectGetMaxX(searchBounds) + 2;
}
if (_cancelButton)
{
- var cancelRect = [_cancelButton frame];
- width = cancelRect.origin.x - leftOffset;
+ var cancelRect = [self cancelButtonRectForBounds:bounds];
+ width = _CGRectGetMinX(cancelRect) - leftOffset;
}
- return CPMakeRect(leftOffset,rect.origin.y,width,rect.size.height);
+ return _CGRectMake(leftOffset, _CGRectGetMinY(rect), width, _CGRectGetHeight(rect));
}
/*!
@@ -224,9 +242,9 @@ var CPSearchFieldSearchImage = nil,
@param rect The current bounding rectangle for the search button.
Subclasses can override this method to return a new bounding rectangle for the search button. You might use this method to provide a custom layout for the search field control.
*/
-- (CPRect)searchButtonRectForBounds:(CPRect)rect
+- (CGRect)searchButtonRectForBounds:(CGRect)rect
{
- return [_searchButton frame];
+ return _CGRectMake(5, (_CGRectGetHeight(rect) - BUTTON_DEFAULT_HEIGHT) / 2, SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT);
}
/*!
@@ -234,9 +252,9 @@ var CPSearchFieldSearchImage = nil,
@param rect The updated bounding rectangle to use for the cancel button. The default value is the value passed into the rect parameter.
Subclasses can override this method to return a new bounding rectangle for the cancel button. You might use this method to provide a custom layout for the search field control.
*/
-- (CPRect)cancelButtonRectForBounds:(CPRect)rect
-{
- return [_cancelButton frame];
+- (CGRect)cancelButtonRectForBounds:(CGRect)rect
+{
+ return _CGRectMake(_CGRectGetWidth(rect) - CANCEL_BUTTON_DEFAULT_WIDTH - 5, (_CGRectGetHeight(rect) - CANCEL_BUTTON_DEFAULT_WIDTH) / 2, BUTTON_DEFAULT_HEIGHT, BUTTON_DEFAULT_HEIGHT);
}
// Managing Menu Templates
@@ -254,9 +272,9 @@ var CPSearchFieldSearchImage = nil,
@param menu The menu template to use.
The receiver looks for the tag constants described in ŇMenu tagsÓ to determine how to populate the menu with items related to recent searches. See ŇConfiguring a Search MenuÓ for a sample of how you might set up the search menu template.
*/
-- (void)setSearchMenuTemplate:(CPMenu)menu
+- (void)setSearchMenuTemplate:(CPMenu)aMenu
{
- _searchMenuTemplate = menu;
+ _searchMenuTemplate = aMenu;
[self resetSearchButton];
[self _loadRecentSearchList];
@@ -373,7 +391,7 @@ var CPSearchFieldSearchImage = nil,
// Private methods and subclassing
-- (CPRect)contentRectForBounds:(CPRect)bounds
+- (CGRect)contentRectForBounds:(CGRect)bounds
{
var superbounds = [super contentRectForBounds:bounds];
return [self searchTextRectForBounds:superbounds];
@@ -447,37 +465,78 @@ var CPSearchFieldSearchImage = nil,
[self _updateSearchMenu];
}
-- (BOOL)trackMouse:(CPEvent)event
+- (CPView)hitTest:(CGPoint)aPoint
{
- var rect,
- point,
- location = [event locationInWindow];
-
- point = [self convertPoint:location fromView:nil];
-
- rect = [self searchButtonRectForBounds:[self frame]];
- if (CPRectContainsPoint(rect,point))
- {
- return [[self searchButton] trackMouse:event];
- }
-
- rect = [self cancelButtonRectForBounds:[self frame]];
- if (CPRectContainsPoint(rect,point))
- {
- return [[self cancelButton] trackMouse:event];
- }
-
- return [super trackMouse:event];
+ // Make sure a hit anywhere within the search field returns the search field itself
+ if (_CGRectContainsPoint([self frame], aPoint))
+ return self;
+ else
+ return nil;
}
-- (CPMenu)_defaultSearchMenuTemplate
+- (BOOL)resignFirstResponder
{
- var template, item;
+ return _canResignFirstResponder && [super resignFirstResponder];
+}
+
+- (void)mouseDown:(CPEvent)anEvent
+{
+ var location = [anEvent locationInWindow],
+ point = [self convertPoint:location fromView:nil];
+
+ if (_CGRectContainsPoint([self searchButtonRectForBounds:[self bounds]], point))
+ {
+ if (_searchMenuTemplate == nil)
+ [self _sendAction:self];
+ else
+ [self _showMenu];
+ }
+ else if (_CGRectContainsPoint([self cancelButtonRectForBounds:[self bounds]], point))
+ [_cancelButton mouseDown:anEvent];
+ else
+ [super mouseDown:anEvent];
+}
+
+/*!
+ Provides the common case items for a recent searches menu. If there are not recent searches,
+ displays a single disabled item:
- template = [[CPMenu alloc] init];
+ No Recent Searches
+
+ If there are 1 more recent searches, it displays:
- item = [[CPMenuItem alloc] initWithTitle:@"Recent searches"
- action:NULL
+ Recent Searches
+ recent search 1
+ recent search 2
+ etc.
+ ---------------------
+ Clear Recent Searches
+
+ If you wish to add items before or after the template, you can. If you put items
+ before, a separator will automatically be placed before the default template item.
+ If you add items after the default template, it is your responsibility to add a separator.
+
+ To add a custom item:
+
+ item = [[CPMenuItem alloc] initWithTitle:@"google"
+ action:@selector(google:)
+ keyEquivalent:@""];
+ [item setTag:700];
+ [item setTarget:self];
+ [template addItem:item];
+
+ Be sure that your custom items do not use tags in the range 1000-1004 inclusive.
+ If you wish to maintain state in custom menu items that you add, you will need to maintain
+ the item state yourself, then in the action method of the custom items, modify the items
+ in the search menu template and send [searchField setSearchMenuTemplate:template] to update the menu.
+*/
+- (CPMenu)defaultSearchMenuTemplate
+{
+ var template = [[CPMenu alloc] init],
+ item;
+
+ item = [[CPMenuItem alloc] initWithTitle:@"Recent Searches"
+ action:nil
keyEquivalent:@""];
[item setTag:CPSearchFieldRecentsTitleMenuItemTag];
[item setEnabled:NO];
@@ -490,15 +549,15 @@ var CPSearchFieldSearchImage = nil,
[item setTarget:self];
[template addItem:item];
- item = [[CPMenuItem alloc] initWithTitle:@"Clear recent searches"
+ item = [[CPMenuItem alloc] initWithTitle:@"Clear Recent Searches"
action:@selector(_searchFieldClearRecents:)
keyEquivalent:@""];
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
[item setTarget:self];
[template addItem:item];
- item = [[CPMenuItem alloc] initWithTitle:@"No recent searches"
- action:NULL
+ item = [[CPMenuItem alloc] initWithTitle:@"No Recent Searches"
+ action:nil
keyEquivalent:@""];
[item setTag:CPSearchFieldNoRecentsMenuItemTag];
[item setEnabled:NO];
@@ -512,61 +571,96 @@ var CPSearchFieldSearchImage = nil,
if (_searchMenuTemplate === nil)
return;
- var i, menu = [[CPMenu alloc] init],
+ var menu = [[CPMenu alloc] init],
countOfRecents = [_recentSearches count],
numberOfItems = [_searchMenuTemplate numberOfItems];
- for (i = 0; i < numberOfItems; i++)
+ for (var i = 0; i < numberOfItems; i++)
{
- var item = [_searchMenuTemplate itemAtIndex:i],
- tag = [item tag];
-
- if (!(tag === CPSearchFieldRecentsTitleMenuItemTag && countOfRecents === 0) &&
- !(tag === CPSearchFieldClearRecentsMenuItemTag && countOfRecents === 0) &&
- !(tag === CPSearchFieldNoRecentsMenuItemTag && countOfRecents != 0) &&
- !(tag === CPSearchFieldRecentsMenuItemTag))
- {
- var itemAction, itemTarget;
- switch (tag)
- {
- case CPSearchFieldRecentsTitleMenuItemTag : itemAction = NULL; itemTarget = NULL; break;
- case CPSearchFieldClearRecentsMenuItemTag : itemAction = @selector(_searchFieldClearRecents:); itemTarget = self; break;
- case CPSearchFieldNoRecentsMenuItemTag : itemAction = NULL; itemTarget = NULL; break;
- default: itemAction = [item action]; itemTarget = [item target]; break;
- }
+ var item = [[_searchMenuTemplate itemAtIndex:i] copy];
- if (tag === CPSearchFieldClearRecentsMenuItemTag || tag === CPSearchFieldRecentsTitleMenuItemTag)
- {
- var separator = [CPMenuItem separatorItem];
- [separator setEnabled:NO];
- [menu addItem:separator];
- }
-
- var templateItem = [[CPMenuItem alloc] initWithTitle:[item title]
- action:itemAction
- keyEquivalent:[item keyEquivalent]];
- [templateItem setTarget:itemTarget];
- [templateItem setEnabled:([item isEnabled] && itemAction != NULL)];
- [templateItem setTag:tag];
- [menu addItem:templateItem];
- }
- else if (tag === CPSearchFieldRecentsMenuItemTag)
+ switch ([item tag])
{
- var j;
- for (j = 0; j < countOfRecents; j++)
+ case CPSearchFieldRecentsTitleMenuItemTag:
+ if (countOfRecents === 0)
+ continue;
+
+ if ([menu numberOfItems] > 0)
+ [self _addSeparatorToMenu:menu];
+ break;
+
+ case CPSearchFieldRecentsMenuItemTag:
{
- var rencentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:j]
- action:@selector(_searchFieldSearch:)
- keyEquivalent:[item keyEquivalent]];
- [rencentItem setTarget:self];
- [menu addItem:rencentItem];
+ var itemAction = @selector(_searchFieldSearch:);
+
+ for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex)
+ {
+ // RECENT_SEARCH_PREFIX is a hack until CPMenuItem -setIndentationLevel works
+ var recentItem = [[CPMenuItem alloc] initWithTitle:RECENT_SEARCH_PREFIX + [_recentSearches objectAtIndex:recentIndex]
+ action:itemAction
+ keyEquivalent:[item keyEquivalent]];
+ [item setTarget:self];
+ [menu addItem:recentItem];
+ }
+
+ continue;
}
+
+ case CPSearchFieldClearRecentsMenuItemTag:
+ if (countOfRecents === 0)
+ continue;
+
+ if ([menu numberOfItems] > 0)
+ [self _addSeparatorToMenu:menu];
+
+ [item setAction:@selector(_searchFieldClearRecents:)];
+ [item setTarget:self];
+ break;
+
+ case CPSearchFieldNoRecentsMenuItemTag:
+ if (countOfRecents !== 0)
+ continue;
+
+ if ([menu numberOfItems] > 0)
+ [self _addSeparatorToMenu:menu];
+ break;
+
+ case CPSearchFieldSeparatorMenuItemTag:
+ item = [CPMenuItem separatorItem];
+ [item setEnabled:NO];
+ [menu addItem:item];
+ continue;
}
- }
+
+ [item setEnabled:([item isEnabled] && [item action] != nil && [item target] != nil)];
+ [menu addItem:item];
+ }
+
+ [menu setDelegate:self];
+
_searchMenu = menu;
}
-- (void)_showMenu:(id)sender
+- (void)_addSeparatorToMenu:(CPMenu)aMenu
+{
+ var separator = [CPMenuItem separatorItem];
+ [separator setEnabled:NO];
+ [aMenu addItem:separator];
+}
+
+- (void)menuWillOpen:(CPMenu)menu
+{
+ _canResignFirstResponder = NO;
+}
+
+- (void)menuDidClose:(CPMenu)menu
+{
+ _canResignFirstResponder = YES;
+
+ [self becomeFirstResponder];
+}
+
+- (void)_showMenu
{
if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled])
return;
@@ -576,12 +670,14 @@ var CPSearchFieldSearchImage = nil,
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];
- [CPMenu popUpContextMenu:_searchMenu withEvent:anEvent forView:sender];
+ [self selectAll:nil];
+ [CPMenu popUpContextMenu:_searchMenu withEvent:anEvent forView:self];
}
- (void)_sendPartialString
{
- [self _sendAction:self];
+ [super sendAction:[self action] to:[self target]];
+ [_partialStringTimer invalidate];
}
- (void)_searchFieldCancel:(id)sender
@@ -593,14 +689,15 @@ var CPSearchFieldSearchImage = nil,
- (void)_searchFieldSearch:(id)sender
{
- var searchString = [sender title];
+ var searchString = [[sender title] substringFromIndex:[RECENT_SEARCH_PREFIX length]];
if ([sender tag] != CPSearchFieldRecentsMenuItemTag)
[self _addStringToRecentSearches:searchString];
[self setObjectValue:searchString];
[self _sendPartialString];
-
+ [self selectAll:nil];
+
[self _updateCancelButtonVisibility];
}
@@ -608,6 +705,8 @@ var CPSearchFieldSearchImage = nil,
{
[self setRecentSearches:[CPArray array]];
[self _updateSearchMenu];
+ [self setStringValue:@""];
+ [self _updateCancelButtonVisibility];
}
- (void)_registerForAutosaveNotification
@@ -668,7 +767,7 @@ var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey",
CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey",
CPMaximumRecentsKey = @"CPMaximumRecentsKey",
- CPSearchMenuTemplateKey = @"CPSearchMenuTemplateKey";
+ CPSearchMenuTemplateKey = @"CPSearchMenuTemplateKey";
@implementation CPSearchField (CPCoding)
@@ -690,6 +789,7 @@ var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
if (_recentsAutosaveName)
[coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
+
if (_searchMenuTemplate)
[coder encodeObject:_searchMenuTemplate forKey:CPSearchMenuTemplateKey];
}
@@ -698,18 +798,17 @@ var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
{
if (self = [super initWithCoder:coder])
{
- [self _initWithFrame:[self frame]];
-
_recentsAutosaveName = [coder decodeObjectForKey:CPRecentsAutosaveNameKey];
_sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
_sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
_maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
-
+
var template = [coder decodeObjectForKey:CPSearchMenuTemplateKey];
+
if (template)
[self setSearchMenuTemplate:template];
- [self setDelegate:self];
+ [self _init];
}
return self;
diff --git a/AppKit/CPSegmentedControl.j b/AppKit/CPSegmentedControl.j
index e7e60d860..4b5f0e3d8 100644
--- a/AppKit/CPSegmentedControl.j
+++ b/AppKit/CPSegmentedControl.j
@@ -82,7 +82,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (int)selectedTag
{
- return _segments[_selectedSegment].tag;
+ return [_segments[_selectedSegment] tag];
}
// Specifying the number of segments
@@ -183,7 +183,7 @@ CPSegmentSwitchTrackingMomentary = 2;
selected = NO;
for (; index < _segments.length; ++index)
- if (_segments[index].selected)
+ if ([_segments[index] selected])
if (selected)
[self setSelected:NO forSegment:index];
else
@@ -195,7 +195,7 @@ CPSegmentSwitchTrackingMomentary = 2;
var index = 0;
for (; index < _segments.length; ++index)
- if (_segments[index].selected)
+ if ([_segments[index] selected])
[self setSelected:NO forSegment:index];
}
}
@@ -217,7 +217,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
{
- _segments[aSegment].width = aWidth;
+ [_segments[aSegment] setWidth:aWidth];
[self tileWithChangedSegment:aSegment];
}
@@ -229,7 +229,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (float)widthForSegment:(unsigned)aSegment
{
- return _segments[aSegment].width;
+ return [_segments[aSegment] width];
}
/*!
@@ -240,9 +240,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
{
- var segment = _segments[aSegment];
-
- segment.image = anImage;
+ [_segments[aSegment] setImage:anImage];
[self tileWithChangedSegment:aSegment];
}
@@ -254,7 +252,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPImage)imageForSegment:(unsigned)aSegment
{
- return _segments[aSegment].image;
+ return [_segments[aSegment] image];
}
/*!
@@ -265,9 +263,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
{
- var segment = _segments[aSegment];
-
- _segments[aSegment].label = aLabel;
+ [_segments[aSegment] setLabel:aLabel];
[self tileWithChangedSegment:aSegment];
}
@@ -279,7 +275,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPString)labelForSegment:(unsigned)aSegment
{
- return _segments[aSegment].label;
+ return [_segments[aSegment] label];
}
/*!
@@ -290,7 +286,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
{
- _segments[aSegment].menu = aMenu;
+ [_segments[aSegment] setMenu:aMenu];
}
/*!
@@ -300,7 +296,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPMenu)menuForSegment:(unsigned)aSegment
{
- return _segments[aSegment].menu;
+ return [_segments[aSegment] menu];
}
/*!
@@ -315,10 +311,10 @@ CPSegmentSwitchTrackingMomentary = 2;
var segment = _segments[aSegment];
// If we're already in this state, bail.
- if (segment.selected == isSelected)
+ if ([segment selected] == isSelected)
return;
- segment.selected = isSelected;
+ [segment setSelected:isSelected];
_themeStates[aSegment] = isSelected ? CPThemeStateSelected : CPThemeStateNormal;
@@ -331,7 +327,7 @@ CPSegmentSwitchTrackingMomentary = 2;
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
{
- _segments[oldSelectedSegment].selected = NO;
+ [_segments[oldSelectedSegment] setSelected:NO];
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
[self drawSegmentBezel:oldSelectedSegment highlight:NO];
@@ -352,7 +348,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (BOOL)isSelectedForSegment:(unsigned)aSegment
{
- return _segments[aSegment].selected;
+ return [_segments[aSegment] selected];
}
/*!
@@ -363,6 +359,8 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment
{
+ [_segments[aSegment] setEnabled:isEnabled];
+
if (isEnabled)
_themeStates[aSegment] &= ~CPThemeStateDisabled;
else
@@ -379,7 +377,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (BOOL)isEnabledForSegment:(unsigned)aSegment
{
- return !(_themeStates[aSegment] & CPThemeStateDisabled)
+ return [_segments[aSegment] enabled];
}
/*!
@@ -389,7 +387,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setTag:(int)aTag forSegment:(unsigned)aSegment
{
- _segments[aSegment].tag = aTag;
+ [_segments[aSegment] setTag:aTag];
}
/*!
@@ -398,7 +396,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (int)tagForSegment:(unsigned)aSegment
{
- return _segments[aSegment].tag;
+ return [_segments[aSegment] tag];
}
// Drawings
@@ -461,7 +459,7 @@ CPSegmentSwitchTrackingMomentary = 2;
else if (aName.indexOf("segment-bezel") === 0)
{
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
- frame = CGRectCreateCopy(_segments[segment].frame);
+ frame = CGRectCreateCopy([_segments[segment] frame]);
if (segment === 0)
{
@@ -556,8 +554,8 @@ CPSegmentSwitchTrackingMomentary = 2;
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"segment-bezel-"+i];
- [contentView setText:segment.label];
- [contentView setImage:segment.image];
+ [contentView setText:[segment label]];
+ [contentView setImage:[segment image]];
[contentView setFont:[self valueForThemeAttribute:@"font" inState:themeState]];
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:themeState]];
@@ -568,9 +566,9 @@ CPSegmentSwitchTrackingMomentary = 2;
[contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:themeState]];
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling" inState:themeState]];
- if (segment.image && segment.label)
+ if ([segment image] && [segment label])
[contentView setImagePosition:[self valueForThemeAttribute:@"image-position" inState:themeState]];
- else if (segment.image)
+ else if ([segment image])
[contentView setImagePosition:CPImageOnly];
if (i == count - 1)
@@ -609,27 +607,32 @@ CPSegmentSwitchTrackingMomentary = 2;
return;
var segment = _segments[aSegment],
- segmentWidth = segment.width,
+ segmentWidth = [segment width],
themeState = _themeStates[aSegment] | (_themeState & CPThemeStateDisabled),
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
font = [self valueForThemeAttribute:@"font" inState:themeState];
if (!segmentWidth)
{
- if (segment.image && segment.label)
- segmentWidth = [segment.label sizeWithFont:font].width + [segment.image size].width + contentInset.left + contentInset.right;
+ if ([segment image] && [segment label])
+ segmentWidth = [[segment label] sizeWithFont:font].width + [[segment image] size].width + contentInset.left + contentInset.right;
else if (segment.image)
- segmentWidth = [segment.image size].width + contentInset.left + contentInset.right;
+ segmentWidth = [[segment image] size].width + contentInset.left + contentInset.right;
else if (segment.label)
- segmentWidth = [segment.label sizeWithFont:font].width + contentInset.left + contentInset.right;
+ segmentWidth = [[segment label] sizeWithFont:font].width + contentInset.left + contentInset.right;
else
segmentWidth = 0.0;
}
- var delta = segmentWidth - CGRectGetWidth(segment.frame);
+ var delta = segmentWidth - CGRectGetWidth([segment frame]);
if (!delta)
+ {
+ [self setNeedsLayout];
+ [self setNeedsDisplay:YES];
+
return;
+ }
// Update Contorl Size
var frame = [self frame];
@@ -637,15 +640,15 @@ CPSegmentSwitchTrackingMomentary = 2;
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
// Update Segment Width
- segment.width = segmentWidth;
- segment.frame = [self frameForSegment:aSegment];;
+ [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;
+ [_segments[index] frame].origin.x += delta;
[self drawSegmentBezel:index highlight:NO];
[self drawSegment:index highlight:NO];
@@ -698,12 +701,12 @@ CPSegmentSwitchTrackingMomentary = 2;
count = _segments.length;
while (count--)
- if (CGRectContainsPoint(_segments[count].frame, aPoint))
+ if (CGRectContainsPoint([_segments[count] frame], aPoint))
return count;
if (_segments.length)
{
- var adjustedLastFrame = CGRectCreateCopy(_segments[_segments.length - 1].frame);
+ var adjustedLastFrame = CGRectCreateCopy([_segments[_segments.length - 1] frame]);
adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x;
if (CGRectContainsPoint(adjustedLastFrame, aPoint))
@@ -844,7 +847,7 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
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];
}
@@ -852,7 +855,7 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
remainingWidth = FLOOR(difference / _segments.length);
for (var i=0; i < _segments.length; i++)
- [self setWidth:_segments[i].width + remainingWidth forSegment:i];
+ [self setWidth:[_segments[i] width] + remainingWidth forSegment:i];
[self tileWithChangedSegment:0];
}
@@ -875,15 +878,15 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
@implementation _CPSegmentItem : CPObject
{
- CPImage image;
- CPString label;
- CPMenu menu;
- BOOL selected;
- BOOL enabled;
- int tag;
- int width;
+ CPImage image @accessors;
+ CPString label @accessors;
+ CPMenu menu @accessors;
+ BOOL selected @accessors;
+ BOOL enabled @accessors;
+ int tag @accessors;
+ int width @accessors;
- CGRect frame;
+ CGRect frame @accessors;
}
- (id)init
@@ -894,8 +897,8 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
label = @"";
menu = nil;
selected = NO;
- enabled = NO;
- tag = 0;
+ enabled = YES;
+ tag = -1;
width = 0;
frame = CGRectMakeZero();
diff --git a/AppKit/CPShadowView.j b/AppKit/CPShadowView.j
index 36cfabdad..c1aaac6f5 100644
--- a/AppKit/CPShadowView.j
+++ b/AppKit/CPShadowView.j
@@ -35,12 +35,12 @@ CPHeavyShadow = 1;
var CPShadowViewLightBackgroundColor = nil,
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,
@@ -61,51 +61,76 @@ var LIGHT_LEFT_INSET = 3.0,
return;
var bundle = [CPBundle bundleForClass:[self class]];
-
+
CPShadowViewLightBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
[
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopLeft.png"] size:CGSizeMake(9.0, 9.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTop.png"] size:CGSizeMake(1.0, 9.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopRight.png"] size:CGSizeMake(9.0, 9.0)],
-
+
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/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
+{
+ return [self shadowViewEnclosingView:aView withWeight:CPLightShadow];
+}
+
++ (id)shadowViewEnclosingView:(CPView)aView withWeight:(CPShadowWeight)aWeight
+{
+ var shadowView = [[CPShadowView alloc] initWithFrame:[aView frame]];
+ [shadowView setWeight:aWeight];
+
+ var size = [shadowView frame].size,
+ width = size.width - [shadowView leftInset] - [shadowView rightInset],
+ height = size.height - [shadowView topInset] - [shadowView bottomInset],
+ enclosingView = [aView superview];
+
+ [shadowView setHitTests:[aView hitTests]];
+ [shadowView setAutoresizingMask:[aView autoresizingMask]];
+ [aView removeFromSuperview];
+ [shadowView addSubview:aView];
+ [aView setFrame:CGRectMake([shadowView leftInset], [shadowView topInset], width, height)]
+ [enclosingView addSubview:shadowView];
+
+ return shadowView;
+}
+
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
-
+
if (self)
{
_weight = CPLightShadow;
-
+
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
-
+
[self setHitTests:NO];
}
-
+
return self;
}
@@ -113,9 +138,9 @@ var LIGHT_LEFT_INSET = 3.0,
{
if (_weight == aWeight)
return;
-
+
_weight = aWeight;
-
+
if (_weight == CPLightShadow)
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
@@ -147,7 +172,7 @@ var LIGHT_LEFT_INSET = 3.0,
{
if (_weight == CPLightShadow)
return LIGHT_LEFT_INSET + LIGHT_RIGHT_INSET;
-
+
return HEAVY_LEFT_INSET + HEAVY_RIGHT_INSET;
}
@@ -155,7 +180,7 @@ var LIGHT_LEFT_INSET = 3.0,
{
if (_weight == CPLightShadow)
return LIGHT_TOP_INSET + LIGHT_BOTTOM_INSET;
-
+
return HEAVY_TOP_INSET + HEAVY_BOTTOM_INSET;
}
diff --git a/AppKit/CPSlider.j b/AppKit/CPSlider.j
index b448becd0..36f951f5c 100644
--- a/AppKit/CPSlider.j
+++ b/AppKit/CPSlider.j
@@ -198,7 +198,7 @@ CPCircularSlider = 1;
else if ([self isVertical])
{
knobRect.origin.x = _CGRectGetMidX(trackRect) - knobSize.width / 2.0;
- knobRect.origin.y = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (_CGRectGetHeight(trackRect) - knobSize.height);
+ knobRect.origin.y = ((_maxValue - [self doubleValue]) / (_maxValue - _minValue)) * (_CGRectGetHeight(trackRect) - knobSize.height);
}
else
{
@@ -320,7 +320,7 @@ CPCircularSlider = 1;
var minValue = [self minValue];
- return MAX(0.0, MIN(1.0, (aPoint.y - _CGRectGetMinY(trackRect)) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue;
+ return MAX(0.0, MIN(1.0, (_CGRectGetMaxY(trackRect) - aPoint.y) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue;
}
else
{
diff --git a/AppKit/CPSliderColorPicker.j b/AppKit/CPSliderColorPicker.j
index 6c02cfb16..3934b89db 100644
--- a/AppKit/CPSliderColorPicker.j
+++ b/AppKit/CPSliderColorPicker.j
@@ -47,16 +47,15 @@
CPTextField _saturationLabel;
CPTextField _brightnessLabel;
CPTextField _hexLabel;
+ CPTextField _hexValue;
-#if PLATFORM(DOM)
- DOMElement _redValue;
- DOMElement _greenValue;
- DOMElement _blueValue;
- DOMElement _hueValue;
- DOMElement _saturationValue;
- DOMElement _brightnessValue;
- DOMElement _hexValue;
-#endif
+ CPTextField _hexValue;
+ CPTextField _redValue;
+ CPTextField _greenValue;
+ CPTextField _blueValue;
+ CPTextField _hueValue;
+ CPTextField _saturationValue;
+ CPTextField _brightnessValue;
}
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
@@ -86,73 +85,13 @@
[_redSlider setAction: @selector(sliderChanged:)];
[_redSlider setAutoresizingMask: CPViewWidthSizable];
-#if PLATFORM(DOM)
- var updateFunction = function(aDOMEvent)
- {
- if(isNaN(this.value))
- return;
-
- switch(this)
- {
- case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
- //[self sliderChanged: _redSlider];
- break;
-
- case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
- //[self sliderChanged: _greenSlider];
- break;
-
- case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
- //[self sliderChanged: _blueSlider];
- break;
-
- case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(this.value), 360), 0)];
- //[self sliderChanged: _hueSlider];
- break;
-
- case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(this.value), 100), 0)];
- //[self sliderChanged: _saturationSlider];
- break;
-
- case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(this.value), 100), 0)];
- //[self sliderChanged: _brightnessSlider];
- break;
- }
-
- this.blur();
- };
-
- var keypressFunction = function(aDOMEvent)
- {
- aDOMEvent = aDOMEvent || window.event;
- if (aDOMEvent.keyCode == 13)
- {
- updateFunction(aDOMEvent);
-
- if(aDOMEvent.preventDefault)
- aDOMEvent.preventDefault();
- else if(aDOMEvent.stopPropagation)
- aDOMEvent.stopPropagation();
- }
- }
-
- //red value input box
- var redValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 35, 45, 20)];
- [redValue setAutoresizingMask: CPViewMinXMargin];
-
- _redValue = document.createElement("input");
- _redValue.style.width = "40px";
- _redValue.style.backgroundColor = "transparent";
- _redValue.style.border = "1px solid black";
- _redValue.style.color = "black";
- _redValue.style.position = "absolute";
- _redValue.style.top = "0px";
- _redValue.style.left = "0px";
- _redValue.onchange = updateFunction;
-
- redValue._DOMElement.appendChild(_redValue);
- [_contentView addSubview: redValue];
-#endif
+ // red value input box
+ _redValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 30, 45, 29)];
+ [_redValue setAutoresizingMask: CPViewMinXMargin];
+ [_redValue setEditable: YES];
+ [_redValue setBezeled: YES];
+ [_redValue setDelegate: self];
+ [_contentView addSubview: _redValue];
_greenLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 58, 15, 20)];
[_greenLabel setStringValue: "G"];
@@ -165,17 +104,13 @@
[_greenSlider setAction: @selector(sliderChanged:)];
[_greenSlider setAutoresizingMask: CPViewWidthSizable];
-#if PLATFORM(DOM)
- //green value input box
- var greenValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 58, 45, 20)];
- [greenValue setAutoresizingMask: CPViewMinXMargin];
-
- _greenValue = _redValue.cloneNode(false);
- _greenValue.onchange = updateFunction;
-
- greenValue._DOMElement.appendChild(_greenValue);
- [_contentView addSubview: greenValue];
-#endif
+ // green value input box
+ _greenValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 53, 45, 29)];
+ [_greenValue setAutoresizingMask: CPViewMinXMargin];
+ [_greenValue setEditable: YES];
+ [_greenValue setBezeled: YES];
+ [_greenValue setDelegate: self];
+ [_contentView addSubview: _greenValue];
_blueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 81, 15, 20)];
[_blueLabel setStringValue: "B"];
@@ -188,17 +123,14 @@
[_blueSlider setAction: @selector(sliderChanged:)];
[_blueSlider setAutoresizingMask: CPViewWidthSizable];
-#if PLATFORM(DOM)
- //blue value input box
- var blueValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 81, 45, 20)];
- [blueValue setAutoresizingMask: CPViewMinXMargin];
+ // blue value input box
+ _blueValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 76, 45, 29)];
+ [_blueValue setAutoresizingMask: CPViewMinXMargin];
+ [_blueValue setEditable: YES];
+ [_blueValue setBezeled: YES];
+ [_blueValue setDelegate: self];
+ [_contentView addSubview: _blueValue];
- _blueValue = _redValue.cloneNode(false);
- _blueValue.onchange = updateFunction;
-
- blueValue._DOMElement.appendChild(_blueValue);
- [_contentView addSubview: blueValue];
-#endif
_hsbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 120, 190, 20)];
[_hsbLabel setStringValue: "Hue, Saturation, Brightness"];
[_hsbLabel setTextColor:[CPColor blackColor]];
@@ -214,17 +146,14 @@
[_hueSlider setAction: @selector(sliderChanged:)];
[_hueSlider setAutoresizingMask: CPViewWidthSizable];
-#if PLATFORM(DOM)
- //red value input box
- var hueValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 145, 45, 20)];
- [hueValue setAutoresizingMask: CPViewMinXMargin];
+ // hue value input box
+ _hueValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 140, 45, 29)];
+ [_hueValue setAutoresizingMask: CPViewMinXMargin];
+ [_hueValue setEditable: YES];
+ [_hueValue setBezeled: YES];
+ [_hueValue setDelegate: self];
+ [_contentView addSubview: _hueValue];
- _hueValue = _redValue.cloneNode(false);
- _hueValue.onchange = updateFunction;
-
- hueValue._DOMElement.appendChild(_hueValue);
- [_contentView addSubview: hueValue];
-#endif
_saturationLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 168, 15, 20)];
[_saturationLabel setStringValue: "S"];
[_saturationLabel setTextColor:[CPColor blackColor]];
@@ -236,17 +165,14 @@
[_saturationSlider setAction: @selector(sliderChanged:)];
[_saturationSlider setAutoresizingMask: CPViewWidthSizable];
-#if PLATFORM(DOM)
- //green value input box
- var saturationValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 168, 45, 20)];
- [saturationValue setAutoresizingMask: CPViewMinXMargin];
+ // saturation value input box
+ _saturationValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 163, 45, 29)];
+ [_saturationValue setAutoresizingMask: CPViewMinXMargin];
+ [_saturationValue setEditable: YES];
+ [_saturationValue setBezeled: YES];
+ [_saturationValue setDelegate: self];
+ [_contentView addSubview: _saturationValue];
- _saturationValue = _redValue.cloneNode(false);
- _saturationValue.onchange = updateFunction;
-
- saturationValue._DOMElement.appendChild(_saturationValue);
- [_contentView addSubview: saturationValue];
-#endif
_brightnessLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 191, 15, 20)];
[_brightnessLabel setStringValue: "B"];
[_brightnessLabel setTextColor:[CPColor blackColor]];
@@ -258,51 +184,24 @@
[_brightnessSlider setAction: @selector(sliderChanged:)];
[_brightnessSlider setAutoresizingMask: CPViewWidthSizable];
-#if PLATFORM(DOM)
- //blue value input box
- var brightnessValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 191, 45, 20)];
- [brightnessValue setAutoresizingMask: CPViewMinXMargin];
+ // brightness value input box
+ _brightnessValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 186, 45, 29)];
+ [_brightnessValue setAutoresizingMask: CPViewMinXMargin];
+ [_brightnessValue setEditable: YES];
+ [_brightnessValue setBezeled: YES];
+ [_brightnessValue setDelegate: self];
+ [_contentView addSubview: _brightnessValue];
- _brightnessValue = _redValue.cloneNode(false);
- _brightnessValue.onchange = updateFunction;
-
- brightnessValue._DOMElement.appendChild(_brightnessValue);
- [_contentView addSubview: brightnessValue];
-#endif
_hexLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 230, 30, 20)];
[_hexLabel setStringValue: "Hex"];
[_hexLabel setTextColor:[CPColor blackColor]];
-#if PLATFORM(DOM)
//hex input box
- _hexValue = _redValue.cloneNode(false);
- _hexValue.style.top = "228px";
- _hexValue.style.width = "80px";
- _hexValue.style.left = "35px";
- _hexValue.onkeypress = function(aDOMEvent)
- {
- aDOMEvent = aDOMEvent || window.event;
- if (aDOMEvent.keyCode == 13)
- {
- var newColor = [CPColor colorWithHexString: this.value];
-
- if(newColor)
- {
- [self setColor: newColor];
- [[self colorPanel] setColor: newColor];
- }
-
- if(aDOMEvent.preventDefault)
- aDOMEvent.preventDefault();
- else if(aDOMEvent.stopPropagation)
- aDOMEvent.stopPropagation();
-
- this.blur();
- }
- };
-
- _contentView._DOMElement.appendChild(_hexValue);
-#endif
+ _hexValue = [[CPTextField alloc] initWithFrame: CGRectMake(32, 225, 80, 29)];
+ [_hexValue setEditable: YES];
+ [_hexValue setBezeled: YES];
+ [_hexValue setDelegate: self];
+ [_contentView addSubview: _hexValue];
[_contentView addSubview: _rgbLabel];
[_contentView addSubview: _redLabel];
@@ -394,9 +293,7 @@
- (void)updateHex:(CPColor)aColor
{
-#if PLATFORM(DOM)
- _hexValue.value = [aColor hexString];
-#endif
+ [_hexValue setStringValue:[aColor hexString]];
}
- (void)updateRGBSliders:(CPColor)aColor
@@ -410,15 +307,13 @@
- (void)updateLabels
{
-#if PLATFORM(DOM)
- _hueValue.value = ROUND([_hueSlider floatValue]);
- _saturationValue.value = ROUND([_saturationSlider floatValue]);
- _brightnessValue.value = ROUND([_brightnessSlider floatValue]);
+ [_hueValue setStringValue: ROUND([_hueSlider floatValue])];
+ [_saturationValue setStringValue: ROUND([_saturationSlider floatValue])];
+ [_brightnessValue setStringValue: ROUND([_brightnessSlider floatValue])];
- _redValue.value = ROUND([_redSlider floatValue] * 255);
- _greenValue.value = ROUND([_greenSlider floatValue] * 255);
- _blueValue.value = ROUND([_blueSlider floatValue] * 255);
-#endif
+ [_redValue setStringValue: ROUND([_redSlider floatValue] * 255)];
+ [_greenValue setStringValue: ROUND([_greenSlider floatValue] * 255)];
+ [_blueValue setStringValue: ROUND([_blueSlider floatValue] * 255)];
}
- (CPImage)provideNewButtonImage
@@ -431,4 +326,43 @@
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:CPColorPicker] pathForResource:"slider_button_h.png"] size:CGSizeMake(32, 32)];
}
+- (void)controlTextDidEndEditing:(CPNotification)aNotification
+{
+ var field = [aNotification object],
+ value = [[field stringValue] stringByTrimmingWhitespace];
+ if (field === _hexValue) {
+ var newColor = [CPColor colorWithHexString: value];
+ if (newColor) {
+ [self setColor: newColor];
+ [[self colorPanel] setColor: newColor];
+ }
+ } else {
+ switch(field) {
+ case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
+ [self sliderChanged: _redSlider];
+ break;
+
+ case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
+ [self sliderChanged: _greenSlider];
+ break;
+
+ case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
+ [self sliderChanged: _blueSlider];
+ break;
+
+ case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(value), 360), 0)];
+ [self sliderChanged: _hueSlider];
+ break;
+
+ case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
+ [self sliderChanged: _saturationSlider];
+ break;
+
+ case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
+ [self sliderChanged: _brightnessSlider];
+ break;
+ }
+ }
+}
+
@end
diff --git a/AppKit/CPSplitView.j b/AppKit/CPSplitView.j
index 70f1a9282..a4028aad8 100644
--- a/AppKit/CPSplitView.j
+++ b/AppKit/CPSplitView.j
@@ -47,6 +47,7 @@ var CPSplitViewHorizontalImage = nil,
int _currentDivider;
float _initialOffset;
+ float _preCollapsePosition;
CPString _originComponent;
CPString _sizeComponent;
@@ -60,6 +61,17 @@ var CPSplitViewHorizontalImage = nil,
CPArray _buttonBars;
}
++ (CPString)themeClass
+{
+ return @"splitview";
+}
+
++ (id)themeAttributes
+{
+ return [CPDictionary dictionaryWithObjects:[10.0, 1.0]
+ forKeys:[@"divider-thickness", @"pane-divider-thickness"]];
+}
+
/*
@ignore
*/
@@ -90,7 +102,7 @@ var CPSplitViewHorizontalImage = nil,
- (float)dividerThickness
{
- return _isPaneSplitter ? 1.0 : 10.0;
+ return [self currentValueForThemeAttribute:[self isPaneSplitter] ? @"pane-divider-thickness" : @"divider-thickness"];
}
- (BOOL)isVertical
@@ -152,7 +164,7 @@ var CPSplitViewHorizontalImage = nil,
_isPaneSplitter = shouldBePaneSplitter;
if(_DOMDividerElements[_drawingDivider])
- [self _setupDOMDivider]
+ [self _setupDOMDivider];
// The divider changes size when pane splitter mode is toggled, so the
// subviews need to change size too.
@@ -293,7 +305,7 @@ var CPSplitViewHorizontalImage = nil,
if ([_delegate respondsToSelector:@selector(splitView:additionalEffectiveRectOfDividerAtIndex:)])
additionalRect = [_delegate splitView:self additionalEffectiveRectOfDividerAtIndex:anIndex];
- return CGRectContainsPoint(effectiveRect, aPoint) ||
+ return CGRectContainsPoint(effectiveRect, aPoint) ||
(additionalRect && CGRectContainsPoint(additionalRect, aPoint)) ||
(buttonBarRect && CGRectContainsPoint(buttonBarRect, aPoint));
}
@@ -358,14 +370,14 @@ var CPSplitViewHorizontalImage = nil,
if ([_delegate splitView:self canCollapseSubview:_subviews[i]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
{
if ([self isSubviewCollapsed:_subviews[i]])
- [self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
+ [self setPosition:_preCollapsePosition ? _preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
else
[self setPosition:minPosition ofDividerAtIndex:i];
}
else if ([_delegate splitView:self canCollapseSubview:_subviews[i+1]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i+1] forDoubleClickOnDividerAtIndex:i])
{
if ([self isSubviewCollapsed:_subviews[i+1]])
- [self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
+ [self setPosition:_preCollapsePosition ? _preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
else
[self setPosition:maxPosition ofDividerAtIndex:i];
}
@@ -444,11 +456,19 @@ var CPSplitViewHorizontalImage = nil,
if (_currentDivider === i || (_currentDivider == CPNotFound && [self cursorAtPoint:point hitDividerAtIndex:i]))
{
var frame = [_subviews[i] frame],
- startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent],
+ size = frame.size[_sizeComponent],
+ startPosition = frame.origin[_originComponent] + size,
canShrink = [self _realPositionForPosition:startPosition-1 ofDividerAtIndex:i] < startPosition,
canGrow = [self _realPositionForPosition:startPosition+1 ofDividerAtIndex:i] > startPosition,
cursor = [CPCursor arrowCursor];
+ if (size === 0)
+ canGrow = YES; // Subview is collapsed.
+ else if (!canShrink &&
+ [_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
+ [_delegate splitView:self canCollapseSubview:_subviews[i]])
+ canShrink = YES; // Subview is collapsible.
+
if (_isVertical && canShrink && canGrow)
cursor = [CPCursor resizeLeftRightCursor];
else if (_isVertical && canShrink)
@@ -531,10 +551,18 @@ var CPSplitViewHorizontalImage = nil,
viewB = _subviews[dividerIndex + 1],
frameB = [viewB frame];
+ _preCollapsePosition = 0;
+
+ var preSize = frameA.size[_sizeComponent];
frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
+ if (preSize !== 0 && frameA.size[_sizeComponent] === 0)
+ _preCollapsePosition = preSize;
[_subviews[dividerIndex] setFrame:frameA];
+ preSize = frameB.size[_sizeComponent];
frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
+ if (preSize !== 0 && frameB.size[_sizeComponent] === 0)
+ _preCollapsePosition = preSize;
frameB.origin[_originComponent] = realPosition + [self dividerThickness];
[_subviews[dividerIndex + 1] setFrame:frameB];
@@ -641,14 +669,14 @@ var CPSplitViewHorizontalImage = nil,
/*!
Set the button bar who's resize control should act as a control for this splitview.
- Each divider can have at most one button bar assigned to it, and that button bar must be
+ Each divider can have at most one button bar assigned to it, and that button bar must be
a subview of one of the split view's subviews.
Calling this method with nil as the button bar will remove any currently assigned button bar
for the divider at that index. Indexes will not be adjusted as new subviews are added, so you
should usually call this method after adding all the desired subviews to the split view.
- This method will automatically configure the hasResizeControl and resizeControlIsLeftAligned
+ This method will automatically configure the hasResizeControl and resizeControlIsLeftAligned
parameters of the button bar, and will override any currently set values.
*/
- (void)setButtonBar:(CPButtonBar)aButtonBar forDividerAtIndex:(unsigned)dividerIndex
@@ -669,7 +697,7 @@ var CPSplitViewHorizontalImage = nil,
}
if (view !== self)
- [CPException raise:CPInvalidArgumentException
+ [CPException raise:CPInvalidArgumentException
reason:@"CPSplitView button bar must be a subview of the split view."];
var viewIndex = [[self subviews] indexOfObject:subview];
@@ -677,7 +705,7 @@ var CPSplitViewHorizontalImage = nil,
[aButtonBar setHasResizeControl:YES];
[aButtonBar setResizeControlIsLeftAligned:dividerIndex < viewIndex];
- _buttonBars[dividerIndex] = aButtonBar;
+ _buttonBars[dividerIndex] = aButtonBar;
}
- (void)_postNotificationWillResize
@@ -714,7 +742,7 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
_DOMDividerElements = [];
_buttonBars = [aCoder decodeObjectForKey:CPSplitViewButtonBarsKey] || [];
-
+
_delegate = [aCoder decodeObjectForKey:CPSplitViewDelegateKey];
_isPaneSplitter = [aCoder decodeBoolForKey:CPSplitViewIsPaneSplitterKey];
diff --git a/AppKit/CPStringDrawing.j b/AppKit/CPStringDrawing.j
index dec7bde1c..859c23926 100644
--- a/AppKit/CPStringDrawing.j
+++ b/AppKit/CPStringDrawing.j
@@ -27,6 +27,14 @@
@implementation CPString (CPStringDrawing)
+/*!
+ Returns a dictionary with the items "ascender", "descender", "lineHeight"
+*/
++ (CPDictionary)metricsOfFont:(CPFont)aFont
+{
+ return [CPPlatformString metricsOfFont:aFont];
+}
+
/*!
Returns the string
*/
diff --git a/AppKit/CPTabView.j b/AppKit/CPTabView.j
index 71aac4a7b..04fa320ba 100644
--- a/AppKit/CPTabView.j
+++ b/AppKit/CPTabView.j
@@ -63,20 +63,20 @@ var CPTabViewBezelBorderLeftImage = nil,
var LEFT_INSET = 7.0,
RIGHT_INSET = 7.0;
-
+
var CPTabViewDidSelectTabViewItemSelector = 1,
CPTabViewShouldSelectTabViewItemSelector = 2,
CPTabViewWillSelectTabViewItemSelector = 4,
CPTabViewDidChangeNumberOfTabViewItemsSelector = 8;
-/*!
+/*!
@ingroup appkit
@class CPTabView
This class represents a view that has multiple subviews (CPTabViewItem) presented as individual tabs.
Only one CPTabViewItem is shown at a time, and other CPTabViewItems can be made visible
(one at a time) by clicking on the CPTabViewItem's tab at the top of the tab view.
-
+
THe currently selected CPTabViewItem is the view that is displayed.
*/
@implementation CPTabView : CPView
@@ -84,15 +84,15 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
CPView _labelsView;
CPView _backgroundView;
CPView _separatorView;
-
+
CPView _auxiliaryView;
CPView _contentView;
-
+
CPArray _tabViewItems;
CPTabViewItem _selectedTabViewItem;
CPTabViewType _tabViewType;
-
+
id _delegate;
unsigned _delegateSelectors;
}
@@ -104,20 +104,20 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
if (self != CPTabView)
return;
-
+
var bundle = [CPBundle bundleForClass:self],
-
+
emptyImage = [[CPImage alloc] initByReferencingFile:@"" size:CGSizeMake(7.0, 0.0)],
backgroundImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBackgroundCenter.png"] size:CGSizeMake(1.0, 1.0)],
-
+
bezelBorderLeftImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorderLeft.png"] size:CGSizeMake(7.0, 1.0)],
bezerBorderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorder.png"] size:CGSizeMake(1.0, 1.0)],
bezelBorderRightImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorderRight.png"] size:CGSizeMake(7.0, 1.0)];
-
+
CPTabViewBezelBorderBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
[
- emptyImage,
- emptyImage,
+ emptyImage,
+ emptyImage,
emptyImage,
bezelBorderLeftImage,
@@ -128,7 +128,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
bezerBorderImage,
bezelBorderRightImage
]]];
-
+
CPTabViewBezelBorderColor = [CPColor colorWithPatternImage:bezerBorderImage];
}
@@ -143,13 +143,13 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
-
+
if (self)
{
_tabViewType = CPTopTabsBezelBorder;
_tabViewItems = [];
}
-
+
return self;
}
@@ -157,7 +157,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
if (_tabViewType != CPTopTabsBezelBorder || _labelsView)
return;
-
+
[self _createBezelBorder];
[self layoutSubviews];
}
@@ -166,7 +166,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
- (void)_createBezelBorder
{
var bounds = [self bounds];
-
+
_labelsView = [[_CPTabLabelsView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), 0.0)];
[_labelsView setTabView:self];
@@ -174,19 +174,19 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
[self addSubview:_labelsView];
- _backgroundView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
-
+ _backgroundView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
+
[_backgroundView setBackgroundColor:CPTabViewBezelBorderBackgroundColor];
[_backgroundView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
-
+
[self addSubview:_backgroundView];
-
+
_separatorView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
[_separatorView setBackgroundColor:[[self class] bezelBorderColor]];
[_separatorView setAutoresizingMask:CPViewWidthSizable | CPViewMaxYMargin];
-
+
[self addSubview:_separatorView];
}
@@ -200,21 +200,21 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
var backgroundRect = [self bounds],
labelsViewHeight = [_CPTabLabelsView height];
-
+
backgroundRect.origin.y += labelsViewHeight;
backgroundRect.size.height -= labelsViewHeight;
-
+
[_backgroundView setFrame:backgroundRect];
-
+
var auxiliaryViewHeight = 5.0;
-
+
if (_auxiliaryView)
{
auxiliaryViewHeight = CGRectGetHeight([_auxiliaryView frame]);
-
+
[_auxiliaryView setFrame:CGRectMake(LEFT_INSET, labelsViewHeight, CGRectGetWidth(backgroundRect) - LEFT_INSET - RIGHT_INSET, auxiliaryViewHeight)];
}
-
+
[_separatorView setFrame:CGRectMake(LEFT_INSET, labelsViewHeight + auxiliaryViewHeight, CGRectGetWidth(backgroundRect) - LEFT_INSET - RIGHT_INSET, 1.0)];
}
@@ -240,15 +240,15 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
*/
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex
{
- if (!_labelsView && _tabViewType == CPTopTabsBezelBorder)
+ if (!_labelsView)
[self _createBezelBorder];
-
+
[_tabViewItems insertObject:aTabViewItem atIndex:anIndex];
-
+
[_labelsView tabView:self didAddTabViewItem:aTabViewItem];
-
+
[aTabViewItem _setTabView:self];
-
+
if ([_tabViewItems count] == 1)
[self selectFirstTabViewItem:self];
@@ -265,11 +265,11 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
var index = [self indexOfTabViewItem:aTabViewItem];
[_tabViewItems removeObjectIdenticalTo:aTabViewItem];
-
+
[_labelsView tabView:self didRemoveTabViewItemAtIndex:index];
-
+
[aTabViewItem _setTabView:nil];
-
+
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
}
@@ -292,7 +292,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
var index = 0,
count = [_tabViewItems count];
-
+
for (; index < count; ++index)
if ([[_tabViewItems[index] identifier] isEqual:anIdentifier])
return index;
@@ -332,7 +332,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
- (void)selectFirstTabViewItem:(id)aSender
{
var count = [_tabViewItems count];
-
+
if (count)
[self selectTabViewItemAtIndex:0];
}
@@ -344,7 +344,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
- (void)selectLastTabViewItem:(id)aSender
{
var count = [_tabViewItems count];
-
+
if (count)
[self selectTabViewItemAtIndex:count - 1];
}
@@ -357,10 +357,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
if (!_selectedTabViewItem)
return;
-
+
var index = [self indexOfTabViewItem:_selectedTabViewItem],
count = [_tabViewItems count];
-
+
[self selectTabViewItemAtIndex:index + 1 % count];
}
@@ -372,10 +372,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
if (!_selectedTabViewItem)
return;
-
+
var index = [self indexOfTabViewItem:_selectedTabViewItem],
count = [_tabViewItems count];
-
+
[self selectTabViewItemAtIndex:index == 0 ? count : index - 1];
}
@@ -387,7 +387,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
return;
-
+
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
@@ -395,29 +395,35 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
_selectedTabViewItem._tabState = CPBackgroundTab;
[_labelsView tabView:self didChangeStateOfTabViewItem:_selectedTabViewItem];
-
- [_contentView removeFromSuperview];
- [_auxiliaryView removeFromSuperview];
}
_selectedTabViewItem = aTabViewItem;
-
- _selectedTabViewItem._tabState = CPSelectedTab;
-
- _contentView = [_selectedTabViewItem view];
- [_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
-
- _auxiliaryView = [_selectedTabViewItem auxiliaryView];
- [_auxiliaryView setAutoresizingMask:CPViewWidthSizable];
-
- [self addSubview:_contentView];
- if (_auxiliaryView)
+ _selectedTabViewItem._tabState = CPSelectedTab;
+
+ var _previousContentView = _contentView;
+ _contentView = [_selectedTabViewItem view];
+
+ if (_previousContentView !== _contentView)
+ {
+ [_previousContentView removeFromSuperview];
+ [_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
+ [self addSubview:_contentView];
+ }
+
+ var _previousAuxiliaryView = _auxiliaryView;
+ _auxiliaryView = [_selectedTabViewItem auxiliaryView];
+
+ if (_previousAuxiliaryView !== _auxiliaryView)
+ {
+ [_previousAuxiliaryView removeFromSuperview];
+ [_auxiliaryView setAutoresizingMask:CPViewWidthSizable];
[self addSubview:_auxiliaryView];
-
+ }
+
[_labelsView tabView:self didChangeStateOfTabViewItem:_selectedTabViewItem];
-
+
[self layoutSubviews];
-
+
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
}
@@ -439,7 +445,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
return _selectedTabViewItem;
}
-//
+//
/*!
Sets the tab view type.
@param aTabViewType the view type
@@ -448,19 +454,19 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
if (_tabViewType == aTabViewType)
return;
-
+
_tabViewType = aTabViewType;
-
+
if (_tabViewType == CPNoTabsBezelBorder || _tabViewType == CPNoTabsLineBorder || _tabViewType == CPNoTabsNoBorder)
[_labelsView removeFromSuperview];
- else if (![_labelsView superview])
+ else if (_labelsView && ![_labelsView superview])
[self addSubview:_labelsView];
-
+
if (_tabViewType == CPNoTabsLineBorder || _tabViewType == CPNoTabsNoBorder)
[_backgroundView removeFromSuperview];
- else if (![_backgroundView superview])
+ else if (_backgroundView && ![_backgroundView superview])
[self addSubview:_backgroundView];
-
+
[self layoutSubviews];
}
@@ -479,7 +485,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
- (CGRect)contentRect
{
var contentRect = CGRectMakeCopy([self bounds]);
-
+
if (_tabViewType == CPTopTabsBezelBorder)
{
var labelsViewHeight = [_CPTabLabelsView height],
@@ -488,7 +494,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
contentRect.origin.y += labelsViewHeight + auxiliaryViewHeight + separatorViewHeight;
contentRect.size.height -= labelsViewHeight + auxiliaryViewHeight + separatorViewHeight * 2.0; // 2 for the bottom border as well.
-
+
contentRect.origin.x += LEFT_INSET;
contentRect.size.width -= LEFT_INSET + RIGHT_INSET;
}
@@ -512,9 +518,9 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
if (_delegate == aDelegate)
return;
-
+
_delegate = aDelegate;
-
+
_delegateSelectors = 0;
if ([_delegate respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)])
@@ -527,7 +533,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
_delegateSelectors |= CPTabViewDidSelectTabViewItemSelector;
if ([_delegate respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)])
- _delegateSelectors |= CPTabViewDidChangeNumberOfTabViewItemsSelector;
+ _delegateSelectors |= CPTabViewDidChangeNumberOfTabViewItemsSelector;
}
//
@@ -536,7 +542,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
{
var location = [_labelsView convertPoint:[anEvent locationInWindow] fromView:nil],
tabViewItem = [_labelsView representedTabViewItemAtPoint:location];
-
+
if (tabViewItem)
[self selectTabViewItem:tabViewItem];
}
@@ -556,18 +562,18 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
{
_tabViewType = [aCoder decodeIntForKey:CPTabViewTypeKey];
_tabViewItems = [];
-
+
// FIXME: this is somewhat hacky
[self _createBezelBorder];
-
+
var items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
for (var i = 0; items && i < items.length; i++)
[self insertTabViewItem:items[i] atIndex:i];
-
+
var selected = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
if (selected)
[self selectTabViewItem:selected];
-
+
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
}
@@ -580,12 +586,12 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
_subviews = [];
[super encodeWithCoder:aCoder];
_subviews = actualSubviews;
-
+
[aCoder encodeObject:_tabViewItems forKey:CPTabViewItemsKey];;
[aCoder encodeObject:_selectedTabViewItem forKey:CPTabViewSelectedItemKey];
-
+
[aCoder encodeInt:_tabViewType forKey:CPTabViewTypeKey];
-
+
[aCoder encodeConditionalObject:_delegate forKey:CPTabViewDelegateKey];
}
@@ -609,7 +615,7 @@ var _CPTabLabelsViewBackgroundColor = nil,
return;
var bundle = [CPBundle bundleForClass:self];
-
+
_CPTabLabelsViewBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
[
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelsViewLeft.png"] size:CGSizeMake(12.0, 26.0)],
@@ -626,16 +632,16 @@ var _CPTabLabelsViewBackgroundColor = nil,
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
-
+
if (self)
{
_tabLabels = [];
-
+
[self setBackgroundColor:_CPTabLabelsViewBackgroundColor];
[self setFrameSize:CGSizeMake(CGRectGetWidth(aFrame), 26.0)];
}
-
+
return self;
}
@@ -652,24 +658,24 @@ var _CPTabLabelsViewBackgroundColor = nil,
- (void)tabView:(CPTabView)aTabView didAddTabViewItem:(CPTabViewItem)aTabViewItem
{
var label = [[_CPTabLabel alloc] initWithFrame:CGRectMakeZero()];
-
+
[label setTabViewItem:aTabViewItem];
-
+
_tabLabels.push(label);
-
+
[self addSubview:label];
-
+
[self layoutSubviews];
}
- (void)tabView:(CPTabView)aTabView didRemoveTabViewItemAtIndex:(unsigned)index
{
var label = _tabLabels[index];
-
+
[_tabLabels removeObjectAtIndex:index];
[label removeFromSuperview];
-
+
[self layoutSubviews];
}
@@ -682,11 +688,11 @@ var _CPTabLabelsViewBackgroundColor = nil,
{
var index = 0,
count = _tabLabels.length;
-
+
for (; index < count; ++index)
{
var label = _tabLabels[index];
-
+
if (CGRectContainsPoint([label frame], aPoint))
return [label tabViewItem];
}
@@ -700,14 +706,14 @@ var _CPTabLabelsViewBackgroundColor = nil,
count = _tabLabels.length,
width = (_CGRectGetWidth([self bounds]) - (count - 1) * _CPTabLabelsViewInsideMargin - 2 * _CPTabLabelsViewOutsideMargin) / count,
x = _CPTabLabelsViewOutsideMargin;
-
+
for (; index < count; ++index)
{
var label = _tabLabels[index],
frame = _CGRectMake(x, 8.0, width, 18.0);
-
+
[label setFrame:frame];
-
+
x = _CGRectGetMaxX(frame) + _CPTabLabelsViewInsideMargin;
}
}
@@ -716,9 +722,9 @@ var _CPTabLabelsViewBackgroundColor = nil,
{
if (CGSizeEqualToSize([self frame].size, aSize))
return;
-
+
[super setFrameSize:aSize];
-
+
[self layoutSubviews];
}
@@ -740,14 +746,14 @@ var _CPTabLabelBackgroundColor = nil,
return;
var bundle = [CPBundle bundleForClass:self];
-
+
_CPTabLabelBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
[
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundLeft.png"] size:CGSizeMake(6.0, 18.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundCenter.png"] size:CGSizeMake(1.0, 18.0)],
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundRight.png"] size:CGSizeMake(6.0, 18.0)]
] isVertical:NO]];
-
+
_CPTabLabelSelectedBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
[
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelSelectedLeft.png"] size:CGSizeMake(3.0, 18.0)],
@@ -759,21 +765,21 @@ var _CPTabLabelBackgroundColor = nil,
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
-
+
if (self)
- {
+ {
_labelField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
-
+
[_labelField setAlignment:CPCenterTextAlignment];
[_labelField setFrame:CGRectMake(5.0, 0.0, CGRectGetWidth(aFrame) - 10.0, 20.0)];
[_labelField setAutoresizingMask:CPViewWidthSizable];
[_labelField setFont:[CPFont boldSystemFontOfSize:11.0]];
-
+
[self addSubview:_labelField];
-
+
[self setTabState:CPBackgroundTab];
}
-
+
return self;
}
@@ -785,7 +791,7 @@ var _CPTabLabelBackgroundColor = nil,
- (void)setTabViewItem:(CPTabViewItem)aTabViewItem
{
_tabViewItem = aTabViewItem;
-
+
[self update];
}
diff --git a/AppKit/CPTableColumn.j b/AppKit/CPTableColumn.j
index 91edc5d50..28808a2b5 100644
--- a/AppKit/CPTableColumn.j
+++ b/AppKit/CPTableColumn.j
@@ -78,18 +78,7 @@ CPTableColumnUserResizingMask = 1 << 1;
var header = [[_CPTableColumnHeaderView alloc] initWithFrame:CGRectMakeZero()];
[self setHeaderView:header];
- var textDataView = [CPTextField new];
-
- [textDataView setValue:[CPColor colorWithRed:51.0 / 255.0 green:51.0 / 255.0 blue:51.0 / 255.0 alpha:1.0]
- forThemeAttribute:"text-color"];
-
- [textDataView setValue:[CPColor whiteColor] forThemeAttribute:@"text-color" inState:CPThemeStateSelectedDataView];
- [textDataView setLineBreakMode:CPLineBreakByTruncatingTail];
- [textDataView setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateSelectedDataView];
- [textDataView setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
- [textDataView setValue:CGInsetMake(0.0, 0.0, 0.0, 5.0) forThemeAttribute:@"content-inset"];
-
- [self setDataView:textDataView];
+ [self setDataView:[CPTextField new]];
}
return self;
@@ -237,12 +226,12 @@ CPTableColumnUserResizingMask = 1 << 1;
/*!
This method set's the "prototype" view which will be used to create all table cells in this column.
-
- It creates a snapshot of aView, using keyed archiving, which is then copied over and over for each
+
+ It creates a snapshot of aView, using keyed archiving, which is then copied over and over for each
individual cell that is shown. As a result, changes made after calling this method won't be reflected.
Example:
-
+
[tableColumn setDataView:someView]; // snapshot taken
[[tableColumn dataView] setSomething:x]; //won't work
@@ -259,6 +248,8 @@ CPTableColumnUserResizingMask = 1 << 1;
if (_dataView)
_dataViewData[[_dataView UID]] = nil;
+ [aView setThemeState:CPThemeStateTableDataView];
+
_dataView = aView;
_dataViewData[[aView UID]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
}
@@ -298,6 +289,9 @@ CPTableColumnUserResizingMask = 1 << 1;
var newDataView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData[dataViewUID]];
newDataView.identifier = dataViewUID;
+ // make sure only we have control over the size and placement
+ [newDataView setAutoresizingMask:CPViewNotSizable];
+
return newDataView;
}
@@ -356,9 +350,9 @@ CPTableColumnUserResizingMask = 1 << 1;
shouldBeHidden = !!shouldBeHidden
if (_isHidden === shouldBeHidden)
return;
-
+
_isHidden = shouldBeHidden;
-
+
[[self headerView] setHidden:shouldBeHidden];
[[self tableView] _tableColumnVisibilityDidChange:self];
}
@@ -413,7 +407,8 @@ CPTableColumnUserResizingMask = 1 << 1;
{
var bindingName = keys[i],
bindingPath = [aDataView _replacementKeyPathForBinding:bindingName],
- bindingInfo = [bindingsDictionary objectForKey:bindingName]._info,
+ binding = [bindingsDictionary objectForKey:bindingName],
+ bindingInfo = binding._info,
destination = [bindingInfo objectForKey:CPObservedObjectKey],
keyPath = [bindingInfo objectForKey:CPObservedKeyPathKey],
dotIndex = keyPath.lastIndexOf("."),
@@ -444,6 +439,8 @@ CPTableColumnUserResizingMask = 1 << 1;
value = [[firstValue valueForKeyPath:secondPart] objectAtIndex:aRow];
}
+ value = [binding transformValue:value withOptions:[bindingInfo objectForKey:CPOptionsKey]];
+
// console.log(bindingName+" : "+keyPath+" : "+aRow+" : "+[[destination valueForKeyPath:keyPath] objectAtIndex:aRow]);
[aDataView setValue:value forKey:bindingPath];
}
@@ -468,9 +465,9 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
CPTableColumnMinWidthKey = @"CPTableColumnMinWidthKey",
CPTableColumnMaxWidthKey = @"CPTableColumnMaxWidthKey",
CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey",
- CPTableColumnIsHiddenkey = @"CPTableColumnIsHiddenKey",
- CPSortDescriptorPrototypeKey = @"CPSortDescriptorPrototypeKey";
- CPTableColumnIsHiddenkey = @"CPTableColumnIsHiddenKey";
+ CPTableColumnIsHiddenKey = @"CPTableColumnIsHiddenKey",
+ CPSortDescriptorPrototypeKey = @"CPSortDescriptorPrototypeKey",
+ CPTableColumnIsEditableKey = @"CPTableColumnIsEditableKey";
@implementation CPTableColumn (CPCoding)
@@ -491,9 +488,10 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
[self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
- _resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
- _isHidden = [aCoder decodeBoolForKey:CPTableColumnIsHiddenkey];
-
+ _resizingMask = [aCoder decodeIntForKey:CPTableColumnResizingMaskKey];
+ _isHidden = [aCoder decodeBoolForKey:CPTableColumnIsHiddenKey];
+ _isEditable = [aCoder decodeBoolForKey:CPTableColumnIsEditableKey];
+
_sortDescriptorPrototype = [aCoder decodeObjectForKey:CPSortDescriptorPrototypeKey];
}
@@ -512,8 +510,9 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
[aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
[aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
- [aCoder encodeBool:_isHidden forKey:CPTableColumnIsHiddenkey];
-
+ [aCoder encodeBool:_isHidden forKey:CPTableColumnIsHiddenKey];
+ [aCoder encodeBool:_isEditable forKey:CPTableColumnIsEditableKey];
+
[aCoder encodeObject:_sortDescriptorPrototype forKey:CPSortDescriptorPrototypeKey];
}
diff --git a/AppKit/CPTableHeaderView.j b/AppKit/CPTableHeaderView.j
index 76f71f426..5656e83b7 100644
--- a/AppKit/CPTableHeaderView.j
+++ b/AppKit/CPTableHeaderView.j
@@ -23,53 +23,60 @@
@import "CPTableColumn.j"
@import "CPTableView.j"
@import "CPView.j"
-
+
+#include "CoreGraphics/CGGeometry.h"
+
@implementation _CPTableColumnHeaderView : CPView
{
_CPImageAndTextView _textField;
}
++ (CPString)themeClass
+{
+ return @"columnHeader";
+}
+
++ (id)themeAttributes
+{
+ return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null], CGInsetMakeZero(), [CPNull null], [CPNull null], [CPNull null], CGSizeMakeZero()]
+ forKeys:[@"background-color", @"text-alignment", @"text-inset", @"text-color", @"text-font", @"text-shadow-color", @"text-shadow-offset"]];
+}
+
- (void)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
- {
[self _init];
- }
return self;
}
- (void)_init
{
- _textField = [[_CPImageAndTextView alloc] initWithFrame:
- CGRectMake(5.0, 0.0, CGRectGetWidth([self bounds]) - 10.0, CGRectGetHeight([self bounds]))];
-
+ _textField = [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
+
[_textField setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
[_textField setLineBreakMode:CPLineBreakByTruncatingTail];
- [_textField setTextColor:[CPColor colorWithRed:51.0 / 255.0 green:51.0 / 255.0 blue:51.0 / 255.0 alpha:1.0]];
- [_textField setFont:[CPFont boldSystemFontOfSize:12.0]];
[_textField setAlignment:CPLeftTextAlignment];
[_textField setVerticalAlignment:CPCenterVerticalTextAlignment];
- [_textField setTextShadowColor:[CPColor whiteColor]];
- [_textField setTextShadowOffset:CGSizeMake(0,1)];
[self addSubview:_textField];
}
- (void)layoutSubviews
{
- var themeState = [self themeState];
+ [self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
- if(themeState & CPThemeStateSelected && themeState & CPThemeStateHighlighted)
- [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview-highlighted-pressed.png", CGSizeMake(1.0, 23.0))]];
- else if (themeState & CPThemeStateSelected)
- [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview-highlighted.png", CGSizeMake(1.0, 23.0))]];
- else if (themeState & CPThemeStateHighlighted)
- [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview-pressed.png", CGSizeMake(1.0, 23.0))]];
- else
- [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview.png", CGSizeMake(1.0, 23.0))]];
+ var inset = [self currentValueForThemeAttribute:@"text-inset"],
+ bounds = [self bounds];
+
+ [_textField setFrame:_CGRectMake(inset.right, inset.top, bounds.size.width - inset.right - inset.left, bounds.size.height - inset.top - inset.bottom)];
+ [_textField setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
+ [_textField setFont:[self currentValueForThemeAttribute:@"text-font"]];
+ [_textField setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
+ [_textField setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
+ [_textField setAlignment:[self currentValueForThemeAttribute:@"text-alignment"]];
}
- (void)setStringValue:(CPString)string
@@ -97,11 +104,6 @@
[_textField setFont:aFont];
}
-- (void)setValue:(id)aValue forThemeAttribute:(id)aKey
-{
- [_textField setValue:aValue forThemeAttribute:aKey];
-}
-
- (void)_setIndicatorImage:(CPImage)anImage
{
if (anImage)
@@ -149,34 +151,47 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
@implementation CPTableHeaderView : CPView
{
- CPPoint _mouseDownLocation;
- CPPoint _previousTrackingLocation;
+ CGPoint _mouseDownLocation;
+ CGPoint _previousTrackingLocation;
int _activeColumn;
int _pressedColumn;
BOOL _isResizing;
BOOL _isDragging;
BOOL _isTrackingColumn;
+ BOOL _drawsColumnLines;
float _columnOldWidth;
CPTableView _tableView @accessors(property=tableView);
}
++ (CPString)themeClass
+{
+ return @"tableHeaderRow";
+}
+
++ (id)themeAttributes
+{
+ return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPColor grayColor]]
+ forKeys:[@"background-color", @"divider-color"]];
+}
+
- (void)_init
{
- _mouseDownLocation = CPPointMakeZero();
- _previousTrackingLocation = CPPointMakeZero();
+ _mouseDownLocation = _CGPointMakeZero();
+ _previousTrackingLocation = _CGPointMakeZero();
_activeColumn = -1;
_pressedColumn = -1;
_isResizing = NO;
_isDragging = NO;
_isTrackingColumn = NO;
+ _drawsColumnLines = YES;
_columnOldWidth = 0.0;
- [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview.png", CGSizeMake(1.0, 23.0))]];
+ [self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
}
- (id)initWithFrame:(CGRect)aFrame
@@ -199,23 +214,33 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
var headerRect = [self bounds],
columnRect = [_tableView rectOfColumn:aColumnIndex];
- headerRect.origin.x = CPRectGetMinX(columnRect);
- headerRect.size.width = CPRectGetWidth(columnRect);
+ headerRect.origin.x = _CGRectGetMinX(columnRect);
+ headerRect.size.width = _CGRectGetWidth(columnRect);
return headerRect;
}
+- (void)setDrawsColumnLines:(BOOL)aFlag
+{
+ _drawsColumnLines = aFlag;
+}
+
+- (BOOL)drawsColumnLines
+{
+ return _drawsColumnLines;
+}
+
- (CGRect)_cursorRectForColumn:(int)column
{
if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
- return CGRectMakeZero();
+ return _CGRectMakeZero();
var rect = [self headerRectOfColumn:column];
- rect.origin.x = CGRectGetMaxX(rect) - 5;
+ rect.origin.x = _CGRectGetMaxX(rect) - 5;
rect.size.width = 20;
- return rect;
+ return rect;
}
- (void)_setPressedColumn:(CPInteger)column
@@ -224,7 +249,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
{
var headerView = [_tableView._tableColumns[_pressedColumn] headerView];
[headerView unsetThemeState:CPThemeStateHighlighted];
- }
+ }
if (column != -1)
{
@@ -249,7 +274,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
currentLocation.x -= 5.0;
var columnIndex = [self columnAtPoint:currentLocation],
- shouldResize = [self shouldResizeTableColumn:columnIndex at:CPPointMake(currentLocation.x + 5.0, currentLocation.y)];
+ shouldResize = [self shouldResizeTableColumn:columnIndex at:_CGPointMake(currentLocation.x + 5.0, currentLocation.y)];
if (type === CPLeftMouseUp)
{
@@ -293,7 +318,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
[self continueResizingTableColumn:_activeColumn at:currentLocation];
else
{
- if (_activeColumn === columnIndex && CPRectContainsPoint([self headerRectOfColumn:columnIndex], currentLocation))
+ if (_activeColumn === columnIndex && _CGRectContainsPoint([self headerRectOfColumn:columnIndex], currentLocation))
{
if (_isTrackingColumn && _pressedColumn !== -1)
{
@@ -311,24 +336,24 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
[CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
}
-- (void)startTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+- (void)startTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
[self _setPressedColumn:aColumnIndex];
}
-- (BOOL)continueTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+- (BOOL)continueTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
if ([self _shouldDragTableColumn:aColumnIndex at:aPoint])
{
var columnRect = [self headerRectOfColumn:aColumnIndex],
- offset = CPPointMakeZero(),
+ offset = _CGPointMakeZero(),
view = [_tableView _dragViewForColumn:aColumnIndex event:[CPApp currentEvent] offset:offset],
- viewLocation = CPPointMakeZero();
+ viewLocation = _CGPointMakeZero();
- viewLocation.x = ( CPRectGetMinX(columnRect) + offset.x ) + ( aPoint.x - _mouseDownLocation.x );
- viewLocation.y = CPRectGetMinY(columnRect) + offset.y;
+ viewLocation.x = ( _CGRectGetMinX(columnRect) + offset.x ) + ( aPoint.x - _mouseDownLocation.x );
+ viewLocation.y = _CGRectGetMinY(columnRect) + offset.y;
- [self dragView:view at:viewLocation offset:CPSizeMakeZero() event:[CPApp currentEvent]
+ [self dragView:view at:viewLocation offset:_CGSizeMakeZero() event:[CPApp currentEvent]
pasteboard:[CPPasteboard pasteboardWithName:CPDragPboard] source:self slideBack:YES];
return NO;
@@ -337,24 +362,24 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
return YES;
}
-- (BOOL)_shouldStopTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+- (BOOL)_shouldStopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
- return _isTrackingColumn && _activeColumn === aColumnIndex &&
- CPRectContainsPoint([self headerRectOfColumn:aColumnIndex], aPoint);
+ return _isTrackingColumn && _activeColumn === aColumnIndex &&
+ _CGRectContainsPoint([self headerRectOfColumn:aColumnIndex], aPoint);
}
-- (void)stopTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+- (void)stopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
[self _setPressedColumn:CPNotFound];
[self _updateResizeCursor:[CPApp currentEvent]];
}
-- (BOOL)_shouldDragTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+- (BOOL)_shouldDragTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
return [_tableView allowsColumnReordering] && ABS(aPoint.x - _mouseDownLocation.x) >= 10.0;
}
-- (CPRect)_headerRectOfLastVisibleColumn
+- (CGRect)_headerRectOfLastVisibleColumn
{
var tableColumns = [_tableView tableColumns],
columnIndex = [tableColumns count];
@@ -370,11 +395,11 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
return nil;
}
-- (void)_constrainDragView:(CPView)theDragView at:(CPPoint)aPoint
+- (void)_constrainDragView:(CPView)theDragView at:(CGPoint)aPoint
{
var tableColumns = [_tableView tableColumns],
- lastColumnRect = [self _headerRectOfLastVisibleColumn];
- activeColumnRect = [self headerRectOfColumn:_activeColumn];
+ lastColumnRect = [self _headerRectOfLastVisibleColumn],
+ activeColumnRect = [self headerRectOfColumn:_activeColumn],
dragWindow = [theDragView window],
frame = [dragWindow frame];
@@ -384,10 +409,10 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
frame.origin = [self convertPoint:frame.origin fromView:nil];
// This effectively clamps the value between the minimum and maximum
- frame.origin.x = MAX(0.0, MIN(CGRectGetMinX(frame), CGRectGetMaxX(lastColumnRect) - CGRectGetWidth(activeColumnRect)));
+ frame.origin.x = MAX(0.0, MIN(_CGRectGetMinX(frame), _CGRectGetMaxX(lastColumnRect) - _CGRectGetWidth(activeColumnRect)));
// Make sure the column cannot move vertically
- frame.origin.y = CPRectGetMinY(lastColumnRect);
+ frame.origin.y = _CGRectGetMinY(lastColumnRect);
// Convert the calculated origin back to the window coordinate system
frame.origin = [self convertPoint:frame.origin toView:nil];
@@ -402,21 +427,21 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
[_tableView moveColumn:aFromIndex toColumn:aToIndex];
_activeColumn = aToIndex;
_pressedColumn = _activeColumn;
-
- [_tableView _setDraggedColumn:_activeColumn];
}
-- (void)draggedView:(CPView)aView beganAt:(CPPoint)aPoint
+- (void)draggedView:(CPView)aView beganAt:(CGPoint)aPoint
{
_isDragging = YES;
- [[[[_tableView tableColumns] objectAtIndex:_activeColumn] headerView] setHidden:YES];
- [_tableView _setDraggedColumn:_activeColumn];
+ var column = [[_tableView tableColumns] objectAtIndex:_activeColumn];
+
+ [[column headerView] setHidden:YES];
+ [_tableView _setDraggedColumn:column];
[self setNeedsDisplay:YES];
}
-- (void)draggedView:(CPView)aView movedTo:(CPPoint)aPoint
+- (void)draggedView:(CPView)aView movedTo:(CGPoint)aPoint
{
[self _constrainDragView:aView at:aPoint];
@@ -426,9 +451,9 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
var hoverPoint = CGPointCreateCopy(aPoint);
if (aPoint.x < _previousTrackingLocation.x)
- hoverPoint = CGPointMake(CGRectGetMinX(dragWindowFrame), CGRectGetMinY(dragWindowFrame));
+ hoverPoint = _CGPointMake(_CGRectGetMinX(dragWindowFrame), _CGRectGetMinY(dragWindowFrame));
else if (aPoint.x > _previousTrackingLocation.x)
- hoverPoint = CGPointMake(CGRectGetMaxX(dragWindowFrame), CGRectGetMinY(dragWindowFrame));
+ hoverPoint = _CGPointMake(_CGRectGetMaxX(dragWindowFrame), _CGRectGetMinY(dragWindowFrame));
// Convert the hover point from the global coordinate system to windows' coordinate system
hoverPoint = [[self window] convertGlobalToBase:hoverPoint];
@@ -440,7 +465,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
if (hoveredColumn !== -1)
{
var columnRect = [self headerRectOfColumn:hoveredColumn],
- columnCenterPoint = [self convertPoint:CGPointMake(CGRectGetMidX(columnRect), CGRectGetMidY(columnRect)) fromView:self];
+ columnCenterPoint = [self convertPoint:CGPointMake(_CGRectGetMidX(columnRect), _CGRectGetMidY(columnRect)) fromView:self];
if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
@@ -455,14 +480,14 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
_isDragging = NO;
_isTrackingColumn = NO; // We need to do this explicitly because the mouse up section of trackMouse is never reached
- [_tableView _setDraggedColumn:-1];
+ [_tableView _setDraggedColumn:nil];
[[[[_tableView tableColumns] objectAtIndex:_activeColumn] headerView] setHidden:NO];
[self stopTrackingTableColumn:_activeColumn at:aLocation];
[self setNeedsDisplay:YES];
}
-- (BOOL)shouldResizeTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+- (BOOL)shouldResizeTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
if (_isResizing)
return YES;
@@ -470,10 +495,10 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
if (_isTrackingColumn)
return NO;
- return [_tableView allowsColumnResizing] && CPRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
+ return [_tableView allowsColumnResizing] && _CGRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
}
-- (void)startResizingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+- (void)startResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
_isResizing = YES;
@@ -483,7 +508,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
[_tableView setDisableAutomaticResizing:YES];
}
-- (void)continueResizingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+- (void)continueResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
newWidth = [tableColumn width] + aPoint.x - _previousTrackingLocation.x;
@@ -503,7 +528,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
}
}
-- (void)stopResizingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+- (void)stopResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
[tableColumn _postDidResizeNotificationWithOldWidth:_columnOldWidth];
@@ -522,11 +547,11 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
return;
}
- var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
+ var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
mouseOverLocation = CGPointMake(mouseLocation.x - 5, mouseLocation.y),
overColumn = [self columnAtPoint:mouseOverLocation];
- if (overColumn >= 0 && CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
+ if (overColumn >= 0 && _CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
{
var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
width = [tableColumn width];
@@ -543,7 +568,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
}
- (void)mouseEntered:(CPEvent)theEvent
-{
+{
[self _updateResizeCursor:theEvent];
}
@@ -561,16 +586,16 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
- (void)layoutSubviews
{
var tableColumns = [_tableView tableColumns],
- count = [tableColumns count];
+ count = [tableColumns count];
- for (var i = 0; i < count; i++)
+ for (var i = 0; i < count; i++)
{
var column = [tableColumns objectAtIndex:i],
headerView = [column headerView];
var frame = [self headerRectOfColumn:i];
frame.size.height -= 0.5;
- if (i > 0)
+ if (i > 0)
{
frame.origin.x += 0.5;
frame.size.width -= 1;
@@ -581,11 +606,13 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
if([headerView superview] != self)
[self addSubview:headerView];
}
+
+ [self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
}
- (void)drawRect:(CGRect)aRect
{
- if (!_tableView)
+ if (!_tableView || ![self drawsColumnLines])
return;
var context = [[CPGraphicsContext currentContext] graphicsPort],
@@ -597,7 +624,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
exposedRange = CPMakeRange(firstIndex, [exposedTableColumns lastIndex] - firstIndex + 1);
CGContextSetLineWidth(context, 1);
- CGContextSetStrokeColor(context, [_tableView gridColor]);
+ CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"divider-color"]);
[exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:exposedRange];
@@ -612,25 +639,26 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
var columnIndex = columnsArray[columnArrayIndex],
columnToStroke = [self headerRectOfColumn:columnIndex];
- columnMaxX = CGRectGetMaxX(columnToStroke);
+ columnMaxX = _CGRectGetMaxX(columnToStroke);
- CGContextMoveToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(CGRectGetMinY(columnToStroke)));
- CGContextAddLineToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(CGRectGetMaxY(columnToStroke)));
+ CGContextMoveToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMinY(columnToStroke)));
+ CGContextAddLineToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMaxY(columnToStroke)));
}
CGContextClosePath(context);
CGContextStrokePath(context);
- if (_isDragging)
+ /*if (_isDragging)
{
CGContextSetFillColor(context, [CPColor grayColor]);
CGContextFillRect(context, [self headerRectOfColumn:_activeColumn])
- }
+ }*/
}
@end
-var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
+var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey",
+ CPTableHeaderViewDrawsColumnLines = @"CPTableHeaderViewDrawsColumnLines";
@implementation CPTableHeaderView (CPCoding)
@@ -640,6 +668,7 @@ var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
{
[self _init];
_tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
+ _drawsColumnLines = [aCoder decodeBoolForKey:CPTableHeaderViewDrawsColumnLines];
}
return self;
@@ -649,6 +678,7 @@ var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:_tableView forKey:CPTableHeaderViewTableViewKey];
+ [aCoder encodeBool:_drawsColumnLines forKey:CPTableHeaderViewDrawsColumnLines];
}
@end
\ No newline at end of file
diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j
index c8bac1aae..9dd6ba280 100644
--- a/AppKit/CPTableView.j
+++ b/AppKit/CPTableView.j
@@ -165,6 +165,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
BOOL _allowsEmptySelection;
CPArray _sortDescriptors;
+
//Setting Display Attributes
CGSize _intercellSpacing;
float _rowHeight;
@@ -174,9 +175,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
unsigned _selectionHighlightStyle;
CPTableColumn _currentHighlightedTableColumn;
- CPColor _selectionHighlightColor;
unsigned _gridStyleMask;
- CPColor _gridColor;
unsigned _numberOfRows;
@@ -214,18 +213,21 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
BOOL _disableAutomaticResizing @accessors(property=disableAutomaticResizing);
BOOL _lastColumnShouldSnap;
+ BOOL _implementsCustomDrawRow;
- CPGradient _sourceListActiveGradient;
- CPColor _sourceListActiveTopLineColor;
- CPColor _sourceListActiveBottomLineColor;
+ CPTableColumn _draggedColumn;
+ CPArray _differedColumnDataToRemove;
+}
- int _draggedColumnIndex;
++ (CPString)themeClass
+{
+ return @"tableview";
+}
-/*
- CPGradient _sourceListInactiveGradient;
- CPColor _sourceListInactiveTopLineColor;
- CPColor _sourceListInactiveBottomLineColor;
-*/
++ (id)themeAttributes
+{
+ return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null], [CPNull null], [CPNull null], [CPNull null], [CPNull null], [CPNull null]]
+ forKeys:["alternating-row-colors", "grid-color", "highlighted-grid-color", "selection-color", "sourcelist-selection-color", "sort-image", "sort-image-reversed"]];
}
- (id)initWithFrame:(CGRect)aFrame
@@ -254,7 +256,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
_dirtyTableColumnRangeIndex = CPNotFound;
_numberOfHiddenColumns = 0;
- _intercellSpacing = _CGSizeMake(0.0, 0.0);
+ _intercellSpacing = _CGSizeMake(3.0, 2.0);
_rowHeight = 23.0;
[self setGridColor:[CPColor colorWithHexString:@"dce0e2"]];
@@ -328,18 +330,15 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
if (!_cornerView)
_cornerView = [[_CPCornerView alloc] initWithFrame:CGRectMake(0, 0, [CPScroller scrollerWidth], CGRectGetHeight([_headerView frame]))];
- _draggedColumnIndex = -1;
-
- // Gradients for the source list
- _sourceListActiveGradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [89.0/255.0, 153.0/255.0, 209.0/255.0,1.0, 33.0/255.0, 94.0/255.0, 208.0/255.0,1.0], [0,1], 2);
- _sourceListActiveTopLineColor = [CPColor colorWithCalibratedRed:(61.0/255.0) green:(123.0/255.0) blue:(218.0/255.0) alpha:1.0];
- _sourceListActiveBottomLineColor = [CPColor colorWithCalibratedRed:(31.0/255.0) green:(92.0/255.0) blue:(207.0/255.0) alpha:1.0];
+ _draggedColumn = nil;
/* //gradients for the source list when CPTableView is NOT first responder or the window is NOT key
// FIX ME: we need to actually implement this.
_sourceListInactiveGradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [168.0/255.0,183.0/255.0,205.0/255.0,1.0,157.0/255.0,174.0/255.0,199.0/255.0,1.0], [0,1], 2);
_sourceListInactiveTopLineColor = [CPColor colorWithCalibratedRed:(173.0/255.0) green:(187.0/255.0) blue:(209.0/255.0) alpha:1.0];
_sourceListInactiveBottomLineColor = [CPColor colorWithCalibratedRed:(150.0/255.0) green:(161.0/255.0) blue:(183.0/255.0) alpha:1.0];*/
+ _differedColumnDataToRemove = [ ];
+ _implementsCustomDrawRow = [self implementsSelector:@selector(drawRow:clipRect:)];
}
/*!
@@ -536,7 +535,12 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
_intercellSpacing = _CGSizeMakeCopy(aSize);
+ _dirtyTableColumnRangeIndex = 0; // so that _recalculateTableColumnRanges will work
+ [self _recalculateTableColumnRanges];
+
[self setNeedsLayout];
+ [_headerView setNeedsDisplay:YES];
+ [_headerView setNeedsLayout];
}
- (void)setThemeState:(int)astae
@@ -586,17 +590,14 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
- (void)setAlternatingRowBackgroundColors:(CPArray)alternatingRowBackgroundColors
{
- if ([_alternatingRowBackgroundColors isEqual:alternatingRowBackgroundColors])
- return;
-
- _alternatingRowBackgroundColors = alternatingRowBackgroundColors;
+ [self setValue:alternatingRowBackgroundColors forThemeAttribute:"alternating-row-colors"];
[self setNeedsDisplay:YES];
}
- (CPArray)alternatingRowBackgroundColors
{
- return _alternatingRowBackgroundColors;
+ return [self currentValueForThemeAttribute:@"alternating-row-colors"];
}
- (unsigned)selectionHighlightStyle
@@ -625,10 +626,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (void)setSelectionHighlightColor:(CPColor)aColor
{
- if (aColor === _selectionHighlightColor)
- return;
+ [self setValue:aColor forThemeAttribute:"selection-color"];
- _selectionHighlightColor = aColor;
[self setNeedsDisplay:YES];
}
@@ -637,7 +636,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (CPColor)selectionHighlightColor
{
- return _selectionHighlightColor;
+ return [self currentValueForThemeAttribute:@"selection-color"];
}
/*!
@@ -650,13 +649,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (void)setSelectionGradientColors:(CPDictionary)aDictionary
{
- if ([aDictionary valueForKey:"CPSourceListGradient"] === _sourceListActiveGradient && [aDictionary valueForKey:"CPSourceListTopLineColor"] === _sourceListActiveTopLineColor && [aDictionary valueForKey:"CPSourceListBottomLineColor"] === _sourceListActiveBottomLineColor)
- return;
+ [self setValue:aDictionary forThemeAttribute:"sourcelist-selection-color"];
- _sourceListActiveGradient = [aDictionary valueForKey:CPSourceListGradient];
- _sourceListActiveTopLineColor = [aDictionary valueForKey:CPSourceListTopLineColor]
- _sourceListActiveBottomLineColor = [aDictionary valueForKey:CPSourceListBottomLineColor];
- [self setNeedsDisplay:YES]
+ [self setNeedsDisplay:YES];
}
/*!
@@ -667,7 +662,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (CPDictionary)selectionGradientColors
{
- return [CPDictionary dictionaryWithObjects:[_sourceListActiveGradient, _sourceListActiveTopLineColor, _sourceListActiveBottomLineColor] forKeys:[CPSourceListGradient, CPSourceListTopLineColor, CPSourceListBottomLineColor]];
+ return [self currentValueForThemeAttribute:@"sourcelist-selection-color"];
}
/*!
@@ -676,17 +671,14 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (void)setGridColor:(CPColor)aColor
{
- if (_gridColor === aColor)
- return;
-
- _gridColor = aColor;
+ [self setValue:aColor forThemeAttribute:"grid-color"];
[self setNeedsDisplay:YES];
}
- (CPColor)gridColor
{
- return _gridColor;
+ return [self currentValueForThemeAttribute:@"grid-color"];;
}
/*!
@@ -725,6 +717,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
else
_dirtyTableColumnRangeIndex = MIN(NUMBER_OF_COLUMNS() - 1, _dirtyTableColumnRangeIndex);
+ [self tile];
[self setNeedsLayout];
}
@@ -742,8 +735,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
if (index === CPNotFound)
return;
+ // we differ the actual removal until the end of the runloop in order to keep a reference to the column.
+ [_differedColumnDataToRemove addObject:{"column":aTableColumn, "shouldBeHidden": [aTableColumn isHidden]}];
+
+ [aTableColumn setHidden:YES];
[aTableColumn setTableView:nil];
- [_tableColumns removeObjectAtIndex:index];
var tableColumnUID = [aTableColumn UID];
@@ -758,14 +754,14 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[self setNeedsLayout];
}
-- (void)_setDraggedColumn:(int)aColumnIndex
+- (void)_setDraggedColumn:(CPTableColumn)aColumn
{
- if (_draggedColumnIndex === aColumnIndex)
+ if (_draggedColumn === aColumn)
return;
- _draggedColumnIndex = aColumnIndex;
+ _draggedColumn = aColumn;
- [self reloadDataForRowIndexes:_exposedRows columnIndexes:[CPIndexSet indexSetWithIndex:aColumnIndex]];
+ [self reloadDataForRowIndexes:_exposedRows columnIndexes:[CPIndexSet indexSetWithIndex:[_tableColumns indexOfObject:aColumn]]];
}
/*!
@@ -874,7 +870,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
_selectedColumnIndexes = [columns copy];
[self _updateHighlightWithOldColumns:previousSelectedIndexes newColumns:_selectedColumnIndexes];
- [_tableDrawView display]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected columns
+ [self setNeedsDisplay:YES]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected columns
// but currently -drawRect: is not implemented here
if (_headerView)
[_headerView setNeedsDisplay:YES];
@@ -882,6 +878,25 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[self _noteSelectionDidChange];
}
+- (void)_setSelectedRowIndexes:(CPIndexSet)rows
+{
+ if ([_selectedRowIndexes isEqualToIndexSet:rows])
+ return;
+
+ var previousSelectedIndexes = _selectedRowIndexes;
+
+ _lastSelectedRow = ([rows count] > 0) ? [rows lastIndex] : -1;
+ _selectedRowIndexes = [rows copy];
+
+ [self _updateHighlightWithOldRows:previousSelectedIndexes newRows:_selectedRowIndexes];
+ [self setNeedsDisplay:YES]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected rows
+ // but currently -drawRect: is not implemented here
+
+ [[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectedRowIndexes"];
+
+ [self _noteSelectionDidChange];
+}
+
/*!
Sets the row selection using indexes.
@param rows a CPIndexSet of rows to select
@@ -902,17 +917,16 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[_headerView setNeedsDisplay:YES];
}
- var previousSelectedIndexes = [_selectedRowIndexes copy];
-
+ var newSelectedIndexes;
if (shouldExtendSelection)
- [_selectedRowIndexes addIndexes:rows];
+ {
+ newSelectedIndexes = [_selectedRowIndexes copy];
+ [newSelectedIndexes addIndexes:rows];
+ }
else
- _selectedRowIndexes = [rows copy];
+ newSelectedIndexes = [rows copy];
- [self _updateHighlightWithOldRows:previousSelectedIndexes newRows:_selectedRowIndexes];
- [_tableDrawView display]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected rows
- // but currently -drawRect: is not implemented here
- [self _noteSelectionDidChange];
+ [self _setSelectedRowIndexes:newSelectedIndexes];
}
- (void)_updateHighlightWithOldRows:(CPIndexSet)oldRows newRows:(CPIndexSet)newRows
@@ -930,9 +944,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
for (var identifier in _dataViewsForTableColumns)
{
- var dataViewsInTableColumn = _dataViewsForTableColumns[identifier];
-
- var count = deselectRows.length;
+ var dataViewsInTableColumn = _dataViewsForTableColumns[identifier],
+ count = deselectRows.length;
while (count--)
[self _performSelection:NO forRow:deselectRows[count] context:dataViewsInTableColumn];
@@ -1020,12 +1033,12 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
- (int)selectedRow
{
- return [_selectedRowIndexes lastIndex];
+ return _lastSelectedRow;
}
- (CPIndexSet)selectedRowIndexes
{
- return _selectedRowIndexes;
+ return [_selectedRowIndexes copy];
}
- (void)deselectColumn:(CPInteger)aColumn
@@ -1138,23 +1151,23 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
}
/*!
- Returns the column of the currently edited cell, or -1 if none.
+ Returns the column of the currently edited cell, or CPNotFound if none.
*/
- (CPInteger)editedColumn
{
if (!_editingCellIndex)
- return -1;
+ return CPNotFound;
return _editingCellIndex.x;
}
/*!
- Returns the row of the currently edited cell, or -1 if none.
+ Returns the row of the currently edited cell, or CPNotFound if none.
*/
- (CPInteger)editedRow
{
if (!_editingCellIndex)
- return -1;
- return _editingCellIndex.x;
+ return CPNotFound;
+ return _editingCellIndex.y;
}
//Setting Auxiliary Views
@@ -1236,7 +1249,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
else
{
- var width = [_tableColumns[index] width];
+ var width = [_tableColumns[index] width] + _intercellSpacing.width;
_tableColumnRanges[index] = CPMakeRange(x, width);
@@ -1268,11 +1281,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
- (CGRect)rectOfRow:(CPInteger)aRowIndex
{
- if (NO)
- return NULL;
+ var height = _rowHeight + _intercellSpacing.height;
- // FIXME: WRONG: ASK TABLE COLUMN RANGE
- return _CGRectMake(0.0, (aRowIndex * (_rowHeight + _intercellSpacing.height)), _CGRectGetWidth([self bounds]), _rowHeight);
+ return _CGRectMake(0.0, aRowIndex * height, _CGRectGetWidth([self bounds]), height);
}
// Complexity:
@@ -1389,9 +1400,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
- (CPInteger)rowAtPoint:(CGPoint)aPoint
{
- var y = aPoint.y;
-
- var row = FLOOR(y / (_rowHeight + _intercellSpacing.height));
+ var y = aPoint.y,
+ row = FLOOR(y / (_rowHeight + _intercellSpacing.height));
if (row >= _numberOfRows)
return -1;
@@ -1404,9 +1414,14 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
UPDATE_COLUMN_RANGES_IF_NECESSARY();
var tableColumnRange = _tableColumnRanges[aColumn],
- rectOfRow = [self rectOfRow:aRow];
+ rectOfRow = [self rectOfRow:aRow],
+ leftInset = FLOOR(_intercellSpacing.width / 2.0),
+ topInset = FLOOR(_intercellSpacing.height / 2.0);
- return _CGRectMake(tableColumnRange.location, _CGRectGetMinY(rectOfRow), tableColumnRange.length, _CGRectGetHeight(rectOfRow));
+ return _CGRectMake(tableColumnRange.location + leftInset,
+ _CGRectGetMinY(rectOfRow) + topInset,
+ tableColumnRange.length - _intercellSpacing.width,
+ _CGRectGetHeight(rectOfRow) - _intercellSpacing.height);
}
- (void)resizeWithOldSuperviewSize:(CGSize)aSize
@@ -1418,11 +1433,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
var mask = _columnAutoResizingStyle;
- if(mask === CPTableViewUniformColumnAutoresizingStyle)
+ if (mask === CPTableViewUniformColumnAutoresizingStyle)
[self _resizeAllColumnUniformlyWithOldSize:aSize];
- else if(mask === CPTableViewLastColumnOnlyAutoresizingStyle)
+ else if (mask === CPTableViewLastColumnOnlyAutoresizingStyle)
[self sizeLastColumnToFit];
- else if(mask === CPTableViewFirstColumnOnlyAutoresizingStyle)
+ else if (mask === CPTableViewFirstColumnOnlyAutoresizingStyle)
[self _autoResizeFirstColumn];
}
@@ -1430,114 +1445,114 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
{
var superview = [self superview];
- if (!superview)
- return;
+ if (!superview)
+ return;
- var superviewSize = [superview bounds].size;
+ UPDATE_COLUMN_RANGES_IF_NECESSARY();
- UPDATE_COLUMN_RANGES_IF_NECESSARY();
+ var count = NUMBER_OF_COLUMNS(),
+ columnToResize = nil,
+ totalWidth = 0,
+ i = 0;
- var count = NUMBER_OF_COLUMNS(),
- visColumns = [[CPArray alloc] init],
- totalWidth = 0,
- i = 0;
+ for (; i < count; i++)
+ {
+ var column = _tableColumns[i];
- for(; i < count; i++)
- {
- if(![_tableColumns[i] isHidden])
- {
- [visColumns addObject:i];
- totalWidth += [_tableColumns[i] width];
- }
- }
+ if (![column isHidden])
+ {
+ if (!columnToResize)
+ columnToResize = column;
+ totalWidth += [column width] + _intercellSpacing.width;
+ }
+ }
- count = [visColumns count];
+ // If there is a visible column
+ if (columnToResize)
+ {
+ var superviewSize = [superview bounds].size,
+ newWidth = superviewSize.width - totalWidth;
- //if there are rows
- if (count > 0)
- {
- var columnToResize = _tableColumns[visColumns[0]];
- var newWidth = superviewSize.width - totalWidth;// - [columnToResize width];
- newWidth += [columnToResize width];
- newWidth = (newWidth < [columnToResize minWidth]) ? [columnToResize minWidth] : newWidth;
- newWidth = (newWidth > [columnToResize maxWidth]) ? [columnToResize maxWidth] : newWidth;
+ newWidth += [columnToResize width];
+ newWidth = MAX([columnToResize minWidth], newWidth);
+ newWidth = MIN([columnToResize maxWidth], newWidth);
- [columnToResize setWidth:FLOOR(newWidth)];
- }
+ [columnToResize setWidth:FLOOR(newWidth)];
+ }
- [self setNeedsLayout];
+ [self setNeedsLayout];
}
- (void)_resizeAllColumnUniformlyWithOldSize:(CGSize)oldSize
{
- var superview = [self superview];
+ var superview = [self superview];
- if (!superview)
+ if (!superview)
+ return;
+
+ var superviewSize = [superview bounds].size;
+
+ UPDATE_COLUMN_RANGES_IF_NECESSARY();
+
+ var count = NUMBER_OF_COLUMNS(),
+ visColumns = [[CPArray alloc] init],
+ buffer = 0.0;
+
+ // Fixme: cache resizable columns because they won't changes betwwen two calls to this method.
+ for (var i = 0; i < count; i++)
+ {
+ var tableColumn = _tableColumns[i];
+ if (![tableColumn isHidden] && ([tableColumn resizingMask] & CPTableColumnAutoresizingMask))
+ [visColumns addObject:i];
+ }
+
+ // redefine count
+ count = [visColumns count];
+
+ //if there are columns
+ if (count > 0)
+ {
+ var maxXofColumns = CGRectGetMaxX([self rectOfColumn:visColumns[count - 1]]);
+
+ // If the x value of the end of the last column is between the current bounds and the previous bounds we should snap.
+ if (!_lastColumnShouldSnap && (maxXofColumns >= superviewSize.width && maxXofColumns <= oldSize.width || maxXofColumns <= superviewSize.width && maxXofColumns >= oldSize.width))
+ {
+ //set the snap mask
+ _lastColumnShouldSnap = YES;
+ //then we need to make sure everything is set correctly.
+ [self _resizeAllColumnUniformlyWithOldSize:CGSizeMake(maxXofColumns, 0)];
+ }
+
+ if (!_lastColumnShouldSnap)
return;
- var superviewSize = [superview bounds].size;
+ // FIX ME: This is wrong because this should continue to resize all columns
+ // If the last column reaches it's max/min it will simply stop resizing,
+ // correct behavior is to resize all columns until they reach their min/max
- if (_dirtyTableColumnRangeIndex !== CPNotFound) [self _recalculateTableColumnRanges];//UPDATE_COLUMN_RANGES_IF_NECESSARY();
-
- var count = _tableColumns.length,//NUMBER_OF_COLUMNS(),
- visColumns = [[CPArray alloc] init],
- buffer = 0.0;
-
- // Fixme: cache resizable columns because they won't changes betwwen two calls to this method.
- for(var i=0; i < count; i++)
+ for (var i = 0; i < count; i++)
{
- var tableColumn = _tableColumns[i];
- if(![tableColumn isHidden] && ([tableColumn resizingMask] & CPTableColumnAutoresizingMask))
- [visColumns addObject:i];
+ var column = visColumns[i],
+ columnToResize = _tableColumns[column],
+ currentBuffer = buffer / (count - i),
+ realNewWidth = ([columnToResize width] / oldSize.width * [superview bounds].size.width) + currentBuffer,
+ newWidth = realNewWidth;
+ newWidth = MAX([columnToResize minWidth], newWidth);
+ newWidth = MIN([columnToResize maxWidth], newWidth);
+ buffer -= currentBuffer;
+
+ // the buffer takes into account the min/max width of the column
+ buffer += realNewWidth - newWidth;
+
+ [columnToResize setWidth:newWidth];
}
- // redefine count
- count = [visColumns count];
+ // if there is space left over that means column resize was too long or too short
+ if (buffer !== 0)
+ _lastColumnShouldSnap = NO;
+ }
- //if there are columns
- if (count > 0)
- {
- var maxXofColumns = CGRectGetMaxX([self rectOfColumn:visColumns[count - 1]]);
-
- // If the x value of the end of the last column is between the current bounds and the previous bounds we should snap.
- if (!_lastColumnShouldSnap && (maxXofColumns >= superviewSize.width && maxXofColumns <= oldSize.width || maxXofColumns <= superviewSize.width && maxXofColumns >= oldSize.width))
- {
- //set the snap mask
- _lastColumnShouldSnap = YES;
- //then we need to make sure everything is set correctly.
- [self _resizeAllColumnUniformlyWithOldSize:CGSizeMake(maxXofColumns, 0)];
- }
-
- if(!_lastColumnShouldSnap)
- return;
-
-
- // FIX ME: This is wrong because this should continue to resize all columns
- // If the last column reaches it's max/min it will simply stop resizing,
- // correct behavior is to resize all columns until they reach their min/max
-
- for (var i = 0; i < count; i++)
- {
- var column = visColumns[i];
- columnToResize = _tableColumns[column],
- currentBuffer = buffer / (count - i),
- realNewWidth = ([columnToResize width] / oldSize.width * [superview bounds].size.width) + currentBuffer ,
- newWidth = MAX([columnToResize minWidth], realNewWidth);
- newWidth = MIN([columnToResize maxWidth], realNewWidth);
- buffer -= currentBuffer;
-
- // the buffer takes into account the min/max width of the column
- buffer += realNewWidth - newWidth;
-
- [columnToResize setWidth:newWidth];
- }
-
- // if there is space left over that means column resize was too long or too short
- if(buffer !== 0)
- _lastColumnShouldSnap = NO;
- }
-
- [self setNeedsLayout];
+ [self setNeedsLayout];
}
/*!
@@ -1579,8 +1594,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
//if the last row exists
if (count >= 0)
{
- var columnToResize = _tableColumns[count];
- var newSize = MAX(0.0, superviewSize.width - CGRectGetMinX([self rectOfColumn:count]));
+ var columnToResize = _tableColumns[count],
+ newSize = MAX(0.0, superviewSize.width - CGRectGetMinX([self rectOfColumn:count]) - _intercellSpacing.width);
if (newSize > 0)
{
@@ -1595,11 +1610,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
- (void)noteNumberOfRowsChanged
{
+ var oldNumberOfRows = _numberOfRows;
+
_numberOfRows = nil;
_numberOfRows = [self numberOfRows];
- var oldNumberOfRows = _numberOfRows;
-
// remove row indexes from the selection if they no longer exist
var hangingSelections = oldNumberOfRows - _numberOfRows;
@@ -1893,11 +1908,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[newSortDescriptors insertObject:newMainSortDescriptor atIndex:0];
// Update indicator image & highlighted column before
- var image = [newMainSortDescriptor ascending] ? [CPTableView _defaultTableHeaderSortImage] : [CPTableView _defaultTableHeaderReverseSortImage];
+ var image = [newMainSortDescriptor ascending] ? [self _tableHeaderSortImage] : [self _tableHeaderReverseSortImage];
[self setIndicatorImage:nil inTableColumn:_currentHighlightedTableColumn];
- [self setIndicatorImage:image inTableColumn:tableColumn];
- [self setHighlightedTableColumn:tableColumn];
+ [self setIndicatorImage:image inTableColumn:tableColumn];
+ [self setHighlightedTableColumn:tableColumn];
[self setSortDescriptors:newSortDescriptors];
}
@@ -1905,17 +1920,21 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
- (void)setIndicatorImage:(CPImage)anImage inTableColumn:(CPTableColumn)aTableColumn
{
if (aTableColumn)
- [[aTableColumn headerView] _setIndicatorImage:anImage];
+ {
+ var headerView = [aTableColumn headerView];
+ if ([headerView respondsToSelector:@selector(_setIndicatorImage:)])
+ [headerView _setIndicatorImage:anImage];
+ }
}
-+ (CPImage)_defaultTableHeaderSortImage
+- (CPImage)_tableHeaderSortImage
{
- return CPAppKitImage("tableview-headerview-ascending.png", CGSizeMake(9.0, 8.0));
+ return [self currentValueForThemeAttribute:"sort-image"];
}
-+ (CPImage)_defaultTableHeaderReverseSortImage
+- (CPImage)_tableHeaderReverseSortImage
{
- return CPAppKitImage("tableview-headerview-descending.png", CGSizeMake(9.0, 8.0));
+ return [self currentValueForThemeAttribute:"sort-image-reversed"];
}
//Highlightable Column Headers
@@ -1990,8 +2009,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
}
var dragPoint = [self convertPoint:[theDragEvent locationInWindow] fromView:nil];
- dragViewOffset.x = CGRectGetWidth(bounds)/2 - dragPoint.x;
- dragViewOffset.y = CGRectGetHeight(bounds)/2 - dragPoint.y;
+ dragViewOffset.x = CGRectGetWidth(bounds) / 2 - dragPoint.x;
+ dragViewOffset.y = CGRectGetHeight(bounds) / 2 - dragPoint.y;
return view;
}
@@ -2004,13 +2023,13 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (CPView)_dragViewForColumn:(int)theColumnIndex event:(CPEvent)theDragEvent offset:(CPPointPointer)theDragViewOffset
{
- var dragView = [[CPView alloc] initWithFrame:CPRectMakeZero()];
+ var dragView = [[_CPColumnDragView alloc] initWithLineColor:[self gridColor]],
tableColumn = [[self tableColumns] objectAtIndex:theColumnIndex],
- bounds = CPRectMake(0.0, 0.0, [tableColumn width], CPRectGetHeight([self _exposedRect]) + 23.0),
+ bounds = CPRectMake(0.0, 0.0, [tableColumn width], _CGRectGetHeight([self visibleRect]) + 23.0),
columnRect = [self rectOfColumn:theColumnIndex],
- headerView = [tableColumn headerView];
+ headerView = [tableColumn headerView],
+ row = [_exposedRows firstIndex];
- row = [_exposedRows firstIndex];
while (row !== CPNotFound)
{
var dataView = [self _newDataViewForRow:row tableColumn:tableColumn],
@@ -2020,7 +2039,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
dataViewFrame.origin.x = 0.0;
// Offset by table header height - scroll position
- dataViewFrame.origin.y = ( CPRectGetMinY(dataViewFrame) - CPRectGetMinY([self _exposedRect]) ) + 23.0;
+ dataViewFrame.origin.y = ( _CGRectGetMinY(dataViewFrame) - _CGRectGetMinY([self visibleRect]) ) + 23.0;
[dataView setFrame:dataViewFrame];
[dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]];
@@ -2031,9 +2050,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
// Add the column header view
var headerFrame = [headerView frame];
- headerFrame.origin = CPPointMakeZero();
+ headerFrame.origin = _CGPointMakeZero();
- columnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:headerFrame];
+ var columnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:headerFrame];
[columnHeaderView setStringValue:[headerView stringValue]];
[columnHeaderView setThemeState:[headerView themeState]];
[dragView addSubview:columnHeaderView];
@@ -2058,11 +2077,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (void)setDropRow:(CPInteger)row dropOperation:(CPTableViewDropOperation)operation
{
- if(row > [self numberOfRows] && operation === CPTableViewDropOn)
+ if (row > [self numberOfRows] && operation === CPTableViewDropOn)
{
- var numberOfRows = [self numberOfRows] + 1;
- var reason = @"Attempt to set dropRow=" + row +
- " dropOperation=CPTableViewDropOn when [0 - " + numberOfRows + "] is valid range of rows."
+ var numberOfRows = [self numberOfRows] + 1,
+ reason = @"Attempt to set dropRow=" + row +
+ " dropOperation=CPTableViewDropOn when [0 - " + numberOfRows + "] is valid range of rows.";
[[CPException exceptionWithName:@"Error" reason:reason userInfo:nil] raise];
}
@@ -2120,7 +2139,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
_sortDescriptors = newSortDescriptors;
- [self _sendDataSourceSortDescriptorsDidChange:oldSortDescriptors];
+ [self _sendDataSourceSortDescriptorsDidChange:oldSortDescriptors];
}
- (CPArray)sortDescriptors
@@ -2160,16 +2179,6 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
return objectValue;
}
-- (CGRect)_exposedRect
-{
- var superview = [self superview];
-
- if (![superview isKindOfClass:[CPClipView class]])
- return [self bounds];
-
- return [self convertRect:CGRectIntersection([superview bounds], [self frame]) fromView:superview];
-}
-
- (void)load
{
if (_reloadAllRows)
@@ -2182,7 +2191,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
_reloadAllRows = NO;
}
- var exposedRect = [self _exposedRect],
+ var exposedRect = [self visibleRect],
exposedRows = [CPIndexSet indexSetWithIndexesInRange:[self rowsInRect:exposedRect]],
exposedColumns = [self columnIndexesInRect:exposedRect],
obscuredRows = [_exposedRows copy],
@@ -2206,6 +2215,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[self _unloadDataViewsInRows:previouslyExposedRows columns:obscuredColumns];
[self _unloadDataViewsInRows:obscuredRows columns:previouslyExposedColumns];
[self _unloadDataViewsInRows:obscuredRows columns:obscuredColumns];
+ [self _unloadDataViewsInRows:newlyExposedRows columns:newlyExposedColumns];
[self _loadDataViewsInRows:previouslyExposedRows columns:newlyExposedColumns];
[self _loadDataViewsInRows:newlyExposedRows columns:previouslyExposedColumns];
@@ -2216,7 +2226,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[_tableDrawView setFrame:exposedRect];
- [_tableDrawView display];
+ [self setNeedsDisplay:YES];
// Now clear all the leftovers
// FIXME: this could be faster!
@@ -2229,6 +2239,20 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[dataViews[count] removeFromSuperview];
}
+ // if we have any columns to remove do that here
+ if ([_differedColumnDataToRemove count])
+ {
+ for (var i = 0; i < _differedColumnDataToRemove.length; i++)
+ {
+ var data = _differedColumnDataToRemove[i],
+ column = data.column;
+
+ [column setHidden:data.shouldBeHidden];
+ [_tableColumns removeObject:column];
+ }
+ [_differedColumnDataToRemove removeAllObjects];
+ }
+
}
- (void)_unloadDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
@@ -2249,17 +2273,21 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
{
var column = columnArray[columnIndex],
tableColumn = _tableColumns[column],
- tableColumnUID = [tableColumn UID];
-
- var rowIndex = 0,
+ tableColumnUID = [tableColumn UID],
+ rowIndex = 0,
rowsCount = rowArray.length;
for (; rowIndex < rowsCount; ++rowIndex)
{
var row = rowArray[rowIndex],
- dataView = _dataViewsForTableColumns[tableColumnUID][row];
+ dataViews = _dataViewsForTableColumns[tableColumnUID];
- _dataViewsForTableColumns[tableColumnUID][row] = nil;
+ if (!dataViews || row >= dataViews.length)
+ continue;
+
+ var dataView = [dataViews objectAtIndex:row];
+
+ [dataViews replaceObjectAtIndex:row withObject:nil];
[self _enqueueReusableDataView:dataView];
}
@@ -2288,7 +2316,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
var column = columnArray[columnIndex],
tableColumn = _tableColumns[column];
- if ([tableColumn isHidden] || columnIndex === _draggedColumnIndex)
+ if ([tableColumn isHidden] || tableColumn === _draggedColumn)
continue;
var tableColumnUID = [tableColumn UID];
@@ -2297,9 +2325,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
_dataViewsForTableColumns[tableColumnUID] = [];
var rowIndex = 0,
- rowsCount = rowArray.length;
+ rowsCount = rowArray.length,
+ isColumnSelected = [_selectedColumnIndexes containsIndex:column];
- var isColumnSelected = [_selectedColumnIndexes containsIndex:column];
for (; rowIndex < rowsCount; ++rowIndex)
{
var row = rowArray[rowIndex],
@@ -2372,9 +2400,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
tableColumn = _tableColumns[column],
tableColumnUID = [tableColumn UID],
dataViewsForTableColumn = _dataViewsForTableColumns[tableColumnUID],
- columnRange = _tableColumnRanges[column];
-
- var rowIndex = 0,
+ columnRange = _tableColumnRanges[column],
+ rowIndex = 0,
rowsCount = rowArray.length;
for (; rowIndex < rowsCount; ++rowIndex)
@@ -2429,14 +2456,10 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[_headerView setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), _CGRectGetHeight([_headerView frame]))];
}
-- (CGRect)exposedClipRect
+- (void)setNeedsDisplay:(BOOL)aFlag
{
- var superview = [self superview];
-
- if (![superview isKindOfClass:[CPClipView class]])
- return [self bounds];
-
- return [self convertRect:CGRectIntersection([superview bounds], [self frame]) fromView:superview];
+ [super setNeedsDisplay:aFlag];
+ [_tableDrawView setNeedsDisplay:aFlag];
}
- (void)_drawRect:(CGRect)aRect
@@ -2444,20 +2467,14 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
// FIX ME: All three of these methods will likely need to be rewritten for 1.0
// We've got grid drawing in highlightSelection and crap everywhere.
- var exposedRect = [self _exposedRect];
+ var exposedRect = [self visibleRect];
[self drawBackgroundInClipRect:exposedRect];
[self drawGridInClipRect:exposedRect];
[self highlightSelectionInClipRect:exposedRect];
- if (_draggedColumnIndex === -1)
- return;
-
- var context = [[CPGraphicsContext currentContext] graphicsPort],
- columnRect = [self rectOfColumn:_draggedColumnIndex];
-
- CGContextSetFillColor(context, [CPColor grayColor]);
- CGContextFillRect(context, columnRect);
+ if (_implementsCustomDrawRow)
+ [self _drawRows:_exposedRows clipRect:exposedRect];
}
- (void)drawBackgroundInClipRect:(CGRect)aRect
@@ -2539,10 +2556,10 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
if (gridStyleMask & CPTableViewSolidHorizontalGridLineMask)
{
- var exposedRows = [self rowsInRect:aRect];
+ var exposedRows = [self rowsInRect:aRect],
row = exposedRows.location,
lastRow = CPMaxRange(exposedRows) - 1,
- rowY = 0.0,
+ rowY = -0.5,
minX = _CGRectGetMinX(aRect),
maxX = _CGRectGetMaxX(aRect);
@@ -2595,7 +2612,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
}
CGContextClosePath(context);
- CGContextSetStrokeColor(context, _gridColor);
+ CGContextSetStrokeColor(context, [self gridColor]);
CGContextStrokePath(context);
}
@@ -2638,6 +2655,12 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
deltaHeight = 0.5 * (_gridStyleMask & CPTableViewSolidHorizontalGridLineMask);
CGContextBeginPath(context);
+
+ var gradientCache = [self selectionGradientColors],
+ topLineColor = [gradientCache objectForKey:CPSourceListTopLineColor],
+ bottomLineColor = [gradientCache objectForKey:CPSourceListBottomLineColor],
+ gradientColor = [gradientCache objectForKey:CPSourceListGradient];
+
while (count--)
{
var rowRect = CGRectIntersection(objj_msgSend(self, rectSelector, indexes[count]), aRect);
@@ -2650,21 +2673,21 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
maxX = _CGRectGetMaxX(rowRect),
maxY = _CGRectGetMaxY(rowRect) - deltaHeight;
- CGContextDrawLinearGradient(context, _sourceListActiveGradient, rowRect.origin, CGPointMake(minX, maxY), 0);
+ CGContextDrawLinearGradient(context, gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0);
CGContextClosePath(context);
CGContextBeginPath(context);
CGContextMoveToPoint(context, minX, minY);
CGContextAddLineToPoint(context, maxX, minY);
CGContextClosePath(context);
- CGContextSetStrokeColor(context, _sourceListActiveTopLineColor);
+ CGContextSetStrokeColor(context, topLineColor);
CGContextStrokePath(context);
CGContextBeginPath(context);
CGContextMoveToPoint(context, minX, maxY);
CGContextAddLineToPoint(context, maxX, maxY - 1);
CGContextClosePath(context);
- CGContextSetStrokeColor(context, _sourceListActiveBottomLineColor);
+ CGContextSetStrokeColor(context, bottomLineColor);
CGContextStrokePath(context);
}
}
@@ -2673,13 +2696,13 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
if (!drawGradient)
{
- [_selectionHighlightColor setFill];
+ [[self selectionHighlightColor] setFill];
CGContextFillPath(context);
}
CGContextBeginPath(context);
- gridStyleMask = [self gridStyleMask];
- for(var i=0; i < count2; i++)
+ var gridStyleMask = [self gridStyleMask];
+ for(var i = 0; i < count2; i++)
{
var rect = objj_msgSend(self, rectSelector, indexes[i]),
minX = CGRectGetMinX(rect) - 0.5,
@@ -2690,13 +2713,13 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
if ([_selectedRowIndexes count] >= 1 && gridStyleMask & CPTableViewSolidVerticalGridLineMask)
{
var exposedColumns = [self columnIndexesInRect:aRect],
- exposedColumnIndexes = [],
- firstExposedColumn = [exposedColumns firstIndex],
- exposedRange = CPMakeRange(firstExposedColumn, [exposedColumns lastIndex] - firstExposedColumn + 1);
+ exposedColumnIndexes = [],
+ firstExposedColumn = [exposedColumns firstIndex],
+ exposedRange = CPMakeRange(firstExposedColumn, [exposedColumns lastIndex] - firstExposedColumn + 1);
[exposedColumns getIndexes:exposedColumnIndexes maxCount:-1 inIndexRange:exposedRange];
var exposedColumnCount = [exposedColumnIndexes count];
- for(var c = firstExposedColumn; c < exposedColumnCount; c++)
+ for (var c = firstExposedColumn; c < exposedColumnCount; c++)
{
var colRect = [self rectOfColumn:exposedColumnIndexes[c]],
colX = CGRectGetMaxX(colRect) + 0.5;
@@ -2704,11 +2727,10 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
CGContextMoveToPoint(context, colX, minY);
CGContextAddLineToPoint(context, colX, maxY);
}
-
}
//if the row after the current row is not selected then there is no need to draw the bottom grid line white.
- if([indexes containsObject:indexes[i]+1])
+ if ([indexes containsObject:indexes[i] + 1])
{
CGContextMoveToPoint(context, minX, maxY);
CGContextAddLineToPoint(context, maxX, maxY);
@@ -2716,10 +2738,26 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
}
CGContextClosePath(context);
- CGContextSetStrokeColor(context, [CPColor colorWithHexString:@"e5e5e5"]);
+ CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:"highlighted-grid-color"]);
CGContextStrokePath(context);
}
+- (void)_drawRows:(CPIndexSet)rowsIndexes clipRect:(CGRect)clipRect
+{
+ var row = [rowsIndexes firstIndex];
+
+ while (row !== CPNotFound)
+ {
+ [self drawRow:row clipRect:CGRectIntersection(clipRect, [self rectOfRow:row])];
+ row = [rowsIndexes indexGreaterThanIndex:row];
+ }
+}
+
+- (void)drawRow:(CPInteger)row clipRect:(CGRect)rect
+{
+ // This method does currently nothing in cappuccino. Can be overriden by subclasses.
+}
+
- (void)layoutSubviews
{
[self load];
@@ -2810,7 +2848,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
// if the table has drag support then we use mouseUp to select a single row.
// otherwise it uses mouse down.
- if (row >=0 && !(_implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_))
+ if (row >= 0 && !(_implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_))
[self _updateSelectionWithMouseAtRow:row];
[[self window] makeFirstResponder:self];
@@ -2841,7 +2879,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
// begin the drag is the datasource lets us, we've move at least +-3px vertical or horizontal,
// or we're dragging from selected rows and we haven't begun a drag session
- if(!_isSelectingSession && _implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_)
+ if (!_isSelectingSession && _implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_)
{
if (row >= 0 && (ABS(_startTrackingPoint.x - aPoint.x) > 3 || (_verticalMotionCanDrag && ABS(_startTrackingPoint.y - aPoint.y) > 3)) ||
([_selectedRowIndexes containsIndex:row]))
@@ -2879,8 +2917,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[view setImage:image];
}
- var bounds = [view bounds];
- var viewLocation = CPPointMake(aPoint.x - CGRectGetWidth(bounds)/2 + offset.x, aPoint.y - CGRectGetHeight(bounds)/2 + offset.y);
+ var bounds = [view bounds],
+ viewLocation = CPPointMake(aPoint.x - CGRectGetWidth(bounds) / 2 + offset.x, aPoint.y - CGRectGetHeight(bounds) / 2 + offset.y);
[self dragView:view at:viewLocation offset:CPPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES];
_startTrackingPoint = nil;
@@ -2895,7 +2933,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
}
_isSelectingSession = YES;
- if(row >= 0 && row !== _lastTrackedRowIndex)
+ if (row >= 0 && row !== _lastTrackedRowIndex)
{
_lastTrackedRowIndex = row;
[self _updateSelectionWithMouseAtRow:row];
@@ -2928,7 +2966,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
rowIndex,
shouldEdit = YES;
- if(_implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_)
+ if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_)
{
rowIndex = [self rowAtPoint:aPoint];
if (rowIndex !== -1)
@@ -2972,7 +3010,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
} //end of editing conditional
//double click actions
- if([[CPApp currentEvent] clickCount] === 2 && _doubleAction)
+ if ([[CPApp currentEvent] clickCount] === 2 && _doubleAction)
{
_clickedRow = [self rowAtPoint:aPoint];
[self sendAction:_doubleAction to:_target];
@@ -2988,7 +3026,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
dropOperation = [self _proposedDropOperationAtPoint:location],
row = [self _proposedRowAtPoint:location];
- if(_retargetedDropRow !== nil)
+ if (_retargetedDropRow !== nil)
row = _retargetedDropRow;
var draggedTypes = [self registeredDraggedTypes],
@@ -3040,7 +3078,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (CPTableViewDropOperation)_proposedDropOperationAtPoint:(CGPoint)theDragPoint
{
- if(_retargetedDropOperation !== nil)
+ if (_retargetedDropOperation !== nil)
return _retargetedDropOperation;
var row = [self _proposedRowAtPoint:theDragPoint],
@@ -3049,11 +3087,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
// If there is no (the default) or to little inter cell spacing we create some room for the CPTableViewDropAbove indicator
// This probably doesn't work if the row height is smaller than or around 5.0
if ([self intercellSpacing].height < 5.0)
- rowRect = CPRectInset(rowRect, 0.0, 5.0 - [self intercellSpacing].height);
+ rowRect = CPRectInset(rowRect, 0.0, 5.0 - [self intercellSpacing].height);
- // If the altered row rect contains the drag point we show the drop on
- // We don't show the drop on indicator if we are dragging below the last row
- // in that case we always want to show the drop above indicator
+ // If the altered row rect contains the drag point we show the drop on
+ // We don't show the drop on indicator if we are dragging below the last row
+ // in that case we always want to show the drop above indicator
if (CGRectContainsPoint(rowRect, theDragPoint) && row < _numberOfRows)
return CPTableViewDropOn;
@@ -3065,27 +3103,28 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (CPInteger)_proposedRowAtPoint:(CGPoint)dragPoint
{
- // We don't use rowAtPoint here because the drag indicator can appear below the last row
- // and rowAtPoint doesn't return rows that are larger than numberOfRows
- var row = FLOOR(dragPoint.y / ( _rowHeight + _intercellSpacing.height ));
+ // We don't use rowAtPoint here because the drag indicator can appear below the last row
+ // and rowAtPoint doesn't return rows that are larger than numberOfRows
+ // FIX ME: this is going to break when we implement variable row heights...
+ var row = FLOOR(dragPoint.y / ( _rowHeight + _intercellSpacing.height )),
+ // Determine if the mouse is currently closer to this row or the row below it
+ lowerRow = row + 1,
+ rect = [self rectOfRow:row],
+ bottomPoint = CGRectGetMaxY(rect),
+ bottomThirty = bottomPoint - ((bottomPoint - CGRectGetMinY(rect)) * 0.3);
- // Determine if the mouse is currently closer to this row or the row below it
- var lowerRow = row + 1,
- rect = [self rectOfRow:row],
- lowerRect = [self rectOfRow:lowerRow];
-
- if (ABS(CPRectGetMinY(lowerRect) - dragPoint.y) < ABS(dragPoint.y - CPRectGetMinY(rect)))
- row = lowerRow;
+ if (dragPoint.y > MAX(bottomThirty, bottomPoint - 6))
+ row = lowerRow;
if (row >= [self numberOfRows])
row = [self numberOfRows];
- return row;
+ return row;
}
- (void)_validateDrop:(id)info proposedRow:(CPInteger)row proposedDropOperation:(CPTableViewDropOperation)dropOperation
{
- if(_implementedDataSourceMethods & CPTableViewDataSource_tableView_validateDrop_proposedRow_proposedDropOperation_)
+ if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_validateDrop_proposedRow_proposedDropOperation_)
return [_dataSource tableView:self validateDrop:info proposedRow:row proposedDropOperation:dropOperation];
return CPDragOperationNone;
@@ -3104,27 +3143,26 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
if (theLowerRowIndex > [self numberOfRows])
theLowerRowIndex = [self numberOfRows];
- return [self rectOfRow:theLowerRowIndex];
+ return [self rectOfRow:theLowerRowIndex];
}
- (CPDragOperation)draggingUpdated:(id)sender
{
var location = [self convertPoint:[sender draggingLocation] fromView:nil],
dropOperation = [self _proposedDropOperationAtPoint:location],
- numberOfRows = [self numberOfRows];
+ numberOfRows = [self numberOfRows],
+ row = [self _proposedRowAtPoint:location],
+ dragOperation = [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation],
+ exposedClipRect = [self visibleRect];
- var row = [self _proposedRowAtPoint:location],
- dragOperation = [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation];
- exposedClipRect = [self exposedClipRect];
-
- if(_retargetedDropRow !== nil)
+ if (_retargetedDropRow !== nil)
row = _retargetedDropRow;
if (dropOperation === CPTableViewDropOn && row >= [self numberOfRows])
row = [self numberOfRows] - 1;
- var rect = CPRectMakeZero();
+ var rect = _CGRectMakeZero();
if (row === -1)
rect = exposedClipRect;
@@ -3161,11 +3199,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (BOOL)performDragOperation:(id)sender
{
- var location = [self convertPoint:[sender draggingLocation] fromView:nil];
+ var location = [self convertPoint:[sender draggingLocation] fromView:nil],
operation = [self _proposedDropOperationAtPoint:location],
row = _retargetedDropRow;
- if(row === nil)
+ if (row === nil)
var row = [self _proposedRowAtPoint:location];
return [_dataSource tableView:self acceptDrop:sender row:row dropOperation:operation];
@@ -3266,9 +3304,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
if (![_delegate tableView:self shouldSelectRow:index])
[newSelection removeIndex:index];
}
- }
- _lastSelectedRow = ([newSelection count] > 0) ? aRow : -1;
+ // as per cocoa
+ if ([newSelection count] === 0)
+ return;
+ }
// if empty selection is not allowed and the new selection has nothing selected, abort
if (!_allowsEmptySelection && [newSelection count] === 0)
@@ -3358,6 +3398,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[self scrollRowToVisible:i];
}
+- (void)moveDownAndModifySelection:(id)sender
+{
+ [self moveDown:sender];
+}
+
- (void)moveUp:(id)sender
{
if (_implementedDelegateMethods & CPTableViewDelegate_selectionShouldChangeInTableView_ &&
@@ -3405,6 +3450,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[self scrollRowToVisible:i];
}
+- (void)moveUpAndModifySelection:(id)sender
+{
+ [self moveUp:sender];
+}
+
- (void)deleteBackward:(id)sender
{
if([_delegate respondsToSelector: @selector(tableViewDeleteKeyPressed:)])
@@ -3415,14 +3465,23 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
@implementation CPTableView (Bindings)
+- (CPString)_replacementKeyPathForBinding:(CPString)aBinding
+{
+ if (aBinding === @"selectionIndexes")
+ return @"selectedRowIndexes";
+
+ return [super _replacementKeyPathForBinding:aBinding];
+}
+
- (void)_establishBindingsIfUnbound:(id)destination
{
if ([[self infoForBinding:@"content"] objectForKey:CPObservedObjectKey] !== destination)
- {
[self bind:@"content" toObject:destination withKeyPath:@"arrangedObjects" options:nil];
- //[self bind:@"sortDescriptors" toObject:destination withKeyPath:@"sortDescriptors" options:nil];
- //[self bind:@"selectionIndexes" toObject:destination withKeyPath:@"selectionIndexes" options:nil];
- }
+
+ if ([[self infoForBinding:@"selectionIndexes"] objectForKey:CPObservedObjectKey] !== destination)
+ [self bind:@"selectionIndexes" toObject:destination withKeyPath:@"selectionIndexes" options:nil];
+
+ //[self bind:@"sortDescriptors" toObject:destination withKeyPath:@"sortDescriptors" options:nil];
}
- (void)setContent:(CPArray)content
@@ -3438,11 +3497,13 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
CPTableViewTableColumnsKey = @"CPTableViewTableColumnsKey",
CPTableViewRowHeightKey = @"CPTableViewRowHeightKey",
CPTableViewIntercellSpacingKey = @"CPTableViewIntercellSpacingKey",
+ CPTableViewSelectionHighlightStyleKey = @"CPTableViewSelectionHighlightStyleKey",
CPTableViewMultipleSelectionKey = @"CPTableViewMultipleSelectionKey",
CPTableViewEmptySelectionKey = @"CPTableViewEmptySelectionKey",
CPTableViewColumnReorderingKey = @"CPTableViewColumnReorderingKey",
CPTableViewColumnResizingKey = @"CPTableViewColumnResizingKey",
CPTableViewColumnSelectionKey = @"CPTableViewColumnSelectionKey",
+ CPTableViewColumnAutoresizingStyleKey = @"CPTableViewColumnAutoresizingStyleKey",
CPTableViewGridColorKey = @"CPTableViewGridColorKey",
CPTableViewGridStyleMaskKey = @"CPTableViewGridStyleMaskKey",
CPTableViewUsesAlternatingBackgroundKey = @"CPTableViewUsesAlternatingBackgroundKey",
@@ -3466,7 +3527,8 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
_allowsColumnSelection = [aCoder decodeBoolForKey:CPTableViewColumnSelectionKey];
//Setting Display Attributes
- _selectionHighlightStyle = CPTableViewSelectionHighlightStyleRegular;
+ _selectionHighlightStyle = [aCoder decodeIntForKey:CPTableViewSelectionHighlightStyleKey];
+ _columnAutoResizingStyle = [aCoder decodeIntForKey:CPTableViewColumnAutoresizingStyleKey];
_tableColumns = [aCoder decodeObjectForKey:CPTableViewTableColumnsKey] || [];
[_tableColumns makeObjectsPerformSelector:@selector(setTableView:) withObject:self];
@@ -3476,18 +3538,22 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
else
_rowHeight = 23.0;
- _intercellSpacing = [aCoder decodeSizeForKey:CPTableViewIntercellSpacingKey] || _CGSizeMake(0.0, 0.0);
+ _intercellSpacing = [aCoder decodeSizeForKey:CPTableViewIntercellSpacingKey] || _CGSizeMake(3.0, 2.0);
- _gridColor = [aCoder decodeObjectForKey:CPTableViewGridColorKey] || [CPColor grayColor];
+ [self setGridColor:[aCoder decodeObjectForKey:CPTableViewGridColorKey]];
_gridStyleMask = [aCoder decodeIntForKey:CPTableViewGridStyleMaskKey] || CPTableViewGridNone;
- _usesAlternatingRowBackgroundColors = [aCoder decodeObjectForKey:CPTableViewUsesAlternatingBackgroundKey]
- _alternatingRowBackgroundColors =
- [[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]];
+ _usesAlternatingRowBackgroundColors = [aCoder decodeObjectForKey:CPTableViewUsesAlternatingBackgroundKey];
+ [self setAlternatingRowBackgroundColors:[aCoder decodeObjectForKey:CPTableViewAlternatingRowColorsKey]];
_headerView = [aCoder decodeObjectForKey:CPTableViewHeaderViewKey];
_cornerView = [aCoder decodeObjectForKey:CPTableViewCornerViewKey];
+ // Make sure we unhide the cornerview because a corner view loaded from cib is always hidden
+ // This might be a bug in IB, or the way we load the NSvFlags might be broken for _NSCornerView
+ if (_cornerView)
+ [_cornerView setHidden:NO];
+
_dataSource = [aCoder decodeObjectForKey:CPTableViewDataSourceKey];
_delegate = [aCoder decodeObjectForKey:CPTableViewDelegateKey];
@@ -3509,6 +3575,9 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
[aCoder encodeFloat:_rowHeight forKey:CPTableViewRowHeightKey];
[aCoder encodeSize:_intercellSpacing forKey:CPTableViewIntercellSpacingKey];
+ [aCoder encodeInt:_selectionHighlightStyle forKey:CPTableViewSelectionHighlightStyleKey];
+ [aCoder encodeInt:_columnAutoResizingStyle forKey:CPTableViewColumnAutoresizingStyleKey];
+
[aCoder encodeBool:_allowsMultipleSelection forKey:CPTableViewMultipleSelectionKey];
[aCoder encodeBool:_allowsEmptySelection forKey:CPTableViewEmptySelectionKey];
[aCoder encodeBool:_allowsColumnReordering forKey:CPTableViewColumnReorderingKey];
@@ -3517,11 +3586,11 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
[aCoder encodeObject:_tableColumns forKey:CPTableViewTableColumnsKey];
- [aCoder encodeObject:_gridColor forKey:CPTableViewGridColorKey];
+ [aCoder encodeObject:[self gridColor] forKey:CPTableViewGridColorKey];
[aCoder encodeInt:_gridStyleMask forKey:CPTableViewGridStyleMaskKey];
[aCoder encodeBool:_usesAlternatingRowBackgroundColors forKey:CPTableViewUsesAlternatingBackgroundKey];
- [aCoder encodeObject:_alternatingRowBackgroundColors forKey:CPTableViewAlternatingRowColorsKey]
+ [aCoder encodeObject:[self alternatingRowBackgroundColors] forKey:CPTableViewAlternatingRowColorsKey]
[aCoder encodeObject:_cornerView forKey:CPTableViewCornerViewKey];
[aCoder encodeObject:_headerView forKey:CPTableViewHeaderViewKey];
@@ -3653,3 +3722,40 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
[CPTimer scheduledTimerWithTimeInterval:0.27 callback:showCallback repeats:NO];
}
@end
+
+@implementation _CPColumnDragView : CPView
+{
+ CPColor _lineColor;
+}
+
+- (id)initWithLineColor:(CPColor)aColor
+{
+ self = [super initWithFrame:_CGRectMakeZero()];
+
+ if (self)
+ _lineColor = aColor;
+
+ return self;
+}
+
+- (void)drawRect:(CGRect)aRect
+{
+ var context = [[CPGraphicsContext currentContext] graphicsPort];
+
+ CGContextSetStrokeColor(context, _lineColor);
+
+ var points = [
+ _CGPointMake(0.5, 0),
+ _CGPointMake(0.5, aRect.size.height)
+ ];
+
+ CGContextStrokeLineSegments(context, points, 2);
+
+ points = [
+ _CGPointMake(aRect.size.width - 0.5, 0),
+ _CGPointMake(aRect.size.width - 0.5, aRect.size.height)
+ ];
+
+ CGContextStrokeLineSegments(context, points, 2);
+}
+@end
diff --git a/AppKit/CPText.j b/AppKit/CPText.j
index 1ef33efe1..4ac6354d4 100644
--- a/AppKit/CPText.j
+++ b/AppKit/CPText.j
@@ -22,19 +22,11 @@
@import "CPView.j"
-CPTabCharacter = "\u0009";
-CPFormFeedCharacter = "\u000c";
-CPNewlineCharacter = "\u000a";
-CPCarriageReturnCharacter = "\u000d";
CPEnterCharacter = "\u0003";
CPBackspaceCharacter = "\u0008";
+CPTabCharacter = "\u0009";
+CPNewlineCharacter = "\u000a";
+CPFormFeedCharacter = "\u000c";
+CPCarriageReturnCharacter = "\u000d";
CPBackTabCharacter = "\u0019";
CPDeleteCharacter = "\u007f";
-
-@implementation CPText : CPView
-{
-
-}
-
-@end
-
diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j
index d3873e524..cf5ec9465 100644
--- a/AppKit/CPTextField.j
+++ b/AppKit/CPTextField.j
@@ -49,7 +49,7 @@ var CPTextFieldDOMInputElement = nil,
CPTextFieldCachedSelectStartFunction = nil,
CPTextFieldCachedDragFunction = nil,
CPTextFieldBlurFunction = nil;
-
+
#endif
var CPSecureTextFieldCharacter = "\u2022";
@@ -82,13 +82,13 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
BOOL _isSecure;
BOOL _drawsBackground;
-
+
CPColor _textFieldBackgroundColor;
-
+
id _placeholderString;
-
+
id _delegate;
-
+
CPString _textDidChangeValue;
// NS-style Display Properties
@@ -199,7 +199,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
}
CPTextFieldHandleBlur = function(anEvent)
- {
+ {
CPTextFieldInputOwner = nil;
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
@@ -207,10 +207,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
//FIXME make this not onblur
CPTextFieldDOMInputElement.onblur = CPTextFieldBlurFunction;
-
+
CPTextFieldDOMStandardInputElement = CPTextFieldDOMInputElement;
}
-
+
if (CPFeatureIsCompatible(CPInputTypeCanBeChangedFeature))
{
if ([self isSecure])
@@ -237,14 +237,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
CPTextFieldDOMPasswordInputElement.onblur = CPTextFieldBlurFunction;
}
-
+
CPTextFieldDOMInputElement = CPTextFieldDOMPasswordInputElement;
}
else
{
CPTextFieldDOMInputElement = CPTextFieldDOMStandardInputElement;
}
-
+
return CPTextFieldDOMInputElement;
}
#endif
@@ -262,18 +262,29 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
}
-
+
return self;
}
#pragma mark Controlling Editability and Selectability
-/*!
- Sets whether or not the receiver text field can be edited
+/*!
+ Sets whether or not the receiver text field can be edited. If NO, any
+ ongoing edit is ended.
*/
- (void)setEditable:(BOOL)shouldBeEditable
{
+ if (_isEditable === shouldBeEditable)
+ return;
+
_isEditable = shouldBeEditable;
+
+ if(shouldBeEditable)
+ _isSelectable = YES;
+
+ // We only allow first responder status if the field is editable and enabled.
+ if (!shouldBeEditable && [[self window] firstResponder] === self)
+ [[self window] makeFirstResponder:nil];
}
/*!
@@ -284,6 +295,19 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
return _isEditable;
}
+/*!
+ Sets whether the field reacts to events. If NO, any ongoing edit is
+ ended.
+*/
+- (void)setEnabled:(BOOL)shouldBeEnabled
+{
+ [super setEnabled:shouldBeEnabled];
+
+ // We only allow first responder status if the field is editable and enabled.
+ if (!shouldBeEnabled && [[self window] firstResponder] === self)
+ [[self window] makeFirstResponder:nil];
+}
+
/*!
Sets whether the field's text is selectable by the user.
@param aFlag \c YES makes the text selectable
@@ -346,7 +370,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)setBezelStyle:(CPTextFieldBezelStyle)aBezelStyle
{
var shouldBeRounded = aBezelStyle === CPTextFieldRoundedBezel;
-
+
if (shouldBeRounded)
[self setThemeState:CPTextFieldStateRounded];
else
@@ -392,9 +416,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
if (_drawsBackground == shouldDrawBackground)
return;
-
+
_drawsBackground = shouldDrawBackground;
-
+
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
@@ -415,9 +439,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
if (_textFieldBackgroundColor == aColor)
return;
-
+
_textFieldBackgroundColor = aColor;
-
+
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
@@ -480,24 +504,24 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
_DOMElement.appendChild(element);
- window.setTimeout(function()
- {
+ window.setTimeout(function()
+ {
element.focus();
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
CPTextFieldInputOwner = self;
}, 0.0);
-
+
element.value = [self stringValue];
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
-
+
CPTextFieldInputIsActive = YES;
if (document.attachEvent)
{
CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
CPTextFieldCachedDragFunction = [[self window] platformWindow]._DOMBodyElement.ondrag;
-
+
[[self window] platformWindow]._DOMBodyElement.ondrag = function () {};
[[self window] platformWindow]._DOMBodyElement.onselectstart = function () {};
}
@@ -523,10 +547,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
CPTextFieldInputResigning = YES;
element.blur();
-
+
if (!CPTextFieldInputDidBlur)
CPTextFieldBlurFunction();
-
+
CPTextFieldInputDidBlur = NO;
CPTextFieldInputResigning = NO;
@@ -536,14 +560,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
CPTextFieldInputIsActive = NO;
if (document.attachEvent)
- {
+ {
[[self window] platformWindow]._DOMBodyElement.ondrag = CPTextFieldCachedDragFunction;
[[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
CPTextFieldCachedSelectStartFunction = nil;
CPTextFieldCachedDragFunction = nil;
}
-
+
#endif
//post CPControlTextDidEndEditingNotification
@@ -580,7 +604,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
CPTextFieldCachedDragFunction = [[self window] platformWindow]._DOMBodyElement.ondrag;
-
+
[[self window] platformWindow]._DOMBodyElement.ondrag = function () {};
[[self window] platformWindow]._DOMBodyElement.onselectstart = function () {};
}
@@ -599,7 +623,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (document.attachEvent)
{
[[self window] platformWindow]._DOMBodyElement.ondrag = CPTextFieldCachedDragFunction;
- [[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
+ [[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
CPTextFieldCachedSelectStartFunction = nil
CPTextFieldCachedDragFunction = nil;
@@ -709,7 +733,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)setObjectValue:(id)aValue
{
[super setObjectValue:aValue];
-
+
#if PLATFORM(DOM)
if (CPTextFieldInputOwner === self || [[self window] firstResponder] === self)
@@ -737,7 +761,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
if (_placeholderString === aStringValue)
return;
-
+
_placeholderString = aStringValue;
// Only update things if we need to show the placeholder
@@ -758,17 +782,17 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
/*!
Size to fit has two behavior, depending on if the receiver is an editable text field or not.
-
- For non-editable text fields (typically, a label), sizeToFit will change the frame of the
+
+ For non-editable text fields (typically, a label), sizeToFit will change the frame of the
receiver to perfectly fit the current text in stringValue in the current font, and respecting
the current theme values for content-inset, min-size, and max-size.
-
- For editable text fields, sizeToFit will ONLY change the HEIGHT of the text field. It will not
- change the width of the text field. You can use setFrameSize: with the current height to set the
- width, and you can get the size of a string with [CPString sizeWithFont:].
-
+
+ For editable text fields, sizeToFit will ONLY change the HEIGHT of the text field. It will not
+ change the width of the text field. You can use setFrameSize: with the current height to set the
+ width, and you can get the size of a string with [CPString sizeWithFont:].
+
The logic behind this decision is that most of the time you do not know what content will be placed
- in an editable text field, so you want to just choose a fixed width and leave it at that size.
+ in an editable text field, so you want to just choose a fixed width and leave it at that size.
However, since you don't know how tall it needs to be if you change the font, sizeToFit will still be
useful for making the textfield an appropriate height.
*/
@@ -802,16 +826,13 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
#if PLATFORM(DOM)
var element = [self _inputElement];
-
+
if (([self isEditable] || [self isSelectable]))
{
if ([[self window] firstResponder] === self)
window.setTimeout(function() { element.select(); }, 0);
- else
- {
- [[self window] makeFirstResponder:self];
+ else if ([self window] !== nil && [[self window] makeFirstResponder:self])
window.setTimeout(function() {[self selectText:sender];}, 0);
- }
}
#endif
}
@@ -870,14 +891,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
return CPMakeRange(0, 0);
// we wrap this in try catch because firefox will throw an exception in certain instances
- try
+ try
{
var inputElement = [self _inputElement],
selectionStart = inputElement.selectionStart,
selectionEnd = inputElement.selectionEnd;
if ([selectionStart isKindOfClass:CPNumber])
- return CPMakeRange(selectionStart, selectionEnd - selectionStart);
+ return CPMakeRange(selectionStart, selectionEnd - selectionStart);
// browsers which don't support selectionStart/selectionEnd (aka IE).
var theDocument = inputElement.ownerDocument || inputElement.document,
@@ -889,8 +910,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
range.setEndPoint('EndToStart', selectionRange);
return CPMakeRange(range.text.length, selectionRange.text.length);
}
- }
- catch (e)
+ }
+ catch (e)
{
// fall through to the return
}
@@ -905,7 +926,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
var inputElement = [self _inputElement];
- try
+ try
{
if ([inputElement.selectionStart isKindOfClass:CPNumber])
{
@@ -918,7 +939,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
var theDocument = inputElement.ownerDocument || inputElement.document,
existingRange = theDocument.selection.createRange(),
range = inputElement.createTextRange();
-
+
if (range.inRange(existingRange))
{
range.collapse(true);
@@ -953,7 +974,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)setDelegate:(id)aDelegate
{
var defaultCenter = [CPNotificationCenter defaultCenter];
-
+
//unsubscribe the existing delegate if it exists
if (_delegate)
{
@@ -963,24 +984,24 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[defaultCenter removeObserver:_delegate name:CPTextFieldDidFocusNotification object:self];
[defaultCenter removeObserver:_delegate name:CPTextFieldDidBlurNotification object:self];
}
-
+
_delegate = aDelegate;
-
+
if ([_delegate respondsToSelector:@selector(controlTextDidBeginEditing:)])
[defaultCenter
addObserver:_delegate
selector:@selector(controlTextDidBeginEditing:)
name:CPControlTextDidBeginEditingNotification
object:self];
-
+
if ([_delegate respondsToSelector:@selector(controlTextDidChange:)])
[defaultCenter
addObserver:_delegate
selector:@selector(controlTextDidChange:)
name:CPControlTextDidChangeNotification
object:self];
-
-
+
+
if ([_delegate respondsToSelector:@selector(controlTextDidEndEditing:)])
[defaultCenter
addObserver:_delegate
@@ -1011,15 +1032,15 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (CGRect)contentRectForBounds:(CGRect)bounds
{
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
-
+
if (!contentInset)
return 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;
}
@@ -1029,12 +1050,12 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (_CGInsetIsEmpty(bezelInset))
return 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;
}
@@ -1042,10 +1063,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
if (aName === "bezel-view")
return [self bezelRectForBounds:[self bounds]];
-
+
else if (aName === "content-view")
return [self contentRectForBounds:[self bounds]];
-
+
return [super rectForEphemeralSubviewNamed:aName];
}
@@ -1056,19 +1077,19 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[view setHitTests:NO];
-
+
return view;
}
else
{
var view = [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
//[view setImagePosition:CPNoImage];
-
+
[view setHitTests:NO];
-
+
return view;
}
-
+
return [super createEphemeralSubviewNamed:aName];
}
@@ -1077,10 +1098,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:@"content-view"];
-
+
if (bezelView)
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
-
+
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"bezel-view"];
@@ -1090,7 +1111,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[contentView setHidden:[self hasThemeState:CPThemeStateEditing]];
var string = "";
-
+
if ([self hasThemeState:CPTextFieldStatePlaceholder])
string = [self placeholderString];
else
@@ -1148,6 +1169,7 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
CPTextFieldBezelStyleKey = "CPTextFieldBezelStyleKey",
CPTextFieldDrawsBackgroundKey = "CPTextFieldDrawsBackgroundKey",
CPTextFieldLineBreakModeKey = "CPTextFieldLineBreakModeKey",
+ CPTextFieldAlignmentKey = "CPTextFieldAlignmentKey",
CPTextFieldBackgroundColorKey = "CPTextFieldBackgroundColorKey",
CPTextFieldPlaceholderStringKey = "CPTextFieldPlaceholderStringKey";
@@ -1161,7 +1183,7 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
-
+
if (self)
{
[self setEditable:[aCoder decodeBoolForKey:CPTextFieldIsEditableKey]];
@@ -1171,9 +1193,12 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
[self setTextFieldBackgroundColor:[aCoder decodeObjectForKey:CPTextFieldBackgroundColorKey]];
+ [self setLineBreakMode:[aCoder decodeIntForKey:CPTextFieldLineBreakModeKey]];
+ [self setAlignment:[aCoder decodeIntForKey:CPTextFieldAlignmentKey]];
+
[self setPlaceholderString:[aCoder decodeObjectForKey:CPTextFieldPlaceholderStringKey]];
}
-
+
return self;
}
@@ -1184,14 +1209,17 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
-
+
[aCoder encodeBool:_isEditable forKey:CPTextFieldIsEditableKey];
[aCoder encodeBool:_isSelectable forKey:CPTextFieldIsSelectableKey];
-
+
[aCoder encodeBool:_drawsBackground forKey:CPTextFieldDrawsBackgroundKey];
-
+
[aCoder encodeObject:_textFieldBackgroundColor forKey:CPTextFieldBackgroundColorKey];
-
+
+ [aCoder encodeInt:[self lineBreakMode] forKey:CPTextFieldLineBreakModeKey];
+ [aCoder encodeInt:[self alignment] forKey:CPTextFieldAlignmentKey];
+
[aCoder encodeObject:_placeholderString forKey:CPTextFieldPlaceholderStringKey];
}
diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j
index 4b37e6d7a..a375b4f62 100644
--- a/AppKit/CPTheme.j
+++ b/AppKit/CPTheme.j
@@ -24,8 +24,9 @@
@import
@import
-var CPThemesByName = { },
- CPThemeDefaultTheme = nil;
+var CPThemesByName = { },
+ CPThemeDefaultTheme = nil,
+ CPThemeDefaultHudTheme = nil;
/*!
@@ -48,6 +49,27 @@ var CPThemesByName = { },
return CPThemeDefaultTheme;
}
+/*!
+ Set the default HUD theme. If set to nil, the default described in defaultHudTheme
+ will be used.
+*/
++ (void)setDefaultHudTheme:(CPTheme)aTheme
+{
+ CPThemeDefaultHudTheme = aTheme;
+}
+
+/*!
+ The default HUD theme is (sometimes) used for windows with the CPHUDBackgroundWindowMask
+ style mask. The default is theme with the name of the default theme with -HUD appended
+ at the end.
+*/
++ (CPTheme)defaultHudTheme
+{
+ if (!CPThemeDefaultHudTheme)
+ CPThemeDefaultHudTheme = [CPTheme themeNamed:[[self defaultTheme] name] + "-HUD"];
+ return CPThemeDefaultHudTheme;
+}
+
+ (CPTheme)themeNamed:(CPString)aName
{
return CPThemesByName[aName];
@@ -73,9 +95,105 @@ var CPThemesByName = { },
return _name;
}
-- (_CPThemeAttribute)_attributeWithName:(CPString)aName forClass:(CPString)aClass
+/*!
+ Returns an array of names of themed classes defined in this theme, as found in its
+ ThemeDescriptors.j file.
+
+ NOTE: The names are not class names (such as "CPButton"), but the names returned
+ by the class' +themeClass method. For example, the name for CPCheckBox is "check-box",
+ as defined in CPCheckBox::themeClass.
+*/
+- (CPArray)classNames
{
- var attributes = [_attributes objectForKey:aClass];
+ return [_attributes allKeys];
+}
+
+/*!
+ Returns a dictionary of all theme attributes defined for the given class, as found in the
+ theme's ThemeDescriptors.j file. The keys of the dictionary are attribute names, and the values
+ are instances of _CPThemeAttribute.
+
+ For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
+
+ @param aClass The themed class whose attributes you want to retrieve
+ @return A dictionary of attributes or nil
+*/
+- (CPDictionary)attributesForClass:(id)aClass
+{
+ if (!aClass)
+ return nil;
+
+ var className = nil;
+
+ if ([aClass isKindOfClass:[CPString class]])
+ {
+ // See if it is a class name
+ var theClass = CPClassFromString(aClass);
+
+ if (theClass)
+ aClass = theClass;
+ else
+ className = aClass;
+ }
+
+ if (!className)
+ {
+ if ([aClass isKindOfClass:[CPView class]])
+ {
+ if ([aClass respondsToSelector:@selector(themeClass)])
+ className = [aClass themeClass];
+ else
+ return nil;
+ }
+ else
+ [CPException raise:CPInvalidArgumentException reason:@"aClass must be a class object or a string."];
+ }
+
+ return [_attributes objectForKey:className];
+}
+
+/*!
+ Returns an array of names of all theme attributes defined for the given class, as found in the
+ theme's ThemeDescriptors.j file.
+
+ The \c aClass parameter can be one of the following:
+
+ - A class instance, for example the result of [CPCheckBox class]. The class must be a subclass
+ of CPView.
+ - A class name, for example "CPCheckBox".
+ - A themed class name, for example "check-box".
+
+ If \c aClass does not refer to a themed class in this theme, nil is returned.
+
+ @param aClass The themed class whose attributes you want to retrieve
+ @return An array of attribute names or nil
+*/
+- (CPDictionary)attributeNamesForClass:(id)aClass
+{
+ var attributes = [self attributesForClass:aClass];
+
+ if (attributes)
+ return [attributes allKeys];
+ else
+ return [CPArray array];
+}
+
+/*!
+ Returns a theme attribute defined for the given class, as found in the
+ theme's ThemeDescriptors.j file.
+
+ \c aName should be the attribute name as you would pass to the method
+ CPView::valueForThemeAttribute:.
+
+ For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
+
+ @param aName The name of the attribute you want to retrieve
+ @param aClass The themed class in which to look for the attribute
+ @return An instance of _CPThemeAttribute or nil
+*/
+- (_CPThemeAttribute)attributeWithName:(CPString)aName forClass:(id)aClass
+{
+ var attributes = [self attributesForClass:aClass];
if (!attributes)
return nil;
@@ -83,6 +201,47 @@ var CPThemesByName = { },
return [attributes objectForKey:aName];
}
+/*!
+ Returns the value for a theme attribute in its normal state, as defined for the given class
+ in the theme's ThemeDescriptors.j file.
+
+ \c aName should be the attribute name as you would pass to the method
+ CPView::valueForThemeAttribute:.
+
+ For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
+
+ @param aName The name of the attribute whose value you want to retrieve
+ @param aClass The themed class in which to look for the attribute
+ @return A value or nil
+*/
+- (id)valueForAttributeWithName:(CPString)aName forClass:(id)aClass
+{
+ return [self valueForAttributeWithName:aName inState:CPThemeStateNormal forClass:aClass];
+}
+
+/*!
+ Returns the value for a theme attribute in a given state, as defined for the given class
+ in the theme's ThemeDescriptors.j file. This is the equivalent of the method
+ CPView::valueForThemeAttribute:inState:, but retrieves the value from the theme definition as
+ opposed to a single view's current theme state.
+
+ For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
+
+ @param aName The name of the attribute whose value you want to retrieve
+ @param aState The state qualifier for the attribute
+ @param aClass The themed class in which to look for the attribute
+ @return A value or nil
+*/
+- (id)valueForAttributeWithName:(CPString)aName inState:(CPThemeState)aState forClass:(id)aClass
+{
+ var attribute = [self attributeWithName:aName forClass:aClass];
+
+ if (!attribute)
+ return nil;
+
+ return [attribute valueForState:aState];
+}
+
- (void)takeThemeFromObject:(id)anObject
{
var attributes = [anObject _themeAttributeDictionary],
@@ -242,7 +401,8 @@ CPThemeStateNormal = CPThemeStates["normal"] = 0;
CPThemeStateDisabled = CPThemeState("disabled");
CPThemeStateHighlighted = CPThemeState("highlighted");
CPThemeStateSelected = CPThemeState("selected");
-CPThemeStateSelectedDataView = CPThemeState("selectedDataView");
+CPThemeStateTableDataView = CPThemeState("tableDataView");
+CPThemeStateSelectedDataView = CPThemeStateSelectedTableDataView = CPThemeState("selectedTableDataView");
CPThemeStateBezeled = CPThemeState("bezeled");
CPThemeStateBordered = CPThemeState("bordered");
CPThemeStateEditable = CPThemeState("editable");
@@ -255,10 +415,10 @@ CPThemeStateCircular = CPThemeState("circular");
{
CPString _name;
id _defaultValue;
- CPDictionary _values;
+ CPDictionary _values @accessors(readonly, getter=values);
JSObject _cache;
- CPThemeAttribute _parentAttribute;
+ _CPThemeAttribute _parentAttribute;
}
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue
@@ -379,7 +539,7 @@ CPThemeStateCircular = CPThemeState("circular");
return value;
}
-- (void)setParentAttribute:(CPThemeAttribute)anAttribute
+- (void)setParentAttribute:(_CPThemeAttribute)anAttribute
{
if (_parentAttribute === anAttribute)
return;
@@ -388,7 +548,7 @@ CPThemeStateCircular = CPThemeState("circular");
_parentAttribute = anAttribute;
}
-- (CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
+- (_CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
{
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue];
@@ -473,15 +633,15 @@ CPThemeStateCircular = CPThemeState("circular");
@end
-var cachedNumberOfOnes = [ 0 /*000000*/, 1 /*000001*/, 1 /*000010*/, 2 /*000011*/, 1 /*000100*/, 2 /*000101*/, 2 /*000110*/,
- 3 /*000111*/, 1 /*001000*/, 2 /*001001*/, 2 /*001010*/, 3 /*001011*/, 2 /*001100*/, 3 /*001101*/,
- 3 /*001110*/, 4 /*001111*/, 1 /*010000*/, 2 /*010001*/, 2 /*010010*/, 3 /*010011*/, 2 /*010100*/,
- 3 /*010101*/, 3 /*010110*/, 4 /*010111*/, 2 /*011000*/, 3 /*011001*/, 3 /*011010*/, 4 /*011011*/,
- 3 /*011100*/, 4 /*011101*/, 4 /*011110*/, 5 /*011111*/, 1 /*100000*/, 2 /*100001*/, 2 /*100010*/,
- 3 /*100011*/, 2 /*100100*/, 3 /*100101*/, 3 /*100110*/, 4 /*100111*/, 2 /*101000*/, 3 /*101001*/,
- 3 /*101010*/, 4 /*101011*/, 3 /*101100*/, 4 /*101101*/, 4 /*101110*/, 5 /*101111*/, 2 /*110000*/,
- 3 /*110001*/, 3 /*110010*/, 4 /*110011*/, 3 /*110100*/, 4 /*110101*/, 4 /*110110*/, 5 /*110111*/,
- 3 /*111000*/, 4 /*111001*/, 4 /*111010*/, 5 /*111011*/, 4 /*111100*/, 5 /*111101*/, 5 /*111110*/,
+var cachedNumberOfOnes = [ 0 /*000000*/, 1 /*000001*/, 1 /*000010*/, 2 /*000011*/, 1 /*000100*/, 2 /*000101*/, 2 /*000110*/,
+ 3 /*000111*/, 1 /*001000*/, 2 /*001001*/, 2 /*001010*/, 3 /*001011*/, 2 /*001100*/, 3 /*001101*/,
+ 3 /*001110*/, 4 /*001111*/, 1 /*010000*/, 2 /*010001*/, 2 /*010010*/, 3 /*010011*/, 2 /*010100*/,
+ 3 /*010101*/, 3 /*010110*/, 4 /*010111*/, 2 /*011000*/, 3 /*011001*/, 3 /*011010*/, 4 /*011011*/,
+ 3 /*011100*/, 4 /*011101*/, 4 /*011110*/, 5 /*011111*/, 1 /*100000*/, 2 /*100001*/, 2 /*100010*/,
+ 3 /*100011*/, 2 /*100100*/, 3 /*100101*/, 3 /*100110*/, 4 /*100111*/, 2 /*101000*/, 3 /*101001*/,
+ 3 /*101010*/, 4 /*101011*/, 3 /*101100*/, 4 /*101101*/, 4 /*101110*/, 5 /*101111*/, 2 /*110000*/,
+ 3 /*110001*/, 3 /*110010*/, 4 /*110011*/, 3 /*110100*/, 4 /*110101*/, 4 /*110110*/, 5 /*110111*/,
+ 3 /*111000*/, 4 /*111001*/, 4 /*111010*/, 5 /*111011*/, 4 /*111100*/, 5 /*111101*/, 5 /*111110*/,
6 /*111111*/ ];
var numberOfOnes = function(aNumber)
@@ -552,7 +712,7 @@ function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme,
}
if (aTheme && aClass)
- [attribute setParentAttribute:[aTheme _attributeWithName:anAttributeName forClass:aClass]];
+ [attribute setParentAttribute:[aTheme attributeWithName:anAttributeName forClass:aClass]];
return attribute;
}
@@ -586,4 +746,4 @@ for (i = 0;i < Math.pow(2,6);++i)
}
print(str+']');
-*/
\ No newline at end of file
+*/
diff --git a/AppKit/CPThemeBlend.j b/AppKit/CPThemeBlend.j
index cf523037a..b1f58e3df 100644
--- a/AppKit/CPThemeBlend.j
+++ b/AppKit/CPThemeBlend.j
@@ -33,37 +33,60 @@
@implementation CPThemeBlend : CPObject
{
CPBundle _bundle;
- CPArray _themes @accessors(readonly, getter=themes);
+ CPArray _themes;
id _loadDelegate;
}
- (id)initWithContentsOfURL:(CPURL)aURL
{
self = [super init];
-
+
if (self)
{
_bundle = [[CPBundle alloc] initWithPath:aURL];
}
-
+
return self;
}
+/*!
+ Returns an array of names of the keyed theme archives that make up this blend.
+ Each item in the array will have the extension ".keyedtheme".
+*/
+- (CPArray)themes
+{
+ return _themes;
+}
+
+/*!
+ Returns an array of names of the themes that make up this blend.
+*/
+- (CPArray)themeNames
+{
+ var names = [];
+
+ for (var i = 0; i < _themes.length; ++i)
+ names.push(_themes[i].substring(0, _themes[i].indexOf(".keyedtheme")));
+
+ return names;
+}
+
- (void)loadWithDelegate:(id)aDelegate
{
_loadDelegate = aDelegate;
-
+
[_bundle loadWithDelegate:self];
}
- (void)bundleDidFinishLoading:(CPBundle)aBundle
{
- var themes = [_bundle objectForInfoDictionaryKey:@"CPKeyedThemes"],
- count = themes.length;
+ _themes = [_bundle objectForInfoDictionaryKey:@"CPKeyedThemes"];
+
+ var count = _themes.length;
while (count--)
{
- var path = [aBundle pathForResource:themes[count]],
+ var path = [aBundle pathForResource:_themes[count]],
unarchiver = [[_CPThemeKeyedUnarchiver alloc]
initForReadingWithData:[[CPURL URLWithString:path] staticResourceData]
bundle:_bundle];
diff --git a/AppKit/CPToolbar.j b/AppKit/CPToolbar.j
index b45d70211..e3ef772d0 100644
--- a/AppKit/CPToolbar.j
+++ b/AppKit/CPToolbar.j
@@ -563,6 +563,7 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
[_additionalItemsButton setImagePosition:CPImageOnly];
[[_additionalItemsButton menu] setShowsStateColumn:NO];
+ [[_additionalItemsButton menu] setAutoenablesItems:NO];
[_additionalItemsButton setAlternateImage:_CPToolbarViewExtraItemsAlternateImage];
}
@@ -793,14 +794,13 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
hasNonSeparatorItem = YES;
- [_additionalItemsButton addItemWithTitle:[item label]];
-
- var menuItem = [_additionalItemsButton itemArray][index + 1];
+ var menuItem = [[CPMenuItem alloc] initWithTitle:[item label] action:[item action] keyEquivalent:nil];
[menuItem setImage:[item image]];
-
[menuItem setTarget:[item target]];
- [menuItem setAction:[item action]];
+ [menuItem setEnabled:[item isEnabled]];
+
+ [_additionalItemsButton addItem:menuItem];
}
}
else
@@ -1055,6 +1055,8 @@ var TOP_MARGIN = 5.0,
[_imageView setAlphaValue:0.5];
[_labelField setAlphaValue:0.5];
}
+
+ [_toolbar tile];
}
- (CPColor)FIXME_labelColor
diff --git a/AppKit/CPView.j b/AppKit/CPView.j
index 3085a06fe..9b13084f0 100644
--- a/AppKit/CPView.j
+++ b/AppKit/CPView.j
@@ -90,7 +90,7 @@ var CachedNotificationCenter = nil,
#if PLATFORM(DOM)
var DOMElementPrototype = nil,
-
+
BackgroundTrivialColor = 0,
BackgroundVerticalThreePartImage = 1,
BackgroundHorizontalThreePartImage = 2,
@@ -102,7 +102,7 @@ var CPViewFlags = { },
CPViewHasCustomDrawRect = 1 << 0,
CPViewHasCustomLayoutSubviews = 1 << 1;
-/*!
+/*!
@ingroup appkit
@class CPView
@@ -122,37 +122,37 @@ var CPViewFlags = { },
@implementation CPView : CPResponder
{
CPWindow _window;
-
+
CPView _superview;
CPArray _subviews;
-
+
CPGraphicsContext _graphicsContext;
-
+
int _tag;
-
+
CGRect _frame;
CGRect _bounds;
CGAffineTransform _boundsTransform;
CGAffineTransform _inverseBoundsTransform;
-
+
CPSet _registeredDraggedTypes;
CPArray _registeredDraggedTypesArray;
-
+
BOOL _isHidden;
BOOL _hitTests;
BOOL _clipsToBounds;
-
+
BOOL _postsFrameChangedNotifications;
BOOL _postsBoundsChangedNotifications;
BOOL _inhibitFrameAndBoundsChangedNotifications;
-
+
#if PLATFORM(DOM)
DOMElement _DOMElement;
DOMElement _DOMContentsElement;
-
+
CPArray _DOMImageParts;
CPArray _DOMImageSizes;
-
+
unsigned _backgroundType;
#endif
@@ -163,19 +163,19 @@ var CPViewFlags = { },
BOOL _autoresizesSubviews;
unsigned _autoresizingMask;
-
+
CALayer _layer;
BOOL _wantsLayer;
-
+
// Full Screen State
BOOL _isInFullScreenMode;
-
+
_CPViewFullScreenModeState _fullScreenModeState;
-
+
// Layout Support
BOOL _needsLayout;
JSObject _ephemeralSubviews;
-
+
// Theming Support
CPTheme _theme;
JSObject _themeAttributes;
@@ -202,9 +202,9 @@ var CPViewFlags = { },
#if PLATFORM(DOM)
DOMElementPrototype = document.createElement("div");
-
+
var style = DOMElementPrototype.style;
-
+
style.overflow = "hidden";
style.position = "absolute";
style.visibility = "visible";
@@ -262,12 +262,12 @@ var CPViewFlags = { },
- (id)initWithFrame:(CGRect)aFrame
{
self = [super init];
-
+
if (self)
{
var width = _CGRectGetWidth(aFrame),
height = _CGRectGetHeight(aFrame);
-
+
_subviews = [];
_registeredDraggedTypes = [CPSet set];
_registeredDraggedTypesArray = [];
@@ -280,7 +280,7 @@ var CPViewFlags = { },
_autoresizingMask = CPViewNotSizable;
_autoresizesSubviews = YES;
_clipsToBounds = YES;
-
+
_opacity = 1.0;
_isHidden = NO;
_hitTests = YES;
@@ -290,11 +290,11 @@ var CPViewFlags = { },
CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, _CGRectGetMinX(aFrame), _CGRectGetMinY(aFrame));
CPDOMDisplayServerSetStyleSize(_DOMElement, width, height);
-
+
_DOMImageParts = [];
_DOMImageSizes = [];
#endif
-
+
_theme = [CPTheme defaultTheme];
_themeState = CPThemeStateNormal;
@@ -302,7 +302,7 @@ var CPViewFlags = { },
[self _loadThemeAttributes];
}
-
+
return self;
}
@@ -350,15 +350,15 @@ var CPViewFlags = { },
- (void)addSubview:(CPView)aSubview positioned:(CPWindowOrderingMode)anOrderingMode relativeTo:(CPView)anotherView
{
var index = anotherView ? [_subviews indexOfObjectIdenticalTo:anotherView] : CPNotFound;
-
+
// In other words, if no view, then either all the way at the bottom or all the way at the top.
if (index === CPNotFound)
index = (anOrderingMode === CPWindowAbove) ? [_subviews count] : 0;
-
+
// else, if we have a view, above if above.
else if (anOrderingMode === CPWindowAbove)
++index;
-
+
[self _insertSubview:aSubview atIndex:index];
}
@@ -375,20 +375,20 @@ var CPViewFlags = { },
if (aSubview._superview == self)
{
var index = [_subviews indexOfObjectIdenticalTo:aSubview];
-
+
// FIXME: should this be anIndex >= count? (last one)
if (index === anIndex || index === count - 1 && anIndex === count)
return;
-
+
[_subviews removeObjectAtIndex:index];
-
+
#if PLATFORM(DOM)
CPDOMDisplayServerRemoveChild(_DOMElement, aSubview._DOMElement);
#endif
if (anIndex > index)
--anIndex;
-
+
//We've effectively made the subviews array shorter, so represent that.
--count;
}
@@ -397,16 +397,16 @@ var CPViewFlags = { },
// Remove the view from its previous superview.
[aSubview removeFromSuperview];
- // Set the subview's window to our own.
+ // Set the subview's window to our own.
[aSubview _setWindow:_window];
// Notify the subview that it will be moving.
[aSubview viewWillMoveToSuperview:self];
-
+
// Set ourselves as the superview.
aSubview._superview = self;
}
-
+
if (anIndex === CPNotFound || anIndex >= count)
{
_subviews.push(aSubview);
@@ -419,16 +419,16 @@ var CPViewFlags = { },
else
{
_subviews.splice(anIndex, 0, aSubview);
-
+
#if PLATFORM(DOM)
// Attach the actual node.
CPDOMDisplayServerInsertBefore(_DOMElement, aSubview._DOMElement, _subviews[anIndex + 1]._DOMElement);
#endif
}
-
+
[aSubview setNextResponder:self];
[aSubview viewDidMoveToSuperview];
-
+
[self didAddSubview:aSubview];
}
@@ -453,14 +453,14 @@ var CPViewFlags = { },
[[self window] _dirtyKeyViewLoop];
[_superview willRemoveSubview:self];
-
+
[_superview._subviews removeObject:self];
#if PLATFORM(DOM)
CPDOMDisplayServerRemoveChild(_superview._DOMElement, _DOMElement);
#endif
_superview = nil;
-
+
[self _setWindow:nil];
}
@@ -473,11 +473,11 @@ var CPViewFlags = { },
{
if (aSubview._superview != self)
return;
-
+
var index = [_subviews indexOfObjectIdenticalTo:aSubview];
-
+
[aSubview removeFromSuperview];
-
+
[self _insertSubview:aView atIndex:index];
}
@@ -555,7 +555,7 @@ var CPViewFlags = { },
{
if (_window === aWindow)
return;
-
+
[[self window] _dirtyKeyViewLoop];
// Clear out first responder if we're the first responder and leaving.
@@ -579,7 +579,7 @@ var CPViewFlags = { },
while (count--)
[_subviews[count] _setWindow:aWindow];
-
+
[self viewDidMoveToWindow];
[[self window] _dirtyKeyViewLoop];
@@ -592,13 +592,13 @@ var CPViewFlags = { },
- (BOOL)isDescendantOf:(CPView)aView
{
var view = self;
-
+
do
{
if (view == aView)
return YES;
} while(view = [view superview])
-
+
return NO;
}
@@ -649,20 +649,20 @@ var CPViewFlags = { },
- (CPMenuItem)enclosingMenuItem
{
var view = self;
-
+
while (view && ![view isKindOfClass:[_CPMenuItemView class]])
view = [view superview];
-
+
if (view)
return view._menuItem;
-
+
return nil;
/* var view = self,
enclosingMenuItem = _enclosingMenuItem;
-
+
while (!enclosingMenuItem && (view = view._enclosingMenuItem))
view = [view superview];
-
+
return enclosingMenuItem;*/
}
@@ -715,9 +715,9 @@ var CPViewFlags = { },
{
if (_CGRectEqualToRect(_frame, aFrame))
return;
-
+
_inhibitFrameAndBoundsChangedNotifications = YES;
-
+
[self setFrameOrigin:aFrame.origin];
[self setFrameSize:aFrame.size];
@@ -747,19 +747,19 @@ var CPViewFlags = { },
}
/*!
- Moves the center of the receiver's frame to the provided point. The point is defined in the superview's coordinate system.
- The method posts a CPViewFrameDidChangeNotification to the default notification center if the receiver
- is configured to do so. If the specified origin is the same as the frame's current origin, the method will
+ Moves the center of the receiver's frame to the provided point. The point is defined in the superview's coordinate system.
+ The method posts a CPViewFrameDidChangeNotification to the default notification center if the receiver
+ is configured to do so. If the specified origin is the same as the frame's current origin, the method will
simply return (and no notification will be posted).
@param aPoint the new origin point
*/
- (void)setCenter:(CGPoint)aPoint
{
- [self setFrameOrigin:CGPointMake(aPoint.x - _frame.size.width / 2.0, aPoint.y - _frame.size.height / 2.0)];
+ [self setFrameOrigin:CGPointMake(aPoint.x - _frame.size.width / 2.0, aPoint.y - _frame.size.height / 2.0)];
}
/*!
- Returns the center of the receiver's frame to the provided point. The point is defined in the superview's coordinate system.
+ Returns the center of the receiver's frame to the provided point. The point is defined in the superview's coordinate system.
@return CGPoint the center point of the receiver's frame
*/
- (CGPoint)center
@@ -768,16 +768,16 @@ var CPViewFlags = { },
}
/*!
- Sets the receiver's frame origin to the provided point. The point is defined in the superview's coordinate system.
- The method posts a CPViewFrameDidChangeNotification to the default notification center if the receiver
- is configured to do so. If the specified origin is the same as the frame's current origin, the method will
+ Sets the receiver's frame origin to the provided point. The point is defined in the superview's coordinate system.
+ The method posts a CPViewFrameDidChangeNotification to the default notification center if the receiver
+ is configured to do so. If the specified origin is the same as the frame's current origin, the method will
simply return (and no notification will be posted).
@param aPoint the new origin point
*/
- (void)setFrameOrigin:(CGPoint)aPoint
{
var origin = _frame.origin;
-
+
if (!aPoint || _CGPointEqualToPoint(origin, aPoint))
return;
@@ -803,7 +803,7 @@ var CPViewFlags = { },
- (void)setFrameSize:(CGSize)aSize
{
var size = _frame.size;
-
+
if (!aSize || _CGSizeEqualToSize(size, aSize))
return;
@@ -823,7 +823,7 @@ var CPViewFlags = { },
if (_autoresizesSubviews)
[self resizeSubviewsWithOldSize:oldSize];
-
+
[self setNeedsLayout];
[self setNeedsDisplay:YES];
@@ -874,7 +874,7 @@ var CPViewFlags = { },
}
/*!
- Sets the receiver's bounds. The bounds define the size and location of the receiver inside it's frame. Posts a
+ Sets the receiver's bounds. The bounds define the size and location of the receiver inside it's frame. Posts a
CPViewBoundsDidChangeNotification to the default notification center if the receiver is configured to do so.
@param bounds the new bounds
*/
@@ -882,9 +882,9 @@ var CPViewFlags = { },
{
if (_CGRectEqualToRect(_bounds, bounds))
return;
-
+
_inhibitFrameAndBoundsChangedNotifications = YES;
-
+
[self setBoundsOrigin:bounds.origin];
[self setBoundsSize:bounds.size];
@@ -922,13 +922,13 @@ var CPViewFlags = { },
- (void)setBoundsOrigin:(CGPoint)aPoint
{
var origin = _bounds.origin;
-
+
if (_CGPointEqualToPoint(origin, aPoint))
return;
-
+
origin.x = aPoint.x;
origin.y = aPoint.y;
-
+
if (origin.x != 0 || origin.y != 0)
{
_boundsTransform = _CGAffineTransformMakeTranslation(-origin.x, -origin.y);
@@ -939,19 +939,19 @@ var CPViewFlags = { },
_boundsTransform = nil;
_inverseBoundsTransform = nil;
}
-
+
#if PLATFORM(DOM)
var index = _subviews.length;
-
+
while (index--)
{
var view = _subviews[index],
origin = view._frame.origin;
-
+
CPDOMDisplayServerSetStyleLeftTop(view._DOMElement, _boundsTransform, origin.x, origin.y);
}
#endif
-
+
if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
[CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
}
@@ -965,7 +965,7 @@ var CPViewFlags = { },
- (void)setBoundsSize:(CGSize)aSize
{
var size = _bounds.size;
-
+
if (_CGSizeEqualToSize(size, aSize))
return;
@@ -974,22 +974,22 @@ var CPViewFlags = { },
if (!_CGSizeEqualToSize(size, frameSize))
{
var origin = _bounds.origin;
-
+
origin.x /= size.width / frameSize.width;
origin.y /= size.height / frameSize.height;
}
-
+
size.width = aSize.width;
size.height = aSize.height;
-
+
if (!_CGSizeEqualToSize(size, frameSize))
{
var origin = _bounds.origin;
-
+
origin.x *= size.width / frameSize.width;
origin.y *= size.height / frameSize.height;
}
-
+
if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
[CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
}
@@ -1002,7 +1002,7 @@ var CPViewFlags = { },
- (void)resizeWithOldSuperviewSize:(CGSize)aSize
{
var mask = [self autoresizingMask];
-
+
if(mask == CPViewNotSizable)
return;
@@ -1017,7 +1017,7 @@ var CPViewFlags = { },
newFrame.origin.x += dX;
if (mask & CPViewWidthSizable)
newFrame.size.width += dX;
-
+
if (mask & CPViewMinYMargin)
newFrame.origin.y += dY;
if (mask & CPViewHeightSizable)
@@ -1033,7 +1033,7 @@ var CPViewFlags = { },
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
{
var count = _subviews.length;
-
+
while (count--)
[_subviews[count] resizeWithOldSuperviewSize:aSize];
}
@@ -1094,14 +1094,14 @@ var CPViewFlags = { },
- (BOOL)enterFullScreenMode:(CPScreen)aScreen withOptions:(CPDictionary)options
{
_fullScreenModeState = _CPViewFullScreenModeStateMake(self);
-
+
var fullScreenWindow = [[CPWindow alloc] initWithContentRect:[[CPPlatformWindow primaryPlatformWindow] contentBounds] styleMask:CPBorderlessWindowMask];
-
+
[fullScreenWindow setLevel:CPScreenSaverWindowLevel];
[fullScreenWindow setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
-
+
var contentView = [fullScreenWindow contentView];
-
+
[contentView setBackgroundColor:[CPColor blackColor]];
[contentView addSubview:self];
@@ -1109,11 +1109,11 @@ var CPViewFlags = { },
[self setFrame:CGRectMakeCopy([contentView bounds])];
[fullScreenWindow makeKeyAndOrderFront:self];
-
+
[fullScreenWindow makeFirstResponder:self];
-
+
_isInFullScreenMode = YES;
-
+
return YES;
}
@@ -1139,7 +1139,7 @@ var CPViewFlags = { },
[self setFrame:_fullScreenModeState.frame];
[self setAutoresizingMask:_fullScreenModeState.autoresizingMask];
[_fullScreenModeState.superview _insertSubview:self atIndex:_fullScreenModeState.index];
-
+
[[self window] orderOut:self];
}
@@ -1175,17 +1175,41 @@ var CPViewFlags = { },
if ([view isKindOfClass:[CPView class]])
{
- do
+ do
{
if (self == view)
{
[_window makeFirstResponder:[self nextValidKeyView]];
break;
- }
- }
+ }
+ }
while (view = [view superview]);
}
+
+ [self _notifyViewDidHide];
}
+ else
+ {
+ [self _notifyViewDidUnhide];
+ }
+}
+
+- (void)_notifyViewDidHide
+{
+ [self viewDidHide];
+
+ var count = [_subviews count];
+ while (count--)
+ [_subviews[count] _notifyViewDidHide];
+}
+
+- (void)_notifyViewDidUnhide
+{
+ [self viewDidUnhide];
+
+ var count = [_subviews count];
+ while (count--)
+ [_subviews[count] _notifyViewDidUnhide];
}
/*!
@@ -1214,7 +1238,7 @@ var CPViewFlags = { },
}
/*!
- Sets the opacity of the receiver. The value must be in the range of 0.0 to 1.0, where 0.0 is
+ Sets the opacity of the receiver. The value must be in the range of 0.0 to 1.0, where 0.0 is
completely transparent and 1.0 is completely opaque.
@param anAlphaValue an alpha value ranging from 0.0 to 1.0.
*/
@@ -1222,11 +1246,11 @@ var CPViewFlags = { },
{
if (_opacity == anAlphaValue)
return;
-
+
_opacity = anAlphaValue;
-
+
#if PLATFORM(DOM)
-
+
if (CPFeatureIsCompatible(CPOpacityRequiresFilterFeature))
{
if (anAlphaValue === 1.0)
@@ -1252,23 +1276,51 @@ var CPViewFlags = { },
/*!
Returns \c YES if the receiver is hidden, or one
of it's ancestor views is hidden. \c NO, otherwise.
-*/
+*/
- (BOOL)isHiddenOrHasHiddenAncestor
{
var view = self;
-
+
while (view && ![view isHidden])
view = [view superview];
-
+
return view !== nil;
}
+/*!
+ Called when the return value of isHiddenOrHasHiddenAncestor becomes YES,
+ e.g. when this view becomes hidden due to a setHidden:YES message to
+ itself or to one of its superviews.
+
+ Note: in the current implementation, viewDidHide may be called multiple
+ times if additional superviews are hidden, even if
+ isHiddenOrHasHiddenAncestor was already YES.
+*/
+- (void)viewDidHide
+{
+
+}
+
+/*!
+ Called when the return value of isHiddenOrHasHiddenAncestor becomes NO,
+ e.g. when this view stops being hidden due to a setHidden:NO message to
+ itself or to one of its superviews.
+
+ Note: in the current implementation, viewDidUnhide may be called multiple
+ times if additional superviews are unhidden, even if
+ isHiddenOrHasHiddenAncestor was already NO.
+*/
+- (void)viewDidUnhide
+{
+
+}
+
/*!
Returns whether the receiver should be sent a \c -mouseDown: message for \c anEvent.
Returns \c YES by default.
@return \c YES, if the view object accepts first mouse-down event. \c NO, otherwise.
*/
-//FIXME: should be NO by default?
+//FIXME: should be NO by default?
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
{
return YES;
@@ -1301,7 +1353,7 @@ var CPViewFlags = { },
{
if(_isHidden || !_hitTests || !CPRectContainsPoint(_frame, aPoint))
return nil;
-
+
var view = nil,
i = _subviews.length,
adjustedPoint = _CGPointMake(aPoint.x - _CGRectGetMinX(_frame), aPoint.y - _CGRectGetMinY(_frame));
@@ -1364,6 +1416,9 @@ var CPViewFlags = { },
if (_backgroundColor == aColor)
return;
+ if (aColor == [CPNull null])
+ aColor = nil;
+
_backgroundColor = aColor;
#if PLATFORM(DOM)
@@ -1464,7 +1519,7 @@ var CPViewFlags = { },
CPDOMDisplayServerSetStyleSize(_DOMImageParts[5], _DOMImageSizes[5].width, height);
CPDOMDisplayServerSetStyleSize(_DOMImageParts[7], width, _DOMImageSizes[7].height);
- CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[0], NULL, 0.0, 0.0);
+ CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[0], NULL, 0.0, 0.0);
CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[1], NULL, _DOMImageSizes[0].width, 0.0);
CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[2], NULL, 0.0, 0.0);
CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[3], NULL, 0.0, _DOMImageSizes[1].height);
@@ -1472,14 +1527,14 @@ var CPViewFlags = { },
CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[5], NULL, 0.0, _DOMImageSizes[1].height);
CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[6], NULL, 0.0, 0.0);
CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[7], NULL, _DOMImageSizes[6].width, 0.0);
- CPDOMDisplayServerSetStyleRightBottom(_DOMImageParts[8], NULL, 0.0, 0.0);
+ CPDOMDisplayServerSetStyleRightBottom(_DOMImageParts[8], NULL, 0.0, 0.0);
}
else if (_backgroundType == BackgroundVerticalThreePartImage)
- {
+ {
CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], frameSize.width, frameSize.height - _DOMImageSizes[0].height - _DOMImageSizes[2].height);
CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[0], NULL, 0.0, 0.0);
- CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[1], NULL, 0.0, _DOMImageSizes[0].height);
+ CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[1], NULL, 0.0, _DOMImageSizes[0].height);
CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[2], NULL, 0.0, 0.0);
}
else if (_backgroundType == BackgroundHorizontalThreePartImage)
@@ -1487,7 +1542,7 @@ var CPViewFlags = { },
CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], frameSize.width - _DOMImageSizes[0].width - _DOMImageSizes[2].width, frameSize.height);
CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[0], NULL, 0.0, 0.0);
- CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[1], NULL, _DOMImageSizes[0].width, 0.0);
+ CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[1], NULL, _DOMImageSizes[0].width, 0.0);
CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[2], NULL, 0.0, 0.0);
}
}
@@ -1679,7 +1734,7 @@ setBoundsOrigin:
var theWindow = [self window];
[theWindow _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
- [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes]
+ [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes];
[theWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
_registeredDraggedTypesArray = nil;
@@ -1783,20 +1838,25 @@ setBoundsOrigin:
- (void)displayRect:(CPRect)aRect
{
[self viewWillDraw];
-
+
[self displayRectIgnoringOpacity:aRect inContext:nil];
-
+
_dirtyRect = NULL;
}
- (void)displayRectIgnoringOpacity:(CGRect)aRect inContext:(CPGraphicsContext)aGraphicsContext
{
+ if ([self isHidden])
+ return;
+
+#if PLATFORM(DOM)
[self lockFocus];
-
+
CGContextClearRect([[CPGraphicsContext currentContext] graphicsPort], aRect);
-
+
[self drawRect:aRect];
[self unlockFocus];
+#endif
}
- (void)viewWillDraw
@@ -1811,18 +1871,18 @@ setBoundsOrigin:
if (!_graphicsContext)
{
var graphicsPort = CGBitmapGraphicsContextCreate();
-
+
_DOMContentsElement = graphicsPort.DOMElement;
-
+
_DOMContentsElement.style.zIndex = -100;
_DOMContentsElement.style.overflow = "hidden";
_DOMContentsElement.style.position = "absolute";
_DOMContentsElement.style.visibility = "visible";
-
+
_DOMContentsElement.width = ROUND(_CGRectGetWidth(_frame));
_DOMContentsElement.height = ROUND(_CGRectGetHeight(_frame));
-
+
_DOMContentsElement.style.top = "0px";
_DOMContentsElement.style.left = "0px";
_DOMContentsElement.style.width = ROUND(_CGRectGetWidth(_frame)) + "px";
@@ -1833,9 +1893,9 @@ setBoundsOrigin:
#endif
_graphicsContext = [CPGraphicsContext graphicsContextWithGraphicsPort:graphicsPort flipped:YES];
}
-
+
[CPGraphicsContext setCurrentContext:_graphicsContext];
-
+
CGContextSaveGState([_graphicsContext graphicsPort]);
}
@@ -1845,7 +1905,7 @@ setBoundsOrigin:
- (void)unlockFocus
{
CGContextRestoreGState([_graphicsContext graphicsPort]);
-
+
[CPGraphicsContext setCurrentContext:nil];
}
@@ -1864,7 +1924,7 @@ setBoundsOrigin:
if (_needsLayout)
{
_needsLayout = NO;
-
+
[self layoutSubviews];
}
}
@@ -1888,7 +1948,7 @@ setBoundsOrigin:
{
if (!_superview)
return _bounds;
-
+
return CGRectIntersection([self convertRect:[_superview visibleRect] fromView:_superview], _bounds);
}
@@ -1899,7 +1959,7 @@ setBoundsOrigin:
var superview = _superview,
clipViewClass = [CPClipView class];
- while(superview && ![superview isKindOfClass:clipViewClass])
+ while(superview && ![superview isKindOfClass:clipViewClass])
superview = superview._superview;
return superview;
@@ -1912,10 +1972,10 @@ setBoundsOrigin:
- (void)scrollPoint:(CGPoint)aPoint
{
var clipView = [self _enclosingClipView];
-
+
if (!clipView)
return;
-
+
[clipView scrollToPoint:[self convertPoint:aPoint toView:clipView]];
}
@@ -1927,35 +1987,35 @@ setBoundsOrigin:
- (BOOL)scrollRectToVisible:(CGRect)aRect
{
var visibleRect = [self visibleRect];
-
+
// Make sure we have a rect that exists.
aRect = CGRectIntersection(aRect, _bounds);
-
+
// If aRect is empty or is already visible then no scrolling required.
if (_CGRectIsEmpty(aRect) || CGRectContainsRect(visibleRect, aRect))
return NO;
var enclosingClipView = [self _enclosingClipView];
-
+
// If we're not in a clip view, then there isn't much we can do.
if (!enclosingClipView)
return NO;
-
+
var scrollPoint = _CGPointMakeCopy(visibleRect.origin);
-
+
// One of the following has to be true since our current visible rect didn't contain aRect.
if (_CGRectGetMinX(aRect) <= _CGRectGetMinX(visibleRect))
scrollPoint.x = _CGRectGetMinX(aRect);
else if (_CGRectGetMaxX(aRect) > _CGRectGetMaxX(visibleRect))
scrollPoint.x += _CGRectGetMaxX(aRect) - _CGRectGetMaxX(visibleRect);
-
+
if (_CGRectGetMinY(aRect) <= _CGRectGetMinY(visibleRect))
scrollPoint.y = CGRectGetMinY(aRect);
else if (_CGRectGetMaxY(aRect) > _CGRectGetMaxY(visibleRect))
scrollPoint.y += _CGRectGetMaxY(aRect) - _CGRectGetMaxY(visibleRect);
-
+
[enclosingClipView scrollToPoint:CGPointMake(scrollPoint.x, scrollPoint.y)];
-
+
return YES;
}
@@ -1995,7 +2055,7 @@ setBoundsOrigin:
var superview = _superview,
scrollViewClass = [CPScrollView class];
- while(superview && ![superview isKindOfClass:scrollViewClass])
+ while(superview && ![superview isKindOfClass:scrollViewClass])
superview = superview._superview;
return superview;
@@ -2093,7 +2153,7 @@ setBoundsOrigin:
{
if (_layer == aLayer)
return;
-
+
if (_layer)
{
_layer._owningView = nil;
@@ -2101,18 +2161,18 @@ setBoundsOrigin:
_DOMElement.removeChild(_layer._DOMElement);
#endif
}
-
+
_layer = aLayer;
-
+
if (_layer)
{
var bounds = CGRectMakeCopy([self bounds]);
-
+
[_layer _setOwningView:self];
-
+
#if PLATFORM(DOM)
_layer._DOMElement.style.zIndex = 100;
-
+
_DOMElement.appendChild(_layer._DOMElement);
#endif
}
@@ -2224,7 +2284,7 @@ setBoundsOrigin:
}
var attributeDictionary = [theClass themeAttributes];
-
+
if (!attributeDictionary)
continue;
@@ -2257,14 +2317,14 @@ setBoundsOrigin:
themeClass = [theClass themeClass];
_themeAttributes = {};
-
+
while (count--)
{
var attributeName = attributes[count--],
attribute = [[_CPThemeAttribute alloc] initWithName:attributeName defaultValue:attributes[count]];
- [attribute setParentAttribute:[theme _attributeWithName:attributeName forClass:themeClass]];
-
+ [attribute setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
+
_themeAttributes[attributeName] = attribute;
}
}
@@ -2273,9 +2333,9 @@ setBoundsOrigin:
{
if (_theme === aTheme)
return;
-
+
_theme = aTheme;
-
+
[self viewDidChangeTheme];
}
@@ -2294,7 +2354,7 @@ setBoundsOrigin:
for (var attributeName in _themeAttributes)
if (_themeAttributes.hasOwnProperty(attributeName))
- [_themeAttributes[attributeName] setParentAttribute:[theme _attributeWithName:attributeName forClass:themeClass]];
+ [_themeAttributes[attributeName] setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
[self setNeedsLayout];
[self setNeedsDisplay:YES];
@@ -2382,7 +2442,7 @@ setBoundsOrigin:
return _CGRectMakeZero();
}
-- (CPView)layoutEphemeralSubviewNamed:(CPString)aViewName
+- (CPView)layoutEphemeralSubviewNamed:(CPString)aViewName
positioned:(CPWindowOrderingMode)anOrderingMode
relativeToEphemeralSubviewNamed:(CPString)relativeToViewName
{
@@ -2420,6 +2480,14 @@ setBoundsOrigin:
return _ephemeralSubviewsForNames[aViewName];
}
+- (CPView)ephemeralSubviewNamed:(CPString)aViewName
+{
+ if (!_ephemeralSubviewsForNames)
+ return nil;
+
+ return (_ephemeralSubviewsForNames[aViewName] || nil);
+}
+
@end
var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
@@ -2447,9 +2515,9 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
*/
- (id)initWithCoder:(CPCoder)aCoder
{
- // We create the DOMElement "early" because there is a chance that we
- // will decode our superview before we are done decoding, at which point
- // we have to have an element to place in the tree. Perhaps there is
+ // We create the DOMElement "early" because there is a chance that we
+ // will decode our superview before we are done decoding, at which point
+ // we have to have an element to place in the tree. Perhaps there is
// a more "elegant" way to do this...?
#if PLATFORM(DOM)
_DOMElement = DOMElementPrototype.cloneNode(false);
@@ -2460,16 +2528,16 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
_bounds = [aCoder decodeRectForKey:CPViewBoundsKey];
self = [super initWithCoder:aCoder];
-
+
if (self)
{
// We have to manually check because it may be 0, so we can't use ||
_tag = [aCoder containsValueForKey:CPViewTagKey] ? [aCoder decodeIntForKey:CPViewTagKey] : -1;
-
+
_window = [aCoder decodeObjectForKey:CPViewWindowKey];
_subviews = [aCoder decodeObjectForKey:CPViewSubviewsKey] || [];
_superview = [aCoder decodeObjectForKey:CPViewSuperviewKey];
-
+
// FIXME: Should we encode/decode this?
_registeredDraggedTypes = [CPSet set];
_registeredDraggedTypesArray = [];
@@ -2478,7 +2546,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
_autoresizesSubviews = ![aCoder containsValueForKey:CPViewAutoresizesSubviewsKey] || [aCoder decodeBoolForKey:CPViewAutoresizesSubviewsKey];
_hitTests = ![aCoder containsValueForKey:CPViewHitTestsKey] || [aCoder decodeObjectForKey:CPViewHitTestsKey];
-
+
// DOM SETUP
#if PLATFORM(DOM)
_DOMImageParts = [];
@@ -2486,10 +2554,10 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, _CGRectGetMinX(_frame), _CGRectGetMinY(_frame));
CPDOMDisplayServerSetStyleSize(_DOMElement, _CGRectGetWidth(_frame), _CGRectGetHeight(_frame));
-
+
var index = 0,
count = _subviews.length;
-
+
for (; index < count; ++index)
{
CPDOMDisplayServerAppendChild(_DOMElement, _subviews[index]._DOMElement);
@@ -2541,7 +2609,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
-
+
if (_tag !== -1)
[aCoder encodeInt:_tag forKey:CPViewTagKey];
@@ -2611,7 +2679,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
var _CPViewFullScreenModeStateMake = function(aView)
{
var superview = aView._superview;
-
+
return { autoresizingMask:aView._autoresizingMask, frame:CGRectMakeCopy(aView._frame), index:(superview ? [superview._subviews indexOfObjectIdenticalTo:aView] : 0), superview:superview };
}
@@ -2621,93 +2689,93 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView)
sameWindow = YES,
fromWindow = nil,
toWindow = nil;
-
+
if (fromView)
{
var view = fromView;
-
+
// FIXME: This doesn't handle the case when the outside views are equal.
- // If we have a fromView, "climb up" the view tree until
+ // If we have a fromView, "climb up" the view tree until
// we hit the root node or we hit the toLayer.
while (view && view != toView)
{
var frame = view._frame;
-
+
transform.tx += _CGRectGetMinX(frame);
transform.ty += _CGRectGetMinY(frame);
-
+
if (view._boundsTransform)
{
_CGAffineTransformConcatTo(transform, view._boundsTransform, transform);
}
-
+
view = view._superview;
}
-
+
// If we hit toView, then we're done.
if (view === toView)
return transform;
-
+
else if (fromView && toView)
{
fromWindow = [fromView window];
toWindow = [toView window];
-
+
if (fromWindow && toWindow && fromWindow !== toWindow)
{
sameWindow = NO;
-
+
var frame = [fromWindow frame];
-
+
transform.tx += _CGRectGetMinX(frame);
transform.ty += _CGRectGetMinY(frame);
}
}
}
-
+
// FIXME: For now we can do things this way, but eventually we need to do them the "hard" way.
var view = toView;
-
+
while (view)
{
var frame = view._frame;
-
+
transform.tx -= _CGRectGetMinX(frame);
transform.ty -= _CGRectGetMinY(frame);
-
+
if (view._boundsTransform)
{
_CGAffineTransformConcatTo(transform, view._inverseBoundsTransform, transform);
}
-
+
view = view._superview;
}
-
+
if (!sameWindow)
{
var frame = [toWindow frame];
-
+
transform.tx -= _CGRectGetMinX(frame);
transform.ty -= _CGRectGetMinY(frame);
}
/* var views = [],
view = toView;
-
+
while (view)
{
views.push(view);
view = view._superview;
}
-
+
var index = views.length;
-
+
while (index--)
{
var frame = views[index]._frame;
-
+
transform.tx -= _CGRectGetMinX(frame);
transform.ty -= _CGRectGetMinY(frame);
}*/
-
+
return transform;
}
diff --git a/AppKit/CPViewAnimation.j b/AppKit/CPViewAnimation.j
index b1bc4d414..594b638a2 100644
--- a/AppKit/CPViewAnimation.j
+++ b/AppKit/CPViewAnimation.j
@@ -79,9 +79,9 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut";
while (animationIndex--)
{
var dictionary = [_viewAnimations objectAtIndex:animationIndex],
- view = [self _targetView:dictionary]
- startFrame = [self _startFrame:dictionary]
- endFrame = [self _endFrame:dictionary]
+ view = [self _targetView:dictionary],
+ startFrame = [self _startFrame:dictionary],
+ endFrame = [self _endFrame:dictionary],
differenceFrame = _CGRectMakeZero();
differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x;
diff --git a/AppKit/CPViewController.j b/AppKit/CPViewController.j
index 3fffe261d..b5c18de9f 100644
--- a/AppKit/CPViewController.j
+++ b/AppKit/CPViewController.j
@@ -47,7 +47,8 @@ var CPViewControllerCachedCibs;
*/
@implementation CPViewController : CPResponder
{
- CPView _view;
+ CPView _view @accessors(property=view);
+ BOOL _isLoading;
id _representedObject @accessors(property=representedObject);
CPString _title @accessors(property=title);
@@ -99,6 +100,8 @@ var CPViewControllerCachedCibs;
_cibName = aCibNameOrNil;
_cibBundle = aCibBundleOrNil || [CPBundle mainBundle];
_cibExternalNameTable = anExternalNameTable || [CPDictionary dictionaryWithObject:self forKey:CPCibOwner];
+
+ _isLoading = NO;
}
return self;
@@ -111,16 +114,16 @@ var CPViewControllerCachedCibs;
If you create your views manually, you must override this method and use it to create your view and assign it to the view property.
The default implementation for programmatic views is to create a plain view. You can invoke super to utilize this view.
- If you use Interface Builder to create your views and initialize the view controllerŃthat is, you initialize the view using the
- initWithCibName:bundle: methodŃthen you must not override this method. The consequences risk shattering the space-time continuum.
+ If you use Interface Builder to create your views, you initialize the view using the
+ initWithCibName:bundle: method then you must not override this method. The consequences risk shattering the space-time continuum.
- Note: The cib loading system is currently asynchronous.
+ Note: The cib loading system is currently synchronous.
*/
- (void)loadView
{
if (_view)
return;
-
+
// check if a cib is already cached for the current _cibName
var cib = [CPViewControllerCachedCibs objectForKey:_cibName];
@@ -143,6 +146,8 @@ var CPViewControllerCachedCibs;
{
if (!_view)
{
+ _isLoading = YES;
+
var cibOwner = [_cibExternalNameTable objectForKey:CPCibOwner];
if ([cibOwner respondsToSelector:@selector(viewControllerWillLoadCib:)])
@@ -163,6 +168,7 @@ var CPViewControllerCachedCibs;
if ([cibOwner respondsToSelector:@selector(viewControllerDidLoadCib:)])
[cibOwner viewControllerDidLoadCib:self];
+ _isLoading = NO;
[self viewDidLoad];
}
@@ -173,7 +179,7 @@ var CPViewControllerCachedCibs;
/*!
This method is called after the view controller has loaded its associated views into memory.
This method is called regardless of whether the views were stored in a nib file or created programmatically in the loadView method.
- This method is most commonly used to perform additional initialization steps on views that are loaded from nib files.
+ This method is most commonly used to perform additional initialization steps on views that are loaded from cib files.
*/
- (void)viewDidLoad
{
@@ -189,7 +195,13 @@ var CPViewControllerCachedCibs;
*/
- (void)setView:(CPView)aView
{
+ var viewWasLoaded = !_view;
+
_view = aView;
+
+ // Make sure the viewDidLoad method is called if the view is set directly
+ if (!_isLoading && viewWasLoaded)
+ [self viewDidLoad];
}
@end
diff --git a/AppKit/CPWebView.j b/AppKit/CPWebView.j
index bf36a7773..dca2637ab 100644
--- a/AppKit/CPWebView.j
+++ b/AppKit/CPWebView.j
@@ -50,23 +50,23 @@ CPWebViewScrollNative = 2;
{
CPScrollView _scrollView;
CPView _frameView;
-
+
IFrame _iframe;
CPString _mainFrameURL;
CPArray _backwardStack;
CPArray _forwardStack;
-
+
BOOL _ignoreLoadStart;
BOOL _ignoreLoadEnd;
-
+
id _downloadDelegate;
id _frameLoadDelegate;
id _policyDelegate;
id _resourceLoadDelegate;
id _UIDelegate;
-
+
CPWebScriptObject _wso;
-
+
CPString _url;
CPString _html;
@@ -95,10 +95,10 @@ CPWebViewScrollNative = 2;
_backwardStack = [];
_forwardStack = [];
_scrollMode = CPWebViewScrollNative;
-
+
[self _initDOMWithFrame:aFrame];
}
-
+
return self;
}
@@ -106,52 +106,52 @@ CPWebViewScrollNative = 2;
{
_ignoreLoadStart = YES;
_ignoreLoadEnd = YES;
-
+
_iframe = document.createElement("iframe");
_iframe.name = "iframe_" + Math.floor(Math.random()*10000);
_iframe.style.width = "100%";
_iframe.style.height = "100%";
_iframe.style.borderWidth = "0px";
_iframe.frameBorder = "0";
-
+
[self setDrawsBackground:YES];
-
+
_loadCallback = function() {
// HACK: this block handles the case where we don't know about loads initiated by the user clicking a link
if (!_ignoreLoadStart)
{
// post the start load notification
[self _startedLoading];
-
+
if (_mainFrameURL)
[_backwardStack addObject:_mainFrameURL];
-
+
// FIXME: this doesn't actually get the right URL for different domains. Not possible due to browser security restrictions.
_mainFrameURL = _iframe.src;
_mainFrameURL = _iframe.src;
-
+
// clear the forward
[_forwardStack removeAllObjects];
}
else
_ignoreLoadStart = NO;
-
+
if (!_ignoreLoadEnd)
{
[self _finishedLoading];
}
else
_ignoreLoadEnd = NO;
-
+
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
}
-
+
if (_iframe.addEventListener)
_iframe.addEventListener("load", _loadCallback, false);
else if (_iframe.attachEvent)
_iframe.attachEvent("onload", _loadCallback);
-
-
+
+
_frameView = [[CPView alloc] initWithFrame:[self bounds]];
[_frameView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
@@ -159,9 +159,9 @@ CPWebViewScrollNative = 2;
[_scrollView setAutohidesScrollers:YES];
[_scrollView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
[_scrollView setDocumentView:_frameView];
-
+
_frameView._DOMElement.appendChild(_iframe);
-
+
[self _setScrollMode:_scrollMode];
[self addSubview:_scrollView];
@@ -169,7 +169,7 @@ CPWebViewScrollNative = 2;
- (void)setFrameSize:(CPSize)aSize
-{
+{
[super setFrameSize:aSize];
[self _resizeWebFrame];
}
@@ -211,7 +211,7 @@ CPWebViewScrollNative = 2;
{
var visibleRect = [_frameView visibleRect];
[_frameView setFrameSize:CGSizeMake(CGRectGetMaxX(visibleRect), CGRectGetMaxY(visibleRect))];
-
+
// try to get the document size so we can correctly set the frame
var win = null;
try { win = [self DOMWindow]; } catch (e) {}
@@ -229,7 +229,7 @@ CPWebViewScrollNative = 2;
else
{
CPLog.warn("using default size 800*1600");
-
+
[_frameView setFrameSize:CGSizeMake(800, 1600)];
}
@@ -242,7 +242,7 @@ CPWebViewScrollNative = 2;
{
if (_scrollMode == aScrollMode)
return;
-
+
[self _setScrollMode:aScrollMode];
}
@@ -252,7 +252,7 @@ CPWebViewScrollNative = 2;
_scrollMode = CPWebViewScrollNative;
else
_scrollMode = aScrollMode;
-
+
_ignoreLoadStart = YES;
_ignoreLoadEnd = YES;
@@ -263,16 +263,16 @@ CPWebViewScrollNative = 2;
{
[_scrollView setHasHorizontalScroller:YES];
[_scrollView setHasVerticalScroller:YES];
-
+
_iframe.setAttribute("scrolling", "no");
}
else
{
[_scrollView setHasHorizontalScroller:NO];
[_scrollView setHasVerticalScroller:NO];
-
+
_iframe.setAttribute("scrolling", "auto");
-
+
[_frameView setFrameSize:[_scrollView bounds].size];
}
@@ -293,13 +293,13 @@ CPWebViewScrollNative = 2;
[_frameView setFrameSize:[_scrollView contentSize]];
[self _startedLoading];
-
+
_ignoreLoadStart = YES;
_ignoreLoadEnd = NO;
-
+
_url = null;
_html = aString;
-
+
[self _load];
}
@@ -308,13 +308,13 @@ CPWebViewScrollNative = 2;
[self _setScrollMode:CPWebViewScrollNative];
[self _startedLoading];
-
+
_ignoreLoadStart = YES;
_ignoreLoadEnd = NO;
-
+
_url = _mainFrameURL;
_html = null;
-
+
[self _load];
}
@@ -335,12 +335,13 @@ CPWebViewScrollNative = 2;
_loadHTMLStringTimer = nil;
}
- // need to give the browser a chance to reset iframe, otherwise we'll be document.write()-ing the previous document
+ // need to give the browser a chance to reset iframe, otherwise we'll be document.write()-ing the previous document
_loadHTMLStringTimer = window.setTimeout(function()
{
var win = [self DOMWindow];
-
- win.document.write(_html);
+
+ if (win)
+ win.document.write(_html);
window.setTimeout(_loadCallback, 1);
}, 0);
@@ -372,7 +373,7 @@ CPWebViewScrollNative = 2;
}
- (void)setMainFrameURL:(CPString)URLString
-{
+{
if (_mainFrameURL)
[_backwardStack addObject:_mainFrameURL];
_mainFrameURL = URLString;
@@ -389,9 +390,9 @@ CPWebViewScrollNative = 2;
[_forwardStack addObject:_mainFrameURL];
_mainFrameURL = [_backwardStack lastObject];
[_backwardStack removeLastObject];
-
+
[self _loadMainFrameURL];
-
+
return YES;
}
return NO;
@@ -405,9 +406,9 @@ CPWebViewScrollNative = 2;
[_backwardStack addObject:_mainFrameURL];
_mainFrameURL = [_forwardStack lastObject];
[_forwardStack removeLastObject];
-
+
[self _loadMainFrameURL];
-
+
return YES;
}
return NO;
@@ -630,7 +631,7 @@ CPWebViewScrollNative = 2;
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
-
+
if (self)
{
// FIXME: encode/decode these?
@@ -638,14 +639,14 @@ CPWebViewScrollNative = 2;
_backwardStack = [];
_forwardStack = [];
_scrollMode = CPWebViewScrollNative;
-
+
#if PLATFORM(DOM)
[self _initDOMWithFrame:[self frame]];
#endif
[self setBackgroundColor:[CPColor whiteColor]];
}
-
+
return self;
}
diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j
index 00cd3b8de..a3816c870 100644
--- a/AppKit/CPWindow/CPWindow.j
+++ b/AppKit/CPWindow/CPWindow.j
@@ -200,6 +200,8 @@ var SHADOW_MARGIN_LEFT = 20.0,
var CPWindowSaveImage = nil,
CPWindowSavingImage = nil;
+var CPWindowResizeTime = 0.2;
+
/*!
@ingroup appkit
@class CPWindow
@@ -320,6 +322,8 @@ var CPWindowSaveImage = nil,
CPDictionary _sheetContext;
CPWindow _parentView;
BOOL _isSheet;
+
+ _CPWindowFrameAnimation _frameAnimation;
}
/*
@@ -659,9 +663,10 @@ CPTexturedBackgroundWindowMask
if (shouldAnimate)
{
- var animation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame];
+ [_frameAnimation stopAnimation];
+ _frameAnimation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame];
- [animation startAnimation];
+ [_frameAnimation startAnimation];
}
else
{
@@ -1359,10 +1364,21 @@ CPTexturedBackgroundWindowMask
*/
- (void)center
{
+ if (_isFullPlatformWindow)
+ return;
+
var size = [self frame].size,
containerSize = [CPPlatform isBrowser] ? [_platformWindow contentBounds].size : [[self screen] visibleFrame].size;
- [self setFrameOrigin:CGPointMake((containerSize.width - size.width) / 2.0, (containerSize.height - size.height) / 2.0)];
+ var origin = CGPointMake((containerSize.width - size.width) / 2.0, (containerSize.height - size.height) / 2.0);
+
+ if (origin.x < 0.0)
+ origin.x = 0.0;
+
+ if (origin.y < 0.0)
+ origin.y = 0.0;
+
+ [self setFrameOrigin:origin];
}
/*!
@@ -1376,8 +1392,18 @@ CPTexturedBackgroundWindowMask
switch (type)
{
+ case CPFlagsChanged: return [[self firstResponder] flagsChanged:anEvent];
+
case CPKeyUp: return [[self firstResponder] keyUp:anEvent];
- case CPKeyDown: return [[self firstResponder] keyDown:anEvent];
+
+ case CPKeyDown: [[self firstResponder] keyDown:anEvent];
+
+ // Trigger the default button if needed
+ if (![self disableKeyEquivalentForDefaultButton])
+ if ([anEvent _triggersKeyEquivalent:[[self defaultButton] keyEquivalent] withModifierMask:[[self defaultButton] keyEquivalentModifierMask]])
+ [[self defaultButton] performClick:self];
+
+ return;
case CPScrollWheel: return [[_windowView hitTest:point] scrollWheel:anEvent];
@@ -1600,7 +1626,7 @@ CPTexturedBackgroundWindowMask
if (!pasteboardTypes)
return;
- [_inclusiveRegisteredDraggedTypes minusSet:pasteboardTypes]
+ [_inclusiveRegisteredDraggedTypes minusSet:pasteboardTypes];
if ([_inclusiveRegisteredDraggedTypes count] === 0)
_inclusiveRegisteredDraggedTypes = nil;
@@ -1631,7 +1657,7 @@ CPTexturedBackgroundWindowMask
return;
[self _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
- [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes]
+ [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes];
[self _noteRegisteredDraggedTypes:_registeredDraggedTypes];
_registeredDraggedTypesArray = nil;
@@ -1644,7 +1670,7 @@ CPTexturedBackgroundWindowMask
- (CPArray)registeredDraggedTypes
{
if (!_registeredDraggedTypesArray)
- _registeredDraggedTypesArray = [_registeredDraggedTypes allObjects]
+ _registeredDraggedTypesArray = [_registeredDraggedTypes allObjects];
return _registeredDraggedTypesArray;
}
@@ -1942,10 +1968,16 @@ CPTexturedBackgroundWindowMask
else
{
var mainMenu = [CPApp mainMenu],
- menuWindow = mainMenu ? mainMenu._menuWindow : nil;
+ menuBarClass = objj_getClass("_CPMenuBarWindow"),
+ menuWindow;
+
for (var i = 0; i < windowCount; i++)
{
var currentWindow = allWindows[i];
+
+ if ([currentWindow isKindOfClass:menuBarClass])
+ menuWindow = currentWindow;
+
if (currentWindow === self || currentWindow === menuWindow)
continue;
@@ -1971,10 +2003,16 @@ CPTexturedBackgroundWindowMask
else
{
var mainMenu = [CPApp mainMenu],
- menuWindow = mainMenu ? mainMenu._menuWindow : nil;
+ menuBarClass = objj_getClass("_CPMenuBarWindow"),
+ menuWindow;
+
for (var i = 0; i < windowCount; i++)
{
var currentWindow = allWindows[i];
+
+ if ([currentWindow isKindOfClass:menuBarClass])
+ menuWindow = currentWindow;
+
if (currentWindow === self || currentWindow === menuWindow)
continue;
@@ -2054,11 +2092,17 @@ CPTexturedBackgroundWindowMask
- (void)_setFrame:(CGRect)aFrame delegate:(id)delegate duration:(int)duration curve:(CPAnimationCurve)curve
{
- var animation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame];
- [animation setDelegate:delegate];
- [animation setAnimationCurve:curve];
- [animation setDuration:duration];
- [animation startAnimation];
+ [_frameAnimation stopAnimation];
+ _frameAnimation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame];
+ [_frameAnimation setDelegate:delegate];
+ [_frameAnimation setAnimationCurve:curve];
+ [_frameAnimation setDuration:duration];
+ [_frameAnimation startAnimation];
+}
+
+- (CPTimeInterval)animationResizeTime:(CGRect)newWindowFrame
+{
+ return CPWindowResizeTime;
}
/* @ignore */
@@ -2109,7 +2153,7 @@ CPTexturedBackgroundWindowMask
[aSheet setFrame:startFrame display:YES animate:NO];
_sheetContext["opened"] = YES;
- [aSheet _setFrame:endFrame delegate:self duration:0.2 curve:CPAnimationEaseOut];
+ [aSheet _setFrame:endFrame delegate:self duration:[self animationResizeTime:endFrame] curve:CPAnimationEaseOut];
// Should run the main loop here until _isAnimating = FALSE
[aSheet becomeKeyWindow];
@@ -2130,7 +2174,7 @@ CPTexturedBackgroundWindowMask
[self _setUpMasksForView:sheetContent];
_sheetContext["opened"] = NO;
- [sheet _setFrame:endFrame delegate:self duration:0.2 curve:CPAnimationEaseIn];
+ [sheet _setFrame:endFrame delegate:self duration:[self animationResizeTime:endFrame] curve:CPAnimationEaseIn];
}
/* @ignore */
@@ -2159,13 +2203,15 @@ CPTexturedBackgroundWindowMask
[self _restoreMasksForView:sheetContent];
var delegate = _sheetContext["modalDelegate"],
- endSelector = _sheetContext["endSelector"];
-
- if (delegate != nil && endSelector != nil)
- objj_msgSend(delegate, endSelector, sheet, _sheetContext["returnCode"], _sheetContext["contextInfo"]);
+ endSelector = _sheetContext["endSelector"],
+ returnCode = _sheetContext["returnCode"],
+ contextInfo = _sheetContext["contextInfo"];
_sheetContext = nil;
sheet._parentView = nil;
+
+ if (delegate != nil && endSelector != nil)
+ objj_msgSend(delegate, endSelector, sheet, returnCode, contextInfo);
}
- (void)_setUpMasksForView:(CPView)aView
@@ -2238,7 +2284,7 @@ CPTexturedBackgroundWindowMask
return NO;
}
-- (void)performKeyEquivalent:(CPEvent)anEvent
+- (BOOL)performKeyEquivalent:(CPEvent)anEvent
{
// FIXME: should we be starting at the root, in other words _windowView?
// The evidence seems to point to no...
@@ -2249,14 +2295,11 @@ CPTexturedBackgroundWindowMask
{
// It's not clear why we do performKeyEquivalent again here...
// Perhaps to allow something to happen between sendEvent: and keyDown:?
- if (![anEvent _couldBeKeyEquivalent] || ![self performKeyEquivalent:anEvent])
- [self interpretKeyEvents:[anEvent]];
-}
+ if ([anEvent _couldBeKeyEquivalent] && [self performKeyEquivalent:anEvent])
+ return;
-- (void)insertNewline:(id)sender
-{
- if (_defaultButton && _defaultButtonEnabled)
- [_defaultButton performClick:nil];
+ // Interpret the key events
+ [self interpretKeyEvents:[anEvent]];
}
- (void)insertTab:(id)sender
@@ -2369,11 +2412,16 @@ CPTexturedBackgroundWindowMask
- (void)setDefaultButton:(CPButton)aButton
{
- [_defaultButton setDefaultButton:NO];
+ if (_defaultButton === aButton)
+ return;
+
+ if ([_defaultButton keyEquivalent] === CPCarriageReturnCharacter)
+ [_defaultButton setKeyEquivalent:nil];
_defaultButton = aButton;
- [_defaultButton setDefaultButton:YES];
+ if ([_defaultButton keyEquivalent] !== CPCarriageReturnCharacter)
+ [_defaultButton setKeyEquivalent:CPCarriageReturnCharacter];
}
- (CPButton)defaultButton
@@ -2634,7 +2682,7 @@ var interpolate = function(fromValue, toValue, progress)
- (id)initWithWindow:(CPWindow)aWindow targetFrame:(CGRect)aTargetFrame
{
- self = [super initWithDuration:0.2 animationCurve:CPAnimationLinear];
+ self = [super initWithDuration:[aWindow animationResizeTime:aTargetFrame] animationCurve:CPAnimationLinear];
if (self)
{
diff --git a/AppKit/CPWindow/_CPHUDWindowView.j b/AppKit/CPWindow/_CPHUDWindowView.j
index ad0efd630..f10167577 100644
--- a/AppKit/CPWindow/_CPHUDWindowView.j
+++ b/AppKit/CPWindow/_CPHUDWindowView.j
@@ -43,21 +43,21 @@ var HUD_TITLEBAR_HEIGHT = 26.0;
_CPHUDWindowViewBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
[
- [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground0.png"] size:CPSizeMake(6.0, 78.0)],
- [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground1.png"] size:CPSizeMake(1.0, 78.0)],
- [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground2.png"] size:CPSizeMake(6.0, 78.0)],
+ [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground0.png"] size:CPSizeMake(7.0, 37.0)],
+ [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground1.png"] size:CPSizeMake(1.0, 37.0)],
+ [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground2.png"] size:CPSizeMake(7.0, 37.0)],
- [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground3.png"] size:CPSizeMake(6.0, 1.0)],
- [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground4.png"] size:CPSizeMake(5.0, 5.0)],
- [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground5.png"] size:CPSizeMake(6.0, 1.0)],
+ [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground3.png"] size:CPSizeMake(7.0, 1.0)],
+ [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground4.png"] size:CPSizeMake(2.0, 2.0)],
+ [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground5.png"] size:CPSizeMake(7.0, 1.0)],
- [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground6.png"] size:CPSizeMake(6.0, 6.0)],
- [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground7.png"] size:CPSizeMake(6.0, 6.0)],
- [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground8.png"] size:CPSizeMake(6.0, 6.0)]
+ [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground6.png"] size:CPSizeMake(7.0, 3.0)],
+ [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground7.png"] size:CPSizeMake(1.0, 3.0)],
+ [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground8.png"] size:CPSizeMake(7.0, 3.0)]
]]];
- _CPHUDWindowViewCloseImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowClose.png"] size:CPSizeMake(20.0, 20.0)];
- _CPHUDWindowViewCloseActiveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowCloseActive.png"] size:CPSizeMake(20.0, 20.0)];
+ _CPHUDWindowViewCloseImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowClose.png"] size:CPSizeMake(18.0, 18.0)];
+ _CPHUDWindowViewCloseActiveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowCloseActive.png"] size:CPSizeMake(18.0, 18.0)];
}
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
@@ -148,7 +148,7 @@ var HUD_TITLEBAR_HEIGHT = 26.0;
{
var closeSize = [_CPHUDWindowViewCloseImage size];
- _closeButton = [[CPButton alloc] initWithFrame:CGRectMake(4.0, 4.0, closeSize.width, closeSize.height)];
+ _closeButton = [[CPButton alloc] initWithFrame:CGRectMake(8.0, 5.0, closeSize.width, closeSize.height)];
[_closeButton setBordered:NO];
diff --git a/AppKit/CPWindowController.j b/AppKit/CPWindowController.j
index aebbc5e89..2d011af98 100644
--- a/AppKit/CPWindowController.j
+++ b/AppKit/CPWindowController.j
@@ -133,7 +133,7 @@
if (_window)
return;
- [[CPBundle bundleForClass:[_cibOwner class]] loadCibFile:[self windowCibPath] externalNameTable:[CPDictionary dictionaryWithObject:_cibOwner forKey:CPCibOwner]];
+ [[CPBundle mainBundle] loadCibFile:[self windowCibPath] externalNameTable:[CPDictionary dictionaryWithObject:_cibOwner forKey:CPCibOwner]];
}
/*!
@@ -424,7 +424,7 @@
if (_windowCibPath)
return _windowCibPath;
- return [[CPBundle bundleForClass:[_cibOwner class]] pathForResource:_windowCibName + @".cib"];
+ return [[CPBundle mainBundle] pathForResource:_windowCibName + @".cib"];
}
// Setting and Getting Window Attributes
diff --git a/AppKit/Cib/CPCib.j b/AppKit/Cib/CPCib.j
index f9a3eeafc..5cc8e4fd2 100644
--- a/AppKit/Cib/CPCib.j
+++ b/AppKit/Cib/CPCib.j
@@ -150,7 +150,7 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
var topLevelObjects = [anExternalNameTable objectForKey:CPCibTopLevelObjects];
- [objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects]
+ [objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects];
[objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects];
[objectData awakeWithOwner:owner topLevelObjects:topLevelObjects];
diff --git a/AppKit/Cib/CPCibControlConnector.j b/AppKit/Cib/CPCibControlConnector.j
index 817982caf..68934e572 100644
--- a/AppKit/Cib/CPCibControlConnector.j
+++ b/AppKit/Cib/CPCibControlConnector.j
@@ -39,9 +39,11 @@
// Not having a selector is a fatal error.
if (!selector)
+ {
[CPException
raise:CPInvalidArgumentException
reason:@"-[" + [self className] + ' ' + _cmd + @"] selector " + selectorName + @" does not exist."];
+ }
// If the destination doesn't respond to this selector, warn but don't die.
if (_destination && ![_destination respondsToSelector:selector])
@@ -58,7 +60,7 @@
else
[CPException
raise:CPInvalidArgumentException
- reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + " does not respond to setAction:"];
+ reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + @" does not respond to setAction:"];
// Not being able to set the target is a fatal error.
if ([_source respondsToSelector:@selector(setTarget:)])
@@ -67,10 +69,14 @@
else
[CPException
raise:CPInvalidArgumentException
- reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + " does not respond to setTarget:"];
+ reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + @" does not respond to setTarget:"];
}
@end
-@implementation _CPCibControlConnector : CPCibControlConnector { } @end
+@implementation _CPCibControlConnector : CPCibControlConnector
+{
+}
+
+@end
diff --git a/AppKit/Cib/_CPCibCustomResource.j b/AppKit/Cib/_CPCibCustomResource.j
index 06e7d189e..995cbef67 100644
--- a/AppKit/Cib/_CPCibCustomResource.j
+++ b/AppKit/Cib/_CPCibCustomResource.j
@@ -46,28 +46,28 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
- (id)initWithClassName:(CPString)aClassName resourceName:(CPString)aResourceName properties:(CPDictionary)properties
{
self = [super init];
-
+
if (self)
{
_className = aClassName;
_resourceName = aResourceName;
_properties = properties;
}
-
+
return self;
}
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super init];
-
+
if (self)
{
_className = [aCoder decodeObjectForKey:_CPCibCustomResourceClassNameKey];
_resourceName = [aCoder decodeObjectForKey:_CPCibCustomResourceResourceNameKey];
_properties = [aCoder decodeObjectForKey:_CPCibCustomResourcePropertiesKey];
}
-
+
return self;
}
@@ -80,7 +80,7 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
- (id)awakeAfterUsingCoder:(CPCoder)aCoder
{
- if ([aCoder respondsToSelector:@selector(bundle)] &&
+ if ([aCoder respondsToSelector:@selector(bundle)] &&
(![aCoder respondsToSelector:@selector(awakenCustomResources)] || [aCoder awakenCustomResources]))
if (_className === @"CPImage")
return [[CPImage alloc] initWithContentsOfFile:[[aCoder bundle] pathForResource:_resourceName] size:_properties.valueForKey(@"size")];
@@ -112,4 +112,14 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
return NO;
}
+- (unsigned)loadStatus
+{
+ return CPImageLoadStatusCompleted;
+}
+
+- (id)delegate
+{
+ return nil;
+}
+
@end
diff --git a/AppKit/CoreGraphics/CGGeometry.h b/AppKit/CoreGraphics/CGGeometry.h
index 094c5a6bd..dbfef2039 100644
--- a/AppKit/CoreGraphics/CGGeometry.h
+++ b/AppKit/CoreGraphics/CGGeometry.h
@@ -63,6 +63,7 @@
#define _CGInsetMakeCopy(anInset) _CGInsetMake(anInset.top, anInset.right, anInset.bottom, anInset.left)
#define _CGInsetMakeZero() _CGInsetMake(0, 0, 0, 0)
#define _CGInsetIsEmpty(anInset) ((anInset).top === 0 && (anInset).right === 0 && (anInset).bottom === 0 && (anInset).left === 0)
+#define _CGInsetEqualToInset(lhsInset, rhsInset) ((lhsInset).top === (rhsInset).top && (lhsInset).right === (rhsInset).right && (lhsInset).bottom === (rhsInset).bottom && (lhsInset).left === (rhsInset).left)
// DEPRECATED
#define _CGPointCreateCopy(aPoint) _CGPointMake(aPoint.x, aPoint.y)
diff --git a/AppKit/CoreGraphics/CGGeometry.j b/AppKit/CoreGraphics/CGGeometry.j
index 3c2902ddb..bd670e5bf 100644
--- a/AppKit/CoreGraphics/CGGeometry.j
+++ b/AppKit/CoreGraphics/CGGeometry.j
@@ -69,6 +69,12 @@ _function(CGInsetMake(top, right, bottom, left))
_function(CGInsetMakeZero())
_function(CGInsetMakeCopy(anInset))
_function(CGInsetIsEmpty(anInset))
+_function(CGInsetEqualToInset(lhsInset, rhsInset))
+
+CGMinXEdge = 0;
+CGMinYEdge = 1;
+CGMaxXEdge = 2;
+CGMaxYEdge = 3;
CGRectNull = _CGRectMake(Infinity, Infinity, 0.0, 0.0);
@@ -77,6 +83,50 @@ CGRectNull = _CGRectMake(Infinity, Infinity, 0.0, 0.0);
@{
*/
+/*!
+ Creates two rectangles -- slice and rem -- from inRect, by dividing inRect
+ with a line that's parallel to the side of inRect specified by edge.
+ The size of slice is determined by amount, which specifies the distance from edge.
+
+ slice and rem must not be NULL, must not be the same object, and must not be the
+ same object as inRect.
+
+ @group CGRect
+*/
+function CGRectDivide(inRect, slice, rem, amount, edge)
+{
+ slice.origin = _CGPointMakeCopy(inRect.origin);
+ slice.size = _CGSizeMakeCopy(inRect.size);
+ rem.origin = _CGPointMakeCopy(inRect.origin);
+ rem.size = _CGSizeMakeCopy(inRect.size);
+
+ switch (edge)
+ {
+ case CGMinXEdge:
+ slice.size.width = amount;
+ rem.origin.x += amount;
+ rem.size.width -= amount;
+ break;
+
+ case CGMaxXEdge:
+ slice.origin.x = _CGRectGetMaxX(slice) - amount;
+ slice.size.width = amount;
+ rem.size.width -= amount;
+ break;
+
+ case CGMinYEdge:
+ slice.size.height = amount;
+ rem.origin.y += amount;
+ rem.size.height -= amount;
+ break;
+
+ case CGMaxYEdge:
+ slice.origin.y = _CGRectGetMaxY(slice) - amount;
+ slice.size.height = amount;
+ rem.size.height -= amount;
+ }
+}
+
/*!
Returns a \c BOOL indicating whether CGRect \c lhsRect
contains CGRect \c rhsRect.
@@ -88,7 +138,7 @@ CGRectNull = _CGRectMake(Infinity, Infinity, 0.0, 0.0);
function CGRectContainsRect(lhsRect, rhsRect)
{
var union = CGRectUnion(lhsRect, rhsRect);
-
+
return _CGRectEqualToRect(union, lhsRect);
}
@@ -102,12 +152,12 @@ function CGRectContainsRect(lhsRect, rhsRect)
function CGRectIntersectsRect(lhsRect, rhsRect)
{
var intersection = CGRectIntersection(lhsRect, rhsRect);
-
+
return !_CGRectIsEmpty(intersection);
}
/*!
- Makes the origin and size of a CGRect all integers. Specifically, by making
+ Makes the origin and size of a CGRect all integers. Specifically, by making
the southwest corner the origin (rounded down), and the northeast corner a CGSize (rounded up).
@param aRect the rectangle to operate on
@return CGRect the modified rectangle (same as the input)
@@ -120,7 +170,7 @@ function CGRectIntegral(aRect)
// Store these out separately, if not the GetMaxes will return incorrect values.
var x = FLOOR(_CGRectGetMinX(aRect)),
y = FLOOR(_CGRectGetMinY(aRect));
-
+
aRect.size.width = CEIL(_CGRectGetMaxX(aRect)) - x;
aRect.size.height = CEIL(_CGRectGetMaxY(aRect)) - y;
@@ -140,18 +190,18 @@ function CGRectIntegral(aRect)
function CGRectIntersection(lhsRect, rhsRect)
{
var intersection = _CGRectMake(
- MAX(_CGRectGetMinX(lhsRect), _CGRectGetMinX(rhsRect)),
- MAX(_CGRectGetMinY(lhsRect), _CGRectGetMinY(rhsRect)),
+ MAX(_CGRectGetMinX(lhsRect), _CGRectGetMinX(rhsRect)),
+ MAX(_CGRectGetMinY(lhsRect), _CGRectGetMinY(rhsRect)),
0, 0);
-
+
intersection.size.width = MIN(_CGRectGetMaxX(lhsRect), _CGRectGetMaxX(rhsRect)) - _CGRectGetMinX(intersection);
intersection.size.height = MIN(_CGRectGetMaxY(lhsRect), _CGRectGetMaxY(rhsRect)) - _CGRectGetMinY(intersection);
-
+
return _CGRectIsEmpty(intersection) ? _CGRectMakeZero() : intersection;
}
/*
-
+
*/
function CGRectStandardize(aRect)
{
@@ -189,28 +239,28 @@ function CGRectUnion(lhsRect, rhsRect)
minY = MIN(_CGRectGetMinY(lhsRect), _CGRectGetMinY(rhsRect)),
maxX = MAX(_CGRectGetMaxX(lhsRect), _CGRectGetMaxX(rhsRect)),
maxY = MAX(_CGRectGetMaxY(lhsRect), _CGRectGetMaxY(rhsRect));
-
+
return _CGRectMake(minX, minY, maxX - minX, maxY - minY);
}
function CGPointFromString(aString)
{
var comma = aString.indexOf(',');
-
+
return { x:parseInt(aString.substr(1, comma - 1)), y:parseInt(aString.substring(comma + 1, aString.length)) };
}
function CGSizeFromString(aString)
{
var comma = aString.indexOf(',');
-
+
return { width:parseInt(aString.substr(1, comma - 1)), height:parseInt(aString.substring(comma + 1, aString.length)) };
}
function CGRectFromString(aString)
{
var comma = aString.indexOf(',', aString.indexOf(',') + 1);
-
+
return { origin:CGPointFromString(aString.substr(1, comma - 1)), size:CGSizeFromString(aString.substring(comma + 2, aString.length)) };
}
@@ -233,6 +283,6 @@ function CPStringFromCGInset(anInset)
return '{' + anInset.top + ", " + anInset.left + ", " + anInset.bottom + ", " + anInset.right + '}';
}
-/*!
- @}
+/*!
+ @}
*/
diff --git a/AppKit/Platform/CPPlatformWindow.j b/AppKit/Platform/CPPlatformWindow.j
index baaab6396..e7e126ec6 100644
--- a/AppKit/Platform/CPPlatformWindow.j
+++ b/AppKit/Platform/CPPlatformWindow.j
@@ -42,6 +42,8 @@ var PrimaryPlatformWindow = NULL;
DOMElement _DOMBodyElement;
DOMElement _DOMFocusElement;
DOMElement _DOMEventGuard;
+ DOMElement _DOMScrollingElement;
+ id _hideDOMScrollingElementTimeout;
CPArray _windowLevels;
CPDictionary _windowLayers;
diff --git a/AppKit/Platform/DOM/CPPlatformString.j b/AppKit/Platform/DOM/CPPlatformString.j
index 592a3b332..ab908ce65 100644
--- a/AppKit/Platform/DOM/CPPlatformString.j
+++ b/AppKit/Platform/DOM/CPPlatformString.j
@@ -24,7 +24,11 @@
var DOMFixedWidthSpanElement = nil,
DOMFlexibleWidthSpanElement = nil,
+ DOMMetricsDivElement = nil,
+ DOMMetricsTextSpanElement = nil,
+ DOMMetricsImgElement = nil,
DOMIFrameElement = nil,
+ DOMIFrameDocument = nil,
DefaultFont = nil;
@implementation CPPlatformString : CPBasePlatformString
@@ -59,7 +63,7 @@ var DOMFixedWidthSpanElement = nil,
bodyElement.appendChild(DOMIFrameElement);
- var DOMIFrameDocument = (DOMIFrameElement.contentDocument || DOMIFrameElement.contentWindow.document);
+ DOMIFrameDocument = (DOMIFrameElement.contentDocument || DOMIFrameElement.contentWindow.document);
DOMIFrameDocument.write(''+
'');
DOMIFrameDocument.close();
@@ -88,6 +92,7 @@ var DOMFixedWidthSpanElement = nil,
style.margin = "0px";
style.width = "1px";
style.wordWrap = "break-word";
+
try
{
style.whiteSpace = "pre";
@@ -106,6 +111,46 @@ var DOMFixedWidthSpanElement = nil,
DOMDivElement.appendChild(DOMFixedWidthSpanElement);
}
++ (void)createDOMMetricsElements
+{
+ if (!DOMIFrameElement)
+ [self createDOMElements];
+
+ var style;
+
+ DOMMetricsDivElement = DOMIFrameDocument.createElement("div");
+ DOMMetricsDivElement.style.position = "absolute";
+ DOMMetricsDivElement.style.width = "100000px";
+
+ DOMIFrameDocument.body.appendChild(DOMMetricsDivElement);
+
+ DOMMetricsTextSpanElement = DOMIFrameDocument.createElement("span");
+ DOMMetricsTextSpanElement.innerHTML = "x";
+ style = DOMMetricsTextSpanElement.style;
+ style.position = "absolute";
+ style.visibility = "visible";
+ style.padding = "0px";
+ style.margin = "0px";
+ style.whiteSpace = "pre";
+
+ var imgPath = [[CPBundle bundleForClass:[CPView class]] pathForResource:@"empty.png"];
+
+ DOMMetricsImgElement = DOMIFrameDocument.createElement("img");
+ DOMMetricsImgElement.setAttribute("src", imgPath);
+ DOMMetricsImgElement.setAttribute("width", "1");
+ DOMMetricsImgElement.setAttribute("height", "1");
+ DOMMetricsImgElement.setAttribute("alt", "");
+ style = DOMMetricsImgElement.style;
+ style.visibility = "visible";
+ style.padding = "0px";
+ style.margin = "0px";
+ style.border = "none";
+ style.verticalAlign = "baseline";
+
+ DOMMetricsDivElement.appendChild(DOMMetricsTextSpanElement);
+ DOMMetricsDivElement.appendChild(DOMMetricsImgElement);
+}
+
+ (CGSize)sizeOfString:(CPString)aString withFont:(CPFont)aFont forWidth:(float)aWidth
{
if (!aFont)
@@ -120,6 +165,7 @@ var DOMFixedWidthSpanElement = nil,
[self createDOMElements];
var span;
+
if (!aWidth)
span = DOMFlexibleWidthSpanElement;
else
@@ -138,4 +184,26 @@ var DOMFixedWidthSpanElement = nil,
return _CGSizeMake(span.clientWidth, span.clientHeight);
}
++ (CPDictionary)metricsOfFont:(CPFont)aFont
+{
+ if (!aFont)
+ {
+ if (!DefaultFont)
+ DefaultFont = [CPFont systemFontOfSize:12.0];
+
+ aFont = DefaultFont;
+ }
+
+ if (!DOMMetricsDivElement)
+ [self createDOMMetricsElements];
+
+ DOMMetricsDivElement.style.font = [aFont cssString];
+
+ var baseline = DOMMetricsImgElement.offsetTop - DOMMetricsTextSpanElement.offsetTop + DOMMetricsImgElement.offsetHeight,
+ descender = baseline - DOMMetricsTextSpanElement.offsetHeight,
+ lineHeight = DOMMetricsTextSpanElement.offsetHeight;
+
+ return [CPDictionary dictionaryWithObjectsAndKeys:baseline, @"ascender", descender, @"descender", lineHeight, @"lineHeight"];
+}
+
@end
diff --git a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j
index e5bd7a19d..8924510f8 100644
--- a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j
+++ b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j
@@ -136,25 +136,58 @@ var CPDOMEventGetClickCount,
//might be mac only, we should investigate futher later.
var KeyCodesToPrevent = {},
CharacterKeysToPrevent = {},
+ KeyCodesToAllow = {},
MozKeyCodeToKeyCodeMap = {
61: 187, // =, equals
59: 186 // ;, semicolon
},
- KeyCodesToFunctionUnicodeMap = {};
+ KeyCodesToUnicodeMap = {};
KeyCodesToPrevent[CPKeyCodes.A] = YES;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.BACKSPACE] = CPBackspaceCharacter;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.DELETE] = CPDeleteCharacter;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.TAB] = CPTabCharacter;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.ENTER] = CPCarriageReturnCharacter;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.ESC] = CPEscapeFunctionKey;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.PAGE_UP] = CPPageUpFunctionKey;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.PAGE_DOWN] = CPPageDownFunctionKey;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.LEFT] = CPLeftArrowFunctionKey;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.UP] = CPUpArrowFunctionKey;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.RIGHT] = CPRightArrowFunctionKey;
-KeyCodesToFunctionUnicodeMap[CPKeyCodes.DOWN] = CPDownArrowFunctionKey;
+KeyCodesToAllow[CPKeyCodes.F1] = YES;
+KeyCodesToAllow[CPKeyCodes.F2] = YES;
+KeyCodesToAllow[CPKeyCodes.F3] = YES;
+KeyCodesToAllow[CPKeyCodes.F4] = YES;
+KeyCodesToAllow[CPKeyCodes.F5] = YES;
+KeyCodesToAllow[CPKeyCodes.F6] = YES;
+KeyCodesToAllow[CPKeyCodes.F7] = YES;
+KeyCodesToAllow[CPKeyCodes.F8] = YES;
+KeyCodesToAllow[CPKeyCodes.F9] = YES;
+KeyCodesToAllow[CPKeyCodes.F10] = YES;
+KeyCodesToAllow[CPKeyCodes.F11] = YES;
+KeyCodesToAllow[CPKeyCodes.F12] = YES;
+
+KeyCodesToUnicodeMap[CPKeyCodes.BACKSPACE] = CPDeleteCharacter;
+KeyCodesToUnicodeMap[CPKeyCodes.DELETE] = CPDeleteFunctionKey;
+KeyCodesToUnicodeMap[CPKeyCodes.TAB] = CPTabCharacter;
+KeyCodesToUnicodeMap[CPKeyCodes.ENTER] = CPCarriageReturnCharacter;
+KeyCodesToUnicodeMap[CPKeyCodes.ESC] = CPEscapeFunctionKey;
+KeyCodesToUnicodeMap[CPKeyCodes.PAGE_UP] = CPPageUpFunctionKey;
+KeyCodesToUnicodeMap[CPKeyCodes.PAGE_DOWN] = CPPageDownFunctionKey;
+KeyCodesToUnicodeMap[CPKeyCodes.LEFT] = CPLeftArrowFunctionKey;
+KeyCodesToUnicodeMap[CPKeyCodes.UP] = CPUpArrowFunctionKey;
+KeyCodesToUnicodeMap[CPKeyCodes.RIGHT] = CPRightArrowFunctionKey;
+KeyCodesToUnicodeMap[CPKeyCodes.DOWN] = CPDownArrowFunctionKey;
+KeyCodesToUnicodeMap[CPKeyCodes.SEMICOLON] = ";";
+KeyCodesToUnicodeMap[CPKeyCodes.DASH] = "-";
+KeyCodesToUnicodeMap[CPKeyCodes.EQUALS] = "=";
+KeyCodesToUnicodeMap[CPKeyCodes.COMMA] = ",";
+KeyCodesToUnicodeMap[CPKeyCodes.PERIOD] = ".";
+KeyCodesToUnicodeMap[CPKeyCodes.SLASH] = "/";
+KeyCodesToUnicodeMap[CPKeyCodes.APOSTROPHE] = "`";
+KeyCodesToUnicodeMap[CPKeyCodes.SINGLE_QUOTE] = "'";
+KeyCodesToUnicodeMap[CPKeyCodes.OPEN_SQUARE_BRACKET] = "[";
+KeyCodesToUnicodeMap[CPKeyCodes.BACKSLASH] = "\\";
+KeyCodesToUnicodeMap[CPKeyCodes.CLOSE_SQUARE_BRACKET] = "]";
+
+var ModifierKeyCodes = [
+ CPKeyCodes.META,
+ CPKeyCodes.MAC_FF_META,
+ CPKeyCodes.CTRL,
+ CPKeyCodes.ALT,
+ CPKeyCodes.SHIFT
+];
var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
@@ -283,6 +316,29 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
_DOMEventGuard.style.display = "none";
_DOMEventGuard.className = "cpdontremove";
_DOMBodyElement.appendChild(_DOMEventGuard);
+
+ // We get scrolling deltas from this element
+ _DOMScrollingElement = theDocument.createElement("div");
+ _DOMScrollingElement.style.position = "absolute";
+ _DOMScrollingElement.style.visibility = "hidden";
+ _DOMScrollingElement.style.zIndex = "998";
+ _DOMScrollingElement.style.height = "60px";
+ _DOMScrollingElement.style.width = "60px";
+ _DOMScrollingElement.style.overflow = "scroll";
+ //_DOMScrollingElement.style.backgroundColor = "rgba(0,0,0,1.0)"; // debug help.
+ _DOMScrollingElement.style.opacity = "0";
+ _DOMScrollingElement.style.filter = "alpha(opacity=0)";
+ _DOMScrollingElement.className = "cpdontremove";
+ _DOMBodyElement.appendChild(_DOMScrollingElement);
+
+ var _DOMInnerScrollingElement = theDocument.createElement("div");
+ _DOMInnerScrollingElement.style.width = "400px";
+ _DOMInnerScrollingElement.style.height = "400px";
+ _DOMScrollingElement.appendChild(_DOMInnerScrollingElement);
+
+ // Set an initial scroll offset
+ _DOMScrollingElement.scrollTop = 150;
+ _DOMScrollingElement.scrollLeft = 150;
}
- (void)registerDOMWindow
@@ -542,7 +598,6 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
[dragServer draggingStartedInPlatformWindow:self globalLocation:[CPPlatform isBrowser] ? location : _CGPointMake(aDOMEvent.screenX, aDOMEvent.screenY)];
}
-
else if (type === "drag")
{
var y = aDOMEvent.screenY;
@@ -552,7 +607,6 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
[dragServer draggingSourceUpdatedWithGlobalLocation:[CPPlatform isBrowser] ? location : _CGPointMake(aDOMEvent.screenX, y)];
}
-
else if (type === "dragover" || type === "dragleave")
{
if (aDOMEvent.preventDefault)
@@ -563,16 +617,13 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
if (dragOperation === CPDragOperationMove || dragOperation === CPDragOperationGeneric || dragOperation === CPDragOperationPrivate)
dropEffect = "move";
-
else if (dragOperation === CPDragOperationCopy)
dropEffect = "copy";
-
else if (dragOperation === CPDragOperationLink)
dropEffect = "link";
aDOMEvent.dataTransfer.dropEffect = dropEffect;
}
-
else if (type === "dragend")
{
var dropEffect = aDOMEvent.dataTransfer.dropEffect;
@@ -588,7 +639,6 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
[dragServer draggingEndedInPlatformWindow:self globalLocation:[CPPlatform isBrowser] ? location : _CGPointMake(aDOMEvent.screenX, aDOMEvent.screenY) operation:dragOperation];
}
-
else //if (type === "drop")
{
[dragServer performDragOperationInPlatformWindow:self];
@@ -607,18 +657,37 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
- (void)keyEvent:(DOMEvent)aDOMEvent
{
var event,
- timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(),
- sourceElement = (aDOMEvent.target || aDOMEvent.srcElement),
+ timestamp = aDOMEvent.timeStamp || new Date(),
+ sourceElement = aDOMEvent.target || aDOMEvent.srcElement,
windowNumber = [[CPApp keyWindow] windowNumber],
modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) |
(aDOMEvent.ctrlKey ? CPControlKeyMask : 0) |
(aDOMEvent.altKey ? CPAlternateKeyMask : 0) |
(aDOMEvent.metaKey ? CPCommandKeyMask : 0);
- //We want to stop propagation if this is a command key AND this character or keycode has been added to our blacklist
- StopDOMEventPropagation = !!(!(modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) ||
- CharacterKeysToPrevent[String.fromCharCode(aDOMEvent.keyCode || aDOMEvent.charCode).toLowerCase()] ||
- KeyCodesToPrevent[aDOMEvent.keyCode]);
+ // With a few exceptions, all key events are blocked from propagating to
+ // the browser. Here the following exceptions are being allowed:
+ //
+ // - All keys pressed along with a ctrl or cmd key _unless_ they are in
+ // one of the two blacklists.
+ // - Any key listed in the whitelist.
+ //
+ // The ctrl/cmd keys are used for browser hotkeys as are the keys listed in
+ // the whitelist (F1-F12 at the time of writing).
+ //
+ // If a key is listed in both the blacklist and whitelist, the blacklist is
+ // checked first. The key will be blocked from propagating in that case.
+
+ StopDOMEventPropagation = YES;
+
+ // Make sure it is not in the blacklists.
+ if (!(CharacterKeysToPrevent[String.fromCharCode(aDOMEvent.keyCode || aDOMEvent.charCode).toLowerCase()] || KeyCodesToPrevent[aDOMEvent.keyCode]))
+ {
+ // It is not in the blacklist, let it through if the ctrl/cmd key is
+ // also down or it's in the whitelist.
+ if ((modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) || KeyCodesToAllow[aDOMEvent.keyCode])
+ StopDOMEventPropagation = NO;
+ }
var isNativePasteEvent = NO,
isNativeCopyOrCutEvent = NO,
@@ -627,19 +696,36 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
switch (aDOMEvent.type)
{
case "keydown": // Grab and store the keycode now since it is correct and consistent at this point.
- if (aDOMEvent.keyCode.keyCode in MozKeyCodeToKeyCodeMap)
+ if (aDOMEvent.keyCode in MozKeyCodeToKeyCodeMap)
_keyCode = MozKeyCodeToKeyCodeMap[aDOMEvent.keyCode];
else
_keyCode = aDOMEvent.keyCode;
- var characters = KeyCodesToFunctionUnicodeMap[_keyCode] || String.fromCharCode(_keyCode).toLowerCase();
+ var characters;
+
+ // Handle key codes for which String.fromCharCode won't work.
+ if (aDOMEvent.which === 0 || aDOMEvent.charCode === 0)
+ characters = KeyCodesToUnicodeMap[_keyCode];
+
+ if (!characters)
+ characters = String.fromCharCode(_keyCode).toLowerCase();
+
overrideCharacters = (modifierFlags & CPShiftKeyMask || _capsLockActive) ? characters.toUpperCase() : characters;
// check for caps lock state
if (_keyCode === CPKeyCodes.CAPS_LOCK)
_capsLockActive = YES;
- if (modifierFlags & (CPControlKeyMask | CPCommandKeyMask))
+ if ([ModifierKeyCodes containsObject:_keyCode])
+ {
+ // A modifier key will never fire keypress. We don't need to do any other processing so we just fire it here and break.
+ event = [CPEvent keyEventWithType:CPFlagsChanged location:location modifierFlags:modifierFlags
+ timestamp:timestamp windowNumber:windowNumber context:nil
+ characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:_keyCode];
+
+ break;
+ }
+ else if (modifierFlags & (CPControlKeyMask | CPCommandKeyMask))
{
//we are simply going to skip all keypress events that use cmd/ctrl key
//this lets us be consistent in all browsers and send on the keydown
@@ -696,8 +782,15 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
_lastKey = keyCode;
_charCodes[keyCode] = charCode;
- var characters = overrideCharacters || KeyCodesToFunctionUnicodeMap[charCode] || String.fromCharCode(charCode),
- charactersIgnoringModifiers = characters.toLowerCase();
+ var characters = overrideCharacters;
+ // Is this a special key?
+ if (!characters && (aDOMEvent.which === 0 || aDOMEvent.charCode === 0))
+ characters = KeyCodesToUnicodeMap[charCode];
+
+ if (!characters)
+ characters = String.fromCharCode(charCode);
+
+ charactersIgnoringModifiers = characters.toLowerCase(); // FIXME: This isn't correct. It SHOULD include Shift.
// Safari won't send proper capitalization during cmd-key events
if (!overrideCharacters && (modifierFlags & CPCommandKeyMask) && ((modifierFlags & CPShiftKeyMask) || _capsLockActive))
@@ -705,7 +798,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags
timestamp:timestamp windowNumber:windowNumber context:nil
- characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:isARepeat keyCode:keyCode];
+ characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:isARepeat keyCode:charCode];
if (isNativePasteEvent)
{
@@ -728,7 +821,10 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
if (keyCode === CPKeyCodes.CAPS_LOCK)
_capsLockActive = NO;
- var characters = KeyCodesToFunctionUnicodeMap[charCode] || String.fromCharCode(charCode),
+ if ([ModifierKeyCodes containsObject:keyCode])
+ break;
+
+ var characters = KeyCodesToUnicodeMap[charCode] || String.fromCharCode(charCode),
charactersIgnoringModifiers = characters.toLowerCase();
if (!(modifierFlags & CPShiftKeyMask) && (modifierFlags & CPCommandKeyMask) && !_capsLockActive)
@@ -862,7 +958,13 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
- (void)scrollEvent:(DOMEvent)aDOMEvent
{
- if(!aDOMEvent)
+ if (_hideDOMScrollingElementTimeout)
+ {
+ clearTimeout(_hideDOMScrollingElementTimeout);
+ _hideDOMScrollingElementTimeout = nil;
+ }
+
+ if (!aDOMEvent)
aDOMEvent = window.event;
var location = nil;
@@ -881,7 +983,6 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
{
x += element.offsetLeft;
y += element.offsetTop;
-
} while (element = element.offsetParent);
}
@@ -901,7 +1002,13 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
(aDOMEvent.altKey ? CPAlternateKeyMask : 0) |
(aDOMEvent.metaKey ? CPCommandKeyMask : 0);
- StopDOMEventPropagation = YES;
+ // Show the dom element
+ _DOMScrollingElement.style.visibility = "visible";
+ _DOMScrollingElement.style.top = (location.y - 15) + @"px";
+ _DOMScrollingElement.style.left = (location.x - 15) + @"px";
+
+ // We let the browser handle the scrolling
+ StopDOMEventPropagation = NO;
var theWindow = [self hitTest:location];
@@ -912,40 +1019,54 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
location = [theWindow convertBridgeToBase:location];
- if(typeof aDOMEvent.wheelDeltaX != "undefined")
- {
- deltaX = aDOMEvent.wheelDeltaX / 120.0;
- deltaY = aDOMEvent.wheelDeltaY / 120.0;
- }
-
- else if (aDOMEvent.wheelDelta)
- deltaY = aDOMEvent.wheelDelta / 120.0;
-
- else if (aDOMEvent.detail)
- deltaY = -aDOMEvent.detail / 3.0;
-
- else
- return;
-
- if(!CPFeatureIsCompatible(CPJavaScriptNegativeMouseWheelValues))
- {
- deltaX = -deltaX;
- deltaY = -deltaY;
- }
-
var event = [CPEvent mouseEventWithType:CPScrollWheel location:location modifierFlags:modifierFlags
- timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0 ];
-
+ timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0];
event._DOMEvent = aDOMEvent;
- event._deltaX = deltaX;
- event._deltaY = deltaY;
- [CPApp sendEvent:event];
+ // We lag 1 event behind without this timeout.
+ setTimeout(function()
+ {
+ // Find the scroll delta
+ var deltaX = _DOMScrollingElement.scrollLeft - 150,
+ deltaY = _DOMScrollingElement.scrollTop - 150;
- if (StopDOMEventPropagation)
- CPDOMEventStop(aDOMEvent, self);
+ // If we scroll super with momentum,
+ // there are so many events going off that
+ // a tiny percent don't actually have any deltas.
+ //
+ // This does *not* make scrolling appear sluggish,
+ // it just seems like that is something that happens.
+ //
+ // We get free performance boost if we skip sending these events,
+ // as sending a scroll event with no deltas doesn't do anything.
+ if (deltaX || deltaY)
+ {
+ event._deltaX = deltaX;
+ event._deltaY = deltaY;
- [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
+ [CPApp sendEvent:event];
+ }
+
+ // We set StopDOMEventPropagation = NO on line 1008
+ //if (StopDOMEventPropagation)
+ // CPDOMEventStop(aDOMEvent, self);
+
+ // Reset the DOM elements scroll offset
+ _DOMScrollingElement.scrollLeft = 150;
+ _DOMScrollingElement.scrollTop = 150;
+
+ // Is this needed?
+ //[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
+
+ }, 0);
+
+ // We hide the dom element after a little bit
+ // so that other DOM elements such as inputs
+ // can receive events.
+ _hideDOMScrollingElementTimeout = setTimeout(function()
+ {
+ _DOMScrollingElement.style.visibility = "hidden";
+ }, 300);
}
- (void)resizeEvent:(DOMEvent)aDOMEvent
@@ -1007,8 +1128,8 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
newEvent.shiftKey = newEvent.ctrlKey = newEvent.altKey = newEvent.metaKey = false;
- newEvent.preventDefault = function(){if(aDOMEvent.preventDefault) aDOMEvent.preventDefault()};
- newEvent.stopPropagation = function(){if(aDOMEvent.stopPropagation) aDOMEvent.stopPropagation()};
+ newEvent.preventDefault = function() { if (aDOMEvent.preventDefault) aDOMEvent.preventDefault() };
+ newEvent.stopPropagation = function() { if (aDOMEvent.stopPropagation) aDOMEvent.stopPropagation() };
[self mouseEvent:newEvent];
@@ -1057,7 +1178,6 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
if (_mouseDownWindow)
windowNumber = [_mouseDownWindow windowNumber];
-
else
{
var theWindow = [self hitTest:location];
@@ -1073,7 +1193,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
if (type === "mouseup")
{
- if(_mouseIsDown)
+ if (_mouseIsDown)
{
event = _CPEventFromNativeMouseEvent(aDOMEvent, _mouseDownIsRightClick ? CPRightMouseUp : CPLeftMouseUp, location, modifierFlags, timestamp, windowNumber, nil, -1, CPDOMEventGetClickCount(_lastMouseUp, timestamp, location), 0);
@@ -1083,7 +1203,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
_mouseDownIsRightClick = NO;
}
- if(_DOMEventMode)
+ if (_DOMEventMode)
{
_DOMEventMode = NO;
return;
@@ -1092,6 +1212,9 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
else if (type === "mousedown")
{
+ var button = aDOMEvent.button;
+ _mouseDownIsRightClick = button == 2 || (CPBrowserIsOperatingSystem(CPMacOperatingSystem) && button == 0 && modifierFlags & CPControlKeyMask);
+
if (sourceElement.tagName === "INPUT" && sourceElement != _DOMFocusElement)
{
if ([CPPlatform supportsDragAndDrop])
@@ -1104,11 +1227,11 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
_mouseIsDown = YES;
//fake a down and up event so that event tracking mode will work correctly
- [CPApp sendEvent:[CPEvent mouseEventWithType:CPLeftMouseDown location:location modifierFlags:modifierFlags
+ [CPApp sendEvent:[CPEvent mouseEventWithType:_mouseDownIsRightClick ? CPRightMouseDown : CPLeftMouseDown location:location modifierFlags:modifierFlags
timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1
clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]];
- [CPApp sendEvent:[CPEvent mouseEventWithType:CPLeftMouseUp location:location modifierFlags:modifierFlags
+ [CPApp sendEvent:[CPEvent mouseEventWithType:_mouseDownIsRightClick ? CPRightMouseUp : CPLeftMouseUp location:location modifierFlags:modifierFlags
timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1
clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]];
@@ -1120,9 +1243,6 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
_DOMBodyElement.style["-khtml-user-drag"] = "element";
}
- var button = aDOMEvent.button;
- _mouseDownIsRightClick = button == 2 || (button == 0 && modifierFlags & CPControlKeyMask);
-
StopContextMenuDOMEventPropagation = YES;
event = _CPEventFromNativeMouseEvent(aDOMEvent, _mouseDownIsRightClick ? CPRightMouseDown : CPLeftMouseDown, location, modifierFlags, timestamp, windowNumber, nil, -1, CPDOMEventGetClickCount(_lastMouseDown, timestamp, location), 0);
@@ -1152,7 +1272,18 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
CPDOMEventStop(aDOMEvent, self);
// if there are any tracking event listeners then show the event guard so we don't lose events to iframes
- _DOMEventGuard.style.display = (CPApp._eventListeners.length === 0) ? "none" : "";
+ // TODO Actually check for tracking event listeners, not just any listener but _CPRunModalLoop.
+ var hasTrackingEventListener = NO;
+ for (var i=0; i < CPApp._eventListeners.length; i++)
+ {
+ if (CPApp._eventListeners[i]._callback !== _CPRunModalLoop)
+ {
+ hasTrackingEventListener = YES;
+ break;
+ }
+ }
+
+ _DOMEventGuard.style.display = hasTrackingEventListener ? "" : "none";
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
}
@@ -1356,7 +1487,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
*/
+ (void)preventCharacterKeysFromPropagating:(CPArray)characters
{
- for(var i=characters.length; i>0; i--)
+ for (var i = characters.length; i > 0; i--)
CharacterKeysToPrevent[""+characters[i-1].toLowerCase()] = YES;
}
@@ -1382,7 +1513,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
*/
+ (void)preventKeyCodesFromPropagating:(CPArray)keyCodes
{
- for(var i=keyCodes.length; i>0; i--)
+ for (var i = keyCodes.length; i > 0; i--)
KeyCodesToPrevent[keyCodes[i-1]] = YES;
}
diff --git a/AppKit/Resources/CPAlert/LICENSE b/AppKit/Resources/CPAlert/LICENSE
deleted file mode 100644
index 4fa7dc4fb..000000000
--- a/AppKit/Resources/CPAlert/LICENSE
+++ /dev/null
@@ -1,58 +0,0 @@
-These graphics come from the Tango Desktop Project (http://tango.freedesktop.org/Tango_Desktop_Project).
-They are released under the Creative Commons Attribution Share-Alike license, full text below.
-
-
-(license available from CC here: http://creativecommons.org/licenses/by-sa/2.5/)
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
-"Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-"Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-"Licensor" means the individual or entity that offers the Work under the terms of this License.
-"Original Author" means the individual or entity who created the Work.
-"Work" means the copyrightable work of authorship offered under the terms of this License.
-"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-"License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
-to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-to create and reproduce Derivative Works;
-to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
-For the avoidance of doubt, where the work is a musical composition:
-
-Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
-Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
-Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
-You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
-You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
-If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
-This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-8. Miscellaneous
-
-Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
diff --git a/AppKit/Resources/CPAlert/dialog-error.png b/AppKit/Resources/CPAlert/dialog-error.png
deleted file mode 100644
index aa4ae7509..000000000
Binary files a/AppKit/Resources/CPAlert/dialog-error.png and /dev/null differ
diff --git a/AppKit/Resources/CPAlert/dialog-information.png b/AppKit/Resources/CPAlert/dialog-information.png
deleted file mode 100644
index cbaf6dbc4..000000000
Binary files a/AppKit/Resources/CPAlert/dialog-information.png and /dev/null differ
diff --git a/AppKit/Resources/CPAlert/dialog-warning.png b/AppKit/Resources/CPAlert/dialog-warning.png
deleted file mode 100644
index 47d84e49d..000000000
Binary files a/AppKit/Resources/CPAlert/dialog-warning.png and /dev/null differ
diff --git a/AppKit/Resources/CPMenuItem/CPMenuItemOnState.png b/AppKit/Resources/CPMenuItem/CPMenuItemOnState.png
index 7ea275e07..614ae28f1 100644
Binary files a/AppKit/Resources/CPMenuItem/CPMenuItemOnState.png and b/AppKit/Resources/CPMenuItem/CPMenuItemOnState.png differ
diff --git a/AppKit/Resources/CPMenuItem/CPMenuItemOnStateHighlighted.png b/AppKit/Resources/CPMenuItem/CPMenuItemOnStateHighlighted.png
index 78f90eddb..f1a9c8360 100644
Binary files a/AppKit/Resources/CPMenuItem/CPMenuItemOnStateHighlighted.png and b/AppKit/Resources/CPMenuItem/CPMenuItemOnStateHighlighted.png differ
diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground0.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground0.png
index f43a3aad2..49f0f1bbf 100644
Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground0.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground0.png differ
diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground1.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground1.png
index ee4dba58a..03afa8531 100644
Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground1.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground1.png differ
diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground2.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground2.png
index 76f41a64e..049910091 100644
Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground2.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground2.png differ
diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground3.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground3.png
index a38e4dad1..3ba64c822 100644
Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground3.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground3.png differ
diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground4.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground4.png
index 4d450f53a..eb0bc87f3 100644
Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground4.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground4.png differ
diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground5.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground5.png
index b49ebef35..260cc579c 100644
Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground5.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground5.png differ
diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground6.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground6.png
index a2cebe44f..01fe3ad8a 100644
Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground6.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground6.png differ
diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground7.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground7.png
index d1b3faf37..6300ed5d6 100644
Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground7.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground7.png differ
diff --git a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground8.png b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground8.png
index 60e25e8e0..14c5c58e1 100644
Binary files a/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground8.png and b/AppKit/Resources/CPWindow/HUD/CPWindowHUDBackground8.png differ
diff --git a/AppKit/Resources/HUDTheme/WindowClose.png b/AppKit/Resources/HUDTheme/WindowClose.png
index 4d3d405cf..17ba6f603 100644
Binary files a/AppKit/Resources/HUDTheme/WindowClose.png and b/AppKit/Resources/HUDTheme/WindowClose.png differ
diff --git a/AppKit/Resources/HUDTheme/WindowCloseActive.png b/AppKit/Resources/HUDTheme/WindowCloseActive.png
index f42282358..5fa29fed3 100644
Binary files a/AppKit/Resources/HUDTheme/WindowCloseActive.png and b/AppKit/Resources/HUDTheme/WindowCloseActive.png differ
diff --git a/AppKit/Resources/empty.png b/AppKit/Resources/empty.png
new file mode 100644
index 000000000..f38e9f910
Binary files /dev/null and b/AppKit/Resources/empty.png differ
diff --git a/AppKit/Themes/Aristo/Resources/alert-error.png b/AppKit/Themes/Aristo/Resources/alert-error.png
new file mode 100644
index 000000000..edded8310
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/alert-error.png differ
diff --git a/AppKit/Themes/Aristo/Resources/alert-info.png b/AppKit/Themes/Aristo/Resources/alert-info.png
new file mode 100644
index 000000000..8ebf63789
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/alert-info.png differ
diff --git a/AppKit/Themes/Aristo/Resources/alert-warning.png b/AppKit/Themes/Aristo/Resources/alert-warning.png
new file mode 100644
index 000000000..c5e480421
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/alert-warning.png differ
diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-disabled.png b/AppKit/Themes/Aristo/Resources/check-box-image-disabled.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/check-box-bezel-disabled.png
rename to AppKit/Themes/Aristo/Resources/check-box-image-disabled.png
diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-highlighted.png b/AppKit/Themes/Aristo/Resources/check-box-image-highlighted.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/check-box-bezel-highlighted.png
rename to AppKit/Themes/Aristo/Resources/check-box-image-highlighted.png
diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed-disabled.png b/AppKit/Themes/Aristo/Resources/check-box-image-mixed-disabled.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/check-box-bezel-mixed-disabled.png
rename to AppKit/Themes/Aristo/Resources/check-box-image-mixed-disabled.png
diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed-highlighted.png b/AppKit/Themes/Aristo/Resources/check-box-image-mixed-highlighted.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/check-box-bezel-mixed-highlighted.png
rename to AppKit/Themes/Aristo/Resources/check-box-image-mixed-highlighted.png
diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-mixed.png b/AppKit/Themes/Aristo/Resources/check-box-image-mixed.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/check-box-bezel-mixed.png
rename to AppKit/Themes/Aristo/Resources/check-box-image-mixed.png
diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-selected-disabled.png b/AppKit/Themes/Aristo/Resources/check-box-image-selected-disabled.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/check-box-bezel-selected-disabled.png
rename to AppKit/Themes/Aristo/Resources/check-box-image-selected-disabled.png
diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-selected-highlighted.png b/AppKit/Themes/Aristo/Resources/check-box-image-selected-highlighted.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/check-box-bezel-selected-highlighted.png
rename to AppKit/Themes/Aristo/Resources/check-box-image-selected-highlighted.png
diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel-selected.png b/AppKit/Themes/Aristo/Resources/check-box-image-selected.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/check-box-bezel-selected.png
rename to AppKit/Themes/Aristo/Resources/check-box-image-selected.png
diff --git a/AppKit/Themes/Aristo/Resources/check-box-bezel.png b/AppKit/Themes/Aristo/Resources/check-box-image.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/check-box-bezel.png
rename to AppKit/Themes/Aristo/Resources/check-box-image.png
diff --git a/AppKit/Themes/Aristo/Resources/radio-bezel-disabled.png b/AppKit/Themes/Aristo/Resources/radio-image-disabled.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/radio-bezel-disabled.png
rename to AppKit/Themes/Aristo/Resources/radio-image-disabled.png
diff --git a/AppKit/Themes/Aristo/Resources/radio-bezel-highlighted.png b/AppKit/Themes/Aristo/Resources/radio-image-highlighted.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/radio-bezel-highlighted.png
rename to AppKit/Themes/Aristo/Resources/radio-image-highlighted.png
diff --git a/AppKit/Themes/Aristo/Resources/radio-bezel-selected-disabled.png b/AppKit/Themes/Aristo/Resources/radio-image-selected-disabled.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/radio-bezel-selected-disabled.png
rename to AppKit/Themes/Aristo/Resources/radio-image-selected-disabled.png
diff --git a/AppKit/Themes/Aristo/Resources/radio-bezel-selected-highlighted.png b/AppKit/Themes/Aristo/Resources/radio-image-selected-highlighted.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/radio-bezel-selected-highlighted.png
rename to AppKit/Themes/Aristo/Resources/radio-image-selected-highlighted.png
diff --git a/AppKit/Themes/Aristo/Resources/radio-bezel-selected.png b/AppKit/Themes/Aristo/Resources/radio-image-selected.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/radio-bezel-selected.png
rename to AppKit/Themes/Aristo/Resources/radio-image-selected.png
diff --git a/AppKit/Themes/Aristo/Resources/radio-bezel.png b/AppKit/Themes/Aristo/Resources/radio-image.png
similarity index 100%
rename from AppKit/Themes/Aristo/Resources/radio-bezel.png
rename to AppKit/Themes/Aristo/Resources/radio-image.png
diff --git a/AppKit/Themes/Aristo/Resources/scrollview-bottom-corner-color.png b/AppKit/Themes/Aristo/Resources/scrollview-bottom-corner-color.png
new file mode 100644
index 000000000..ec7c9e451
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/scrollview-bottom-corner-color.png differ
diff --git a/AppKit/Resources/tableview-headerview-ascending.png b/AppKit/Themes/Aristo/Resources/tableview-headerview-ascending.png
similarity index 100%
rename from AppKit/Resources/tableview-headerview-ascending.png
rename to AppKit/Themes/Aristo/Resources/tableview-headerview-ascending.png
diff --git a/AppKit/Resources/tableview-headerview-descending.png b/AppKit/Themes/Aristo/Resources/tableview-headerview-descending.png
similarity index 100%
rename from AppKit/Resources/tableview-headerview-descending.png
rename to AppKit/Themes/Aristo/Resources/tableview-headerview-descending.png
diff --git a/AppKit/Resources/tableview-headerview-highlighted-pressed.png b/AppKit/Themes/Aristo/Resources/tableview-headerview-highlighted-pressed.png
similarity index 100%
rename from AppKit/Resources/tableview-headerview-highlighted-pressed.png
rename to AppKit/Themes/Aristo/Resources/tableview-headerview-highlighted-pressed.png
diff --git a/AppKit/Resources/tableview-headerview-highlighted.png b/AppKit/Themes/Aristo/Resources/tableview-headerview-highlighted.png
similarity index 100%
rename from AppKit/Resources/tableview-headerview-highlighted.png
rename to AppKit/Themes/Aristo/Resources/tableview-headerview-highlighted.png
diff --git a/AppKit/Resources/tableview-headerview-pressed.png b/AppKit/Themes/Aristo/Resources/tableview-headerview-pressed.png
similarity index 100%
rename from AppKit/Resources/tableview-headerview-pressed.png
rename to AppKit/Themes/Aristo/Resources/tableview-headerview-pressed.png
diff --git a/AppKit/Resources/tableview-headerview.png b/AppKit/Themes/Aristo/Resources/tableview-headerview.png
similarity index 100%
rename from AppKit/Resources/tableview-headerview.png
rename to AppKit/Themes/Aristo/Resources/tableview-headerview.png
diff --git a/AppKit/Themes/Aristo/Resources/tableviewselection.png b/AppKit/Themes/Aristo/Resources/tableviewselection.png
new file mode 100644
index 000000000..990a98983
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/tableviewselection.png differ
diff --git a/AppKit/Themes/Aristo/ThemeDescriptors.j b/AppKit/Themes/Aristo/ThemeDescriptors.j
index d1d77ee49..6ae3eb5a8 100755
--- a/AppKit/Themes/Aristo/ThemeDescriptors.j
+++ b/AppKit/Themes/Aristo/ThemeDescriptors.j
@@ -1,5 +1,5 @@
/*
- * AppController.j
+ * ThemeDescriptors.j
* Aristo
*
* Created by Francisco Tolmasky.
@@ -8,8 +8,312 @@
@import
@import
+@import
+
+var themedButtonValues = nil,
+ themedVerticalScrollerValues = nil,
+ themedHorizontalScrollerValues = nil,
+ themedSegmentedControlValues = nil,
+ themedHorizontalSliderValues = nil,
+ themedVerticalSliderValues = nil,
+ themedCircularSliderValues = nil,
+ themedButtonBarValues = nil,
+ themedAlertValues = nil;
+
+/*
+ HOW TO ADD OR MODIFY THEMED ELEMENTS
+
+ This file serves both as a means of defining default theme values
+ and of defining an interface for the automatically generated theme showcase,
+ so you have to keep both in mind.
+
+ Let's say you define a new view class and you want to theme it.
+ Here's how to do it with minimum work.
+
+ 1. Define +themeAttributes in your class if you define custom attributes.
+
+ 2. If you want a custom name to appear in the theme showcase, define +themeClass
+ and return a string with the name all in lowercase and words separated by
+ dashes. If you don't provide +themeClass, Cappuccino will separate the themed
+ class name into words at each uppercased letter followed by lowercase letters.
+
+ 3. Use the following template for defining your themed view (using a calendar view as an example).
+ Note that we make a separate method for creating and configuring the view.
+ This is to more clearly separate the two functions of ThemeDescriptors.j (defining
+ themes and defining the showcase interface), and also to allow us to easily reuse
+ the creation/configuration code if we make a HUD version of our themed view.
+
+ // Create a private variable to hold the theme values, we can share this with the HUD theme
+ var themedCalendarViewValues = nil;
+
+ + (CPCalendarView)makeCalendarView
+ {
+ // Define the size of the sample that will appear in the theme showcase.
+ // The maximum width/height is around 180 pixels.
+
+ var calendar = [[CPCalendarView alloc] initWithFrame:CGRectMake(0.0, 0.0, 150.0, 100.0)];
+
+ // Do any other initialization of the calendar you want to do here. In this case
+ // we want the calendar to show our birthdate instead of the current date.
+ [calendar setDate:new Date(1961, 2, 30)];
+
+ return calendar;
+ }
+
+ + (CPCalendarView)themedCalendarView
+ {
+ var calendar = [self makeCalendarView],
+
+ // Now define some pattern colors. We want to define the bezel for the calendar
+ // using a nine part image, which consists of nine slices in the order top/left,
+ // top, top/right, left, center, right, bottom/left, bottom, bottom/right.
+ // We can do this declaritively using the PatternColor function, which takes an array
+ // of slice declarations, with each declaration being an array of [filename, width, height].
+
+ bezelColor = PatternColor(
+ [
+ [@"calendar-bezel-0.png", 10.0, 10.0],
+ [@"calendar-bezel-1.png", 1.0, 10.0],
+ [@"calendar-bezel-2.png", 10.0, 10.0],
+ [@"calendar-bezel-3.png", 10.0, 1.0],
+ [@"calendar-bezel-4.png", 1.0, 1.0],
+ [@"calendar-bezel-5.png", 10.0, 1.0],
+ [@"calendar-bezel-6.png", 10.0, 10.0],
+ [@"calendar-bezel-7.png", 1.0, 10.0],
+ [@"calendar-bezel-8.png", 10.0, 10.0]
+ ]),
+
+ // Define an alternate bezel for when the calendar is disabled.
+
+ disabledBezelColor = PatternColor(
+ [
+ [@"calendar-disabled-bezel-0.png", 10.0, 10.0],
+ [@"calendar-disabled-bezel-1.png", 1.0, 10.0],
+ [@"calendar-disabled-bezel-2.png", 10.0, 10.0],
+ [@"calendar-disabled-bezel-3.png", 10.0, 1.0],
+ [@"calendar-disabled-bezel-4.png", 1.0, 1.0],
+ [@"calendar-disabled-bezel-5.png", 10.0, 1.0],
+ [@"calendar-disabled-bezel-6.png", 10.0, 10.0],
+ [@"calendar-disabled-bezel-7.png", 1.0, 10.0],
+ [@"calendar-disabled-bezel-8.png", 10.0, 10.0]
+ ]),
+
+ // We would like the font to be dark blue and lighter blue when disabled
+
+ textColor = [CPColor colorWithHexString:@"001B48"],
+ disabledTextColor = [textColor colorWithAlphaComponent:0.6];
+
+ // Now we will define our theme values. These are done declaratively in an array,
+ // where each element is an array of 2 or 3 values: attribute name, value, and optional state.
+
+ themedCalendarViewValues =
+ [
+ [@"bezel-color", bezelColor],
+ [@"bezel-color", disabledBezelColor, CPThemeStateDisabled],
+
+ [@"text-color", textColor],
+ [@"text-color", disabledTextColor, CPThemeStateDisabled],
+
+ // We will also define a minimum size
+ [@"min-size", CGSizeMake(100.0, 100.0)]
+ ];
+
+ // Now we just register our values
+
+ [self registerThemeValues:themedCalendarViewValues forView:calendar];
+ }
+
+ That's all there is to it. Note that PatternColor can also be used to create a simple
+ patterned color:
+
+ color = PatternColor(filename, width, height);
+
+ It can also be used to create a three part image by specifying three images slices
+ with [filename, width, height] and an orientation:
+
+ trackColor = PatternColor(
+ [
+ ["horizontal-track-left.png", 4.0, 5.0],
+ ["horizontal-track-center.png", 1.0, 5.0],
+ ["horizontal-track-right.png", 4.0, 5.0]
+ ],
+ PatternIsHorizontal);
+
+ trackColor = PatternColor(
+ [
+ ["vertical-track-top.png", 5.0, 6.0],
+ ["vertical-track-center.png", 5.0, 1.0],
+ ["vertical-track-bottom.png", 5.0, 4.0]
+ ],
+ PatternIsVertical);
+ EXCLUDING A THEMED OBJECT FROM THE SHOWCASE
+
+ When a theme is compiled, a showcase application is created that displays all of the themed objects
+ by default. There are some cases in which it is either not feasible or not desirable to display
+ the themed object in the showcase.
+
+ You can exclude themed objects from the showcases by defining the following method in your theme class:
+
+ + (CPArray)themeShowcaseExcludes
+
+ If such a method exists, it should return an array of themed object names to exclude from the showcase.
+ For example, let's say we want to exclude the themed objects that are defined by the methods
+ themedAlert, themedCornerview and themedTableDataView. Here is what the themeShowcaseExcludes method
+ could look like:
+
+ + (CPArray)themeShowcaseExcludes
+ {
+ return ["themedAlert", "cornerview", "tableDataView"];
+ }
+
+ Note that to make it easier to do the right thing, the names in the array can begin with "themed" or not.
+ If the name does not begin with "themed", it is prepended. Name matching is case-insensitive, so you
+ don't have to worry about capitalization.
+
+
+ SUBTHEMES
+
+ If you want to create a theme that inherits from another theme, for example the way
+ the Aristo-HUD theme inherits from Aristo, you can do this easily by following
+ these steps:
+
+ 1. Your subtheme should be named "-", where is the name
+ of the theme from which you are inheriting, and is the subtheme identifier.
+ For example, "Aristo-HUD" inherits from "Aristo".
+
+ 2. Decide which elements in the subtheme will be changed and which will be inherited.
+ For example, in a HUD theme you may want to use a dark or black background
+ and white text.
+
+ 3. If your subtheme will replace pattern colors in the inherited theme, use the same filename as
+ in the inherited theme and put the pattern images in a subdirectory of the inherited
+ directory. The subdirectory's name should be the same as the subtheme identifier.
+ For example, Aristo-HUD's pattern images are in a "HUD" subdirectory of the "Aristo" directory.
+
+ 4. In the subtheme class (such as AristoHUDThemeDescriptor), add a method with this template:
+
+ + (CPCalendarView)themedCalendarView
+ {
+ var calendar = [AristoThemeDescriptor makeCalendarView],
+ subthemeValues = nil; // This may change according to your needs, see below
+
+ [self registerThemeValues:subthemeValues forView:calendar inherit:themedCalendarViewValues];
+
+ return calendar;
+ }
+
+ NOTE: If you pass nil or an empty array for the subthemeValues, ALL pattern images
+ will be automatically inherited and MUST be present in the subtheme directory.
+
+ Depending on your subtheme, you will change subthemeValues to indicate which values you
+ wish to override or remove from the inherited theme.
+
+ For example, let's say in your subtheme you want to use white text instead of dark blue,
+ and slightly gray text when it is disabled. In addition, you decide not to inherit the
+ disabled bezel color pattern. Here is how your updated theme method would look:
+
+ + (CPCalendarView)themedCalendarView
+ {
+ var calendar = [AristoThemeDescriptor makeCalendarView],
+
+ textColor = [CPColor whiteColor],
+ disabledTextColor = [textColor colorWithAlphaComponent:0.6],
+ subthemeValues =
+ [
+ [@"bezel-color", nil, CPThemeStateDisabled],
+ [@"text-color", textColor],
+ [@"text-color", disabledTextColor, CPThemeStateDisabled]
+ ];
+
+ [self registerThemeValues:subthemeValues forView:calendar inherit:themedCalendarViewValues];
+
+ return calendar;
+ }
+
+ If your subtheme consistently applies the same overrides to all themed views, you may
+ want to create a separate method that supplies those overrides. For example, Aristo-HUD
+ uses the following method:
+
+ + (CPArray)defaultThemeOverridesAddedTo:(CPArray)themeValues
+
+ For the example we used above, we could define such a method as follows:
+
+ + (CPArray)defaultThemeOverridesAddedTo:(CPArray)themeValues
+ {
+ var textColor = [CPColor whiteColor],
+ disabledTextColor = [textColor colorWithAlphaComponent:0.6],
+ overrides = [CPArray arrayWithObjects:
+ [@"bezel-color", nil, CPThemeStateDisabled],
+ [@"text-color", textColor],
+ [@"text-color", disabledTextColor, CPThemeStateDisabled]
+ ];
+
+ if (themeValues)
+ [overrides addObjectsFromArray:themeValues];
+
+ return overrides;
+ }
+
+ Note that the values in the themeValues parameter override the defaults provided
+ by the method.
+
+ Our themed view method becomes:
+
+ + (CPCalendarView)themedCalendarView
+ {
+ var calendar = [AristoThemeDescriptor makeCalendarView];
+
+ [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil]
+ forView:calendar
+ inherit:themedCalendarViewValues];
+
+ return calendar;
+ }
+
+ As a last example, let's assume another type of themed view in your subtheme wants
+ to add some new theme values to the default overrides. The theme method might look like this:
+
+ + (CPCalendarView)themedCalendarHeaderView
+ {
+ var header = [AristoThemeDescriptor makeCalendarHeaderView],
+ headerColor = PatternColor(
+ [
+ ["calendar-header-left.png", 5.0, 23.0],
+ ["calendar-header-center.png", 1.0, 23.0],
+ ["calendar-header-right.png", 5.0, 23.0]
+ ],
+ PatternIsHorizontal),
+ subthemeValues =
+ [
+ [@"background-color", headerColor]
+ ];
+
+ [self registerThemeValues:[self defaultThemeOverridesAddedTo:subthemeValues]
+ forView:calendar
+ inherit:themedCalendarHeaderViewValues];
+
+ return calendar;
+ }
+
+
+ ADDING ARISTO-HUD CONTROLS
+
+ If you want to add a new themed control to Aristo-HUD, you should use the
+ +defaultThemeOverridesAddedTo: method to ensure visual consistency with the rest
+ of Aristo-HUD. The overrides returned by this method do the following:
+
+ - Sets the "text-color" attribute to white in the normal state and
+ [CPColor colorWithCalibratedWhite:1.0 alpha:0.6] in the disabled state.
+
+ - Set "text-shadow-color" to black.
+
+ - Sets "text-shadow-offset" to (-1.0, -1.0).
+
+ These are the standards used by Aristo-HUD. If you want to override these defaults,
+ simply add those overrides to the values you pass to +defaultThemeOverridesAddedTo:.
+*/
@implementation AristoThemeDescriptor : BKThemeDescriptor
{
}
@@ -19,80 +323,100 @@
return @"Aristo";
}
++ (CPArray)themeShowcaseExcludes
+{
+ return ["alert", "cornerview", "columnHeader", "tableView", "tableHeaderRow", "tableDataView"];
+}
+
++ (CPButton)makeButton
+{
+ return [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, CPButtonDefaultHeight)];
+}
+
+ (CPButton)button
{
- var button = [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 24.0)],
+ var button = [self makeButton],
- bezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ bezelColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"button-bezel-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"button-bezel-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"button-bezel-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]],
+ ["button-bezel-left.png", 4.0, 24.0],
+ ["button-bezel-center.png", 1.0, 24.0],
+ ["button-bezel-right.png", 4.0, 24.0]
+ ],
+ PatternIsHorizontal),
- highlightedBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ highlightedBezelColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"button-bezel-highlighted-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"button-bezel-highlighted-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"button-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]],
+ ["button-bezel-highlighted-left.png", 4.0, 24.0],
+ ["button-bezel-highlighted-center.png", 1.0, 24.0],
+ ["button-bezel-highlighted-right.png", 4.0, 24.0]
+ ],
+ PatternIsHorizontal),
- defaultBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ defaultBezelColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"default-button-bezel-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"default-button-bezel-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"default-button-bezel-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]],
+ ["default-button-bezel-left.png", 4.0, 24.0],
+ ["default-button-bezel-center.png", 1.0, 24.0],
+ ["default-button-bezel-right.png", 4.0, 24.0]
+ ],
+ PatternIsHorizontal),
- defaultHighlightedBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ defaultHighlightedBezelColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"default-button-bezel-highlighted-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"default-button-bezel-highlighted-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"default-button-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]],
+ ["default-button-bezel-highlighted-left.png", 4.0, 24.0],
+ ["default-button-bezel-highlighted-center.png", 1.0, 24.0],
+ ["default-button-bezel-highlighted-right.png", 4.0, 24.0]
+ ],
+ PatternIsHorizontal),
- defaultDisabledBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ defaultDisabledBezelColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"default-button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"default-button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"default-button-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]],
-
- disabledBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ ["default-button-bezel-disabled-left.png", 4.0, 24.0],
+ ["default-button-bezel-disabled-center.png", 1.0, 24.0],
+ ["default-button-bezel-disabled-right.png", 4.0, 24.0]
+ ],
+ PatternIsHorizontal),
+
+ disabledBezelColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]];
+ ["button-bezel-disabled-left.png", 4.0, 24.0],
+ ["button-bezel-disabled-center.png", 1.0, 24.0],
+ ["button-bezel-disabled-right.png", 4.0, 24.0]
+ ],
+ PatternIsHorizontal),
- [button setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBordered];
- [button setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"];
- [button setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered];
- [button setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:0.6] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
- [button setValue:CGSizeMake(0.0, 1.0) forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateBordered];
- [button setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
-
- [button setValue:bezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
- [button setValue:highlightedBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateHighlighted];
- [button setValue:CGInsetMake(0.0, 5.0, 0.0, 5.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered];
+ defaultTextColor = [CPColor colorWithCalibratedRed:13.0/255.0 green:51.0/255.0 blue:70.0/255.0 alpha:1.0],
+ defaultDisabledTextColor = [CPColor colorWithCalibratedRed:13.0/255.0 green:51.0/255.0 blue:70.0/255.0 alpha:0.6];
- [button setValue:[CPColor colorWithCalibratedWhite:0.6 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled];
- [button setValue:disabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
- [button setValue:defaultDisabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDefault|CPThemeStateDisabled];
-
- [button setValue:defaultBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDefault];
- [button setValue:defaultHighlightedBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateHighlighted|CPThemeStateDefault];
- [button setValue:[CPColor colorWithCalibratedRed:13.0/255.0 green:51.0/255.0 blue:70.0/255.0 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDefault];
- [button setValue:[CPColor colorWithCalibratedRed:13.0/255.0 green:51.0/255.0 blue:70.0/255.0 alpha:0.6] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled|CPThemeStateDefault];
+ themedButtonValues =
+ [
+ [@"font", [CPFont boldSystemFontOfSize:12.0], CPThemeStateBordered],
+ [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0]],
+ [@"text-shadow-color", [CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0], CPThemeStateBordered],
+ [@"text-shadow-color", [CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0], CPThemeStateBordered | CPThemeStateDisabled],
+ [@"text-shadow-offset", CGSizeMake(0.0, 1.0), CPThemeStateBordered],
+ [@"line-break-mode", CPLineBreakByTruncatingTail],
+ [@"content-inset", CGInsetMake(0.0, 5.0, 0.0, 5.0), CPThemeStateBordered],
- [button setValue:CGSizeMake(0.0, 24.0) forThemeAttribute:@"min-size"];
- [button setValue:CGSizeMake(-1.0, 24.0) forThemeAttribute:@"max-size"];
+ [@"bezel-color", bezelColor, CPThemeStateBordered],
+ [@"bezel-color", highlightedBezelColor, CPThemeStateBordered | CPThemeStateHighlighted],
+
+ [@"text-color", [CPColor colorWithCalibratedWhite:0.6 alpha:1.0], CPThemeStateDisabled],
+ [@"bezel-color", disabledBezelColor, CPThemeStateBordered | CPThemeStateDisabled],
+ [@"bezel-color", defaultDisabledBezelColor, CPThemeStateBordered | CPThemeStateDefault | CPThemeStateDisabled],
+
+ [@"text-color", defaultTextColor, CPThemeStateDefault],
+ [@"text-color", defaultDisabledTextColor, CPThemeStateDefault | CPThemeStateDisabled],
+ [@"bezel-color", defaultBezelColor, CPThemeStateBordered | CPThemeStateDefault],
+ [@"bezel-color", defaultHighlightedBezelColor, CPThemeStateBordered | CPThemeStateHighlighted | CPThemeStateDefault],
+
+ [@"min-size", CGSizeMake(0.0, CPButtonDefaultHeight)],
+ [@"max-size", CGSizeMake(-1.0, CPButtonDefaultHeight)],
+
+ [@"image-offset", CPButtonImageOffset]
+ ];
+
+ [self registerThemeValues:themedButtonValues forView:button];
return button;
}
@@ -111,7 +435,7 @@
var button = [self button];
[button setTitle:@"OK"];
- [button setDefaultButton:YES];
+ [button setThemeState:CPThemeStateDefault];
return button;
}
@@ -119,135 +443,184 @@
+ (CPPopUpButton)themedPopUpButton
{
var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 24.0) pullsDown:NO],
- color = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ color = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"button-bezel-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"button-bezel-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"popup-bezel-right.png" size:CGSizeMake(27.0, 24.0)]
- ]
- isVertical:NO]];
+ ["button-bezel-left.png", 4.0, 24.0],
+ ["button-bezel-center.png", 1.0, 24.0],
+ ["popup-bezel-right.png", 27.0, 24.0]
+ ],
+ PatternIsHorizontal),
- var disabledBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ disabledColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"popup-bezel-disabled-right.png" size:CGSizeMake(27.0, 24.0)]
- ]
- isVertical:NO]];
-
+ ["button-bezel-disabled-left.png", 4.0, 24.0],
+ ["button-bezel-disabled-center.png", 1.0, 24.0],
+ ["popup-bezel-disabled-right.png", 27.0, 24.0]
+ ],
+ PatternIsHorizontal),
+
+ themeValues =
+ [
+ [@"bezel-color", color, CPThemeStateBordered],
+ [@"bezel-color", disabledColor, CPThemeStateBordered | CPThemeStateDisabled],
+
+ [@"content-inset", CGInsetMake(0, 27.0 + 5.0, 0, 5.0), CPThemeStateBordered],
+ [@"font", [CPFont boldSystemFontOfSize:12.0]],
+ [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0]],
+ [@"text-shadow-color", [CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0]],
+
+ [@"text-color", [CPColor colorWithCalibratedWhite:0.6 alpha:1.0], CPThemeStateBordered | CPThemeStateDisabled],
+ [@"text-shadow-color", [CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:0.6], CPThemeStateBordered | CPThemeStateDisabled],
+
+ [@"min-size", CGSizeMake(32.0, 24.0)],
+ [@"max-size", CGSizeMake(-1.0, 24.0)]
+ ];
+
+ [self registerThemeValues:themeValues forView:button];
+
[button setTitle:@"Pop Up"];
-
- [button setValue:color forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
- [button setValue:disabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
-
- [button setValue:CGInsetMake(0, 27.0 + 5.0, 0, 5.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered];
- [button setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font"];
- [button setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"];
- [button setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-shadow-color"];
- [button setValue:CGSizeMake(0.0, 1.0) forThemeAttribute:@"text-shadow-offset"];
-
- [button setValue:CGSizeMake(32.0, 24.0) forThemeAttribute:@"min-size"];
- [button setValue:CGSizeMake(-1.0, 24.0) forThemeAttribute:@"max-size"];
-
[button addItemWithTitle:@"item"];
- [button setValue:[CPColor colorWithCalibratedWhite:0.6 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
- [button setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:0.6] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
-
return button;
}
+ (CPPopUpButton)themedPullDownMenu
{
var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 24.0) pullsDown:YES],
- color = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ color = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"button-bezel-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"button-bezel-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"popup-bezel-right-pullsdown.png" size:CGSizeMake(27.0, 24.0)]
- ]
- isVertical:NO]];
+ ["button-bezel-left.png", 4.0, 24.0],
+ ["button-bezel-center.png", 1.0, 24.0],
+ ["popup-bezel-right-pullsdown.png", 27.0, 24.0]
+ ],
+ PatternIsHorizontal),
- var disabledColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ disabledColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"popup-bezel-disabled-right-pullsdown.png" size:CGSizeMake(27.0, 24.0)]
- ]
- isVertical:NO]];
+ ["button-bezel-disabled-left.png", 4.0, 24.0],
+ ["button-bezel-disabled-center.png", 1.0, 24.0],
+ ["popup-bezel-disabled-right-pullsdown.png", 27.0, 24.0]
+ ],
+ PatternIsHorizontal),
+
+ themeValues =
+ [
+ [@"bezel-color", color, CPPopUpButtonStatePullsDown | CPThemeStateBordered],
+ [@"bezel-color", disabledColor, CPPopUpButtonStatePullsDown | CPThemeStateBordered | CPThemeStateDisabled],
+
+ [@"content-inset", CGInsetMake(0, 27.0 + 5.0, 0, 5.0), CPThemeStateBordered],
+ [@"font", [CPFont boldSystemFontOfSize:12.0]],
+ [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0]],
+ [@"text-shadow-color", [CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0]],
+
+ [@"text-color", [CPColor colorWithCalibratedWhite:0.6 alpha:1.0], CPThemeStateBordered | CPThemeStateDisabled],
+ [@"text-shadow-color", [CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:0.6], CPThemeStateBordered | CPThemeStateDisabled],
+
+ [@"min-size", CGSizeMake(32.0, 24.0)],
+ [@"max-size", CGSizeMake(-1.0, 24.0)]
+ ];
+
+ [self registerThemeValues:themeValues forView:button];
[button setTitle:@"Pull Down"];
-
- [button setValue:color forThemeAttribute:@"bezel-color" inState:CPPopUpButtonStatePullsDown|CPThemeStateBordered];
- [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled|CPPopUpButtonStatePullsDown|CPThemeStateBordered];
-
- [button setValue:CGInsetMake(0, 27.0 + 5.0, 0, 5.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered];
- [button setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font"];
- [button setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"];
- [button setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-shadow-color"];
- [button setValue:CGSizeMake(0.0, 1.0) forThemeAttribute:@"text-shadow-offset"];
-
- [button setValue:CGSizeMake(32.0, 24.0) forThemeAttribute:@"min-size"];
- [button setValue:CGSizeMake(-1.0, 24.0) forThemeAttribute:@"max-size"];
-
- [button setValue:[CPColor colorWithCalibratedWhite:0.6 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
- [button setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:0.6] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
-
[button addItemWithTitle:@"item"];
return button;
}
++ (CPScrollView)themedScrollView
+{
+ var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 200.0, 200.0)];
+
+ var borderColor = [CPColor colorWithWhite:0.0 alpha:0.2],
+ bottomCornerColor = PatternColor(@"scrollview-bottom-corner-color.png", 15.0, 15.0);
+
+ var themedScrollViewValues =
+ [
+ [@"border-color", borderColor],
+ [@"bottom-corner-color", bottomCornerColor]
+ ];
+
+ [self registerThemeValues:themedScrollViewValues forView:scrollView];
+
+ [scrollView setAutohidesScrollers:YES];
+ [scrollView setBorderType:CPLineBorder];
+
+ return scrollView;
+}
+
++ (CPScroller)makeVerticalScroller
+{
+ var scroller = [[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, 15.0, 170.0)];
+
+ [scroller setFloatValue:0.1];
+ [scroller setKnobProportion:0.5];
+
+ return scroller;
+}
+
+ (CPScroller)themedVerticalScroller
{
- var scroller = [[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, 15.0, 170.0)],
- trackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-vertical-track.png" size:CGSizeMake(15.0, 1.0)]),
- disabledTrackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-vertical-track-disabled.png" size:CGSizeMake(15.0, 1.0)]);
-
- [scroller setValue:21.0 forThemeAttribute:@"minimum-knob-length" inState:CPThemeStateVertical];
- [scroller setValue:CGInsetMake(0.0, 0.0, 0.0, 0.0) forThemeAttribute:@"knob-inset" inState:CPThemeStateVertical];
- [scroller setValue:CGInsetMake(-10.0, 0.0, -10.0, 0.0) forThemeAttribute:@"track-inset" inState:CPThemeStateVertical];
+ var scroller = [self makeVerticalScroller],
+ trackColor = PatternColor("scroller-vertical-track.png", 15.0, 1.0),
+ disabledTrackColor = PatternColor("scroller-vertical-track-disabled.png", 15.0, 1.0),
- [scroller setValue:trackColor forThemeAttribute:@"knob-slot-color" inState:CPThemeStateVertical];
- [scroller setValue:disabledTrackColor forThemeAttribute:@"knob-slot-color" inState:CPThemeStateVertical | CPThemeStateDisabled];
+ upArrowColor = PatternColor("scroller-up-arrow.png", 15.0, 24.0),
+ highlightedUpArrowColor = PatternColor("scroller-up-arrow-highlighted.png", 15.0, 24.0),
+ disabledUpArrowColor = PatternColor("scroller-up-arrow-disabled.png", 15.0, 24.0),
- var arrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-up-arrow.png" size:CGSizeMake(15.0, 24.0)]),
- highlightedArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-up-arrow-highlighted.png" size:CGSizeMake(15.0, 24.0)]),
- disabledArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-up-arrow-disabled.png" size:CGSizeMake(15.0, 24.0)]);
+ downArrowColor = PatternColor("scroller-down-arrow.png", 15.0, 24.0),
+ highlightedDownArrowColor = PatternColor("scroller-down-arrow-highlighted.png", 15.0, 24.0),
+ disabledDownArrowColor = PatternColor("scroller-down-arrow-disabled.png", 15.0, 24.0),
- [scroller setValue:CGSizeMake(15.0, 24.0) forThemeAttribute:@"decrement-line-size" inState:CPThemeStateVertical];
- [scroller setValue:arrowColor forThemeAttribute:@"decrement-line-color" inState:CPThemeStateVertical];
- [scroller setValue:highlightedArrowColor forThemeAttribute:@"decrement-line-color" inState:CPThemeStateVertical | CPThemeStateHighlighted],
- [scroller setValue:disabledArrowColor forThemeAttribute:@"decrement-line-color" inState:CPThemeStateVertical | CPThemeStateDisabled];
-
- var arrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-down-arrow.png" size:CGSizeMake(15.0, 24.0)]),
- highlightedArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-down-arrow-highlighted.png" size:CGSizeMake(15.0, 24.0)]),
- disabledArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-down-arrow-disabled.png" size:CGSizeMake(15.0, 24.0)]);
-
- [scroller setValue:CGSizeMake(15.0, 24.0) forThemeAttribute:@"increment-line-size" inState:CPThemeStateVertical];
- [scroller setValue:arrowColor forThemeAttribute:@"increment-line-color" inState:CPThemeStateVertical];
- [scroller setValue:highlightedArrowColor forThemeAttribute:@"increment-line-color" inState:CPThemeStateVertical | CPThemeStateHighlighted];
- [scroller setValue:disabledArrowColor forThemeAttribute:@"increment-line-color" inState:CPThemeStateVertical | CPThemeStateDisabled];
-
- var knobColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
+ knobColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"scroller-vertical-knob-top.png" size:CGSizeMake(15.0, 10.0)],
- [_CPCibCustomResource imageResourceWithName:"scroller-vertical-knob-center.png" size:CGSizeMake(15.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"scroller-vertical-knob-bottom.png" size:CGSizeMake(15.0, 10.0)]
- ]
- isVertical:YES]);
+ ["scroller-vertical-knob-top.png", 15.0, 10.0],
+ ["scroller-vertical-knob-center.png", 15.0, 1.0],
+ ["scroller-vertical-knob-bottom.png", 15.0, 10.0]
+ ],
+ PatternIsVertical),
- var knobDisabledColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
+ disabledKnobColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"scroller-vertical-knob-disabled-top.png" size:CGSizeMake(15.0, 10.0)],
- [_CPCibCustomResource imageResourceWithName:"scroller-vertical-knob-disabled-center.png" size:CGSizeMake(15.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"scroller-vertical-knob-disabled-bottom.png" size:CGSizeMake(15.0, 10.0)]
- ]
- isVertical:YES]);
-
- [scroller setValue:knobColor forThemeAttribute:@"knob-color" inState:CPThemeStateVertical];
- [scroller setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateVertical|CPThemeStateDisabled];
-
+ ["scroller-vertical-knob-disabled-top.png", 15.0, 10.0],
+ ["scroller-vertical-knob-disabled-center.png", 15.0, 1.0],
+ ["scroller-vertical-knob-disabled-bottom.png", 15.0, 10.0]
+ ],
+ PatternIsVertical);
+
+ themedVerticalScrollerValues =
+ [
+ [@"minimum-knob-length", 21.0, CPThemeStateVertical],
+ [@"knob-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateVertical],
+ [@"track-inset", CGInsetMake(-10.0, 0.0, -10.0, 0.0), CPThemeStateVertical],
+
+ [@"knob-color", knobColor, CPThemeStateVertical],
+ [@"knob-color", disabledKnobColor, CPThemeStateVertical | CPThemeStateDisabled],
+
+ [@"knob-slot-color", trackColor, CPThemeStateVertical],
+ [@"knob-slot-color", disabledTrackColor, CPThemeStateVertical | CPThemeStateDisabled],
+
+ [@"decrement-line-size", CGSizeMake(15.0, 24.0), CPThemeStateVertical],
+ [@"decrement-line-color", upArrowColor, CPThemeStateVertical],
+ [@"decrement-line-color", highlightedUpArrowColor, CPThemeStateVertical | CPThemeStateHighlighted],
+ [@"decrement-line-color", disabledUpArrowColor, CPThemeStateVertical | CPThemeStateDisabled],
+
+ [@"increment-line-size", CGSizeMake(15.0, 24.0), CPThemeStateVertical],
+ [@"increment-line-color", downArrowColor, CPThemeStateVertical],
+ [@"increment-line-color", highlightedDownArrowColor, CPThemeStateVertical | CPThemeStateHighlighted],
+ [@"increment-line-color", disabledDownArrowColor, CPThemeStateVertical | CPThemeStateDisabled]
+ ];
+
+ [self registerThemeValues:themedVerticalScrollerValues forView:scroller];
+
+ return scroller;
+}
+
++ (CPScroller)makeHorizontalScroller
+{
+ var scroller = [[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, 170.0, 15.0)];
+
[scroller setFloatValue:0.1];
[scroller setKnobProportion:0.5];
@@ -256,56 +629,58 @@
+ (CPScroller)themedHorizontalScroller
{
- var scroller = [[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, 170.0, 15.0)],
- trackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-horizontal-track.png" size:CGSizeMake(1.0, 15.0)]),
- disabledTrackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-horizontal-track-disabled.png" size:CGSizeMake(1.0, 15.0)]);
+ var scroller = [self makeHorizontalScroller],
+ trackColor = PatternColor("scroller-horizontal-track.png", 1.0, 15.0),
+ disabledTrackColor = PatternColor("scroller-horizontal-track-disabled.png", 1.0, 15.0),
- [scroller setValue:21.0 forThemeAttribute:@"minimum-knob-length"];
- [scroller setValue:CGInsetMake(0.0, 0.0, 0.0, 0.0) forThemeAttribute:@"knob-inset"];
- [scroller setValue:CGInsetMake(0.0, -10.0, 0.0, -11.0) forThemeAttribute:@"track-inset"];
+ leftArrowColor = PatternColor("scroller-left-arrow.png", 24.0, 15.0),
+ highlightedLeftArrowColor = PatternColor("scroller-left-arrow-highlighted.png", 24.0, 15.0),
+ disabledLeftArrowColor = PatternColor("scroller-left-arrow-disabled.png", 24.0, 15.0),
- [scroller setValue:trackColor forThemeAttribute:@"knob-slot-color"];
- [scroller setValue:disabledTrackColor forThemeAttribute:@"knob-slot-color" inState:CPThemeStateDisabled];
+ rightArrowColor = PatternColor("scroller-right-arrow.png", 24.0, 15.0),
+ highlightedRightArrowColor = PatternColor("scroller-right-arrow-highlighted.png", 24.0, 15.0),
+ disabledRightArrowColor = PatternColor("scroller-right-arrow-disabled.png", 24.0, 15.0),
- var arrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-left-arrow.png" size:CGSizeMake(24.0, 15.0)]),
- highlightedArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-left-arrow-highlighted.png" size:CGSizeMake(24.0, 15.0)]),
- disabledArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-left-arrow-disabled.png" size:CGSizeMake(24.0, 15.0)]);
-
- [scroller setValue:CGSizeMake(24.0, 15.0) forThemeAttribute:@"decrement-line-size"];
- [scroller setValue:arrowColor forThemeAttribute:@"decrement-line-color"];
- [scroller setValue:highlightedArrowColor forThemeAttribute:@"decrement-line-color" inState:CPThemeStateHighlighted],
- [scroller setValue:disabledArrowColor forThemeAttribute:@"decrement-line-color" inState:CPThemeStateDisabled];
-
- var arrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-right-arrow.png" size:CGSizeMake(24.0, 15.0)]),
- highlightedArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-right-arrow-highlighted.png" size:CGSizeMake(24.0, 15.0)]),
- disabledArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"scroller-right-arrow-disabled.png" size:CGSizeMake(24.0, 15.0)]);
-
- [scroller setValue:CGSizeMake(24.0, 15.0) forThemeAttribute:@"increment-line-size"];
- [scroller setValue:arrowColor forThemeAttribute:@"increment-line-color"];
- [scroller setValue:highlightedArrowColor forThemeAttribute:@"increment-line-color" inState:CPThemeStateHighlighted];
- [scroller setValue:disabledArrowColor forThemeAttribute:@"increment-line-color" inState:CPThemeStateDisabled];
-
- var knobColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
+ knobColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"scroller-horizontal-knob-left.png" size:CGSizeMake(10.0, 15.0)],
- [_CPCibCustomResource imageResourceWithName:"scroller-horizontal-knob-center.png" size:CGSizeMake(1.0, 15.0)],
- [_CPCibCustomResource imageResourceWithName:"scroller-horizontal-knob-right.png" size:CGSizeMake(10.0, 15.0)]
- ]
- isVertical:NO]);
+ ["scroller-horizontal-knob-left.png", 10.0, 15.0],
+ ["scroller-horizontal-knob-center.png", 1.0, 15.0],
+ ["scroller-horizontal-knob-right.png", 10.0, 15.0]
+ ],
+ PatternIsHorizontal),
- var knobDisabledColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
+ disabledKnobColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"scroller-horizontal-knob-disabled-left.png" size:CGSizeMake(10.0, 15.0)],
- [_CPCibCustomResource imageResourceWithName:"scroller-horizontal-knob-disabled-center.png" size:CGSizeMake(1.0, 15.0)],
- [_CPCibCustomResource imageResourceWithName:"scroller-horizontal-knob-disabled-right.png" size:CGSizeMake(10.0, 15.0)]
- ]
- isVertical:NO]);
+ ["scroller-horizontal-knob-disabled-left.png", 10.0, 15.0],
+ ["scroller-horizontal-knob-disabled-center.png", 1.0, 15.0],
+ ["scroller-horizontal-knob-disabled-right.png", 10.0, 15.0]
+ ],
+ PatternIsHorizontal);
- [scroller setValue:knobColor forThemeAttribute:@"knob-color"];
- [scroller setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled];
+ themedHorizontalScrollerValues =
+ [
+ [@"minimum-knob-length", 21.0],
+ [@"knob-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0)],
+ [@"track-inset", CGInsetMake(0.0, -10.0, 0.0, -11.0)],
- [scroller setFloatValue:0.1];
- [scroller setKnobProportion:0.5];
+ [@"knob-color", knobColor],
+ [@"knob-color", disabledKnobColor, CPThemeStateDisabled],
+
+ [@"knob-slot-color", trackColor],
+ [@"knob-slot-color", disabledTrackColor, CPThemeStateDisabled],
+
+ [@"decrement-line-size", CGSizeMake(24.0, 15.0)],
+ [@"decrement-line-color", leftArrowColor],
+ [@"decrement-line-color", highlightedLeftArrowColor, CPThemeStateHighlighted],
+ [@"decrement-line-color", disabledLeftArrowColor, CPThemeStateDisabled],
+
+ [@"increment-line-size", CGSizeMake(24.0, 15.0)],
+ [@"increment-line-color", rightArrowColor],
+ [@"increment-line-color", highlightedRightArrowColor, CPThemeStateHighlighted],
+ [@"increment-line-color", disabledRightArrowColor, CPThemeStateDisabled]
+ ];
+
+ [self registerThemeValues:themedHorizontalScrollerValues forView:scroller];
return scroller;
}
@@ -313,44 +688,60 @@
+ (CPTextField)themedStandardTextField
{
var textfield = [[CPTextField alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 29.0)],
- bezelColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-0.png" size:CGSizeMake(2.0, 3.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-1.png" size:CGSizeMake(1.0, 3.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-2.png" size:CGSizeMake(2.0, 3.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-3.png" size:CGSizeMake(2.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-4.png" size:CGSizeMake(1.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-5.png" size:CGSizeMake(2.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-6.png" size:CGSizeMake(2.0, 2.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-7.png" size:CGSizeMake(1.0, 2.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-8.png" size:CGSizeMake(2.0, 2.0)]
- ]]],
- bezelFocusedColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
+ bezelColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-focused-0.png" size:CGSizeMake(6.0, 7.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-focused-1.png" size:CGSizeMake(1.0, 7.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-focused-2.png" size:CGSizeMake(6.0, 7.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-focused-3.png" size:CGSizeMake(6.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-focused-4.png" size:CGSizeMake(1.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-focused-5.png" size:CGSizeMake(6.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-focused-6.png" size:CGSizeMake(6.0, 5.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-focused-7.png" size:CGSizeMake(1.0, 5.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-square-focused-8.png" size:CGSizeMake(6.0, 5.0)]
- ]]];
+ ["textfield-bezel-square-0.png", 2.0, 3.0],
+ ["textfield-bezel-square-1.png", 1.0, 3.0],
+ ["textfield-bezel-square-2.png", 2.0, 3.0],
+ ["textfield-bezel-square-3.png", 2.0, 1.0],
+ ["textfield-bezel-square-4.png", 1.0, 1.0],
+ ["textfield-bezel-square-5.png", 2.0, 1.0],
+ ["textfield-bezel-square-6.png", 2.0, 2.0],
+ ["textfield-bezel-square-7.png", 1.0, 2.0],
+ ["textfield-bezel-square-8.png", 2.0, 2.0]
+ ]),
+
+ bezelFocusedColor = PatternColor(
+ [
+ ["textfield-bezel-square-focused-0.png", 6.0, 7.0],
+ ["textfield-bezel-square-focused-1.png", 1.0, 7.0],
+ ["textfield-bezel-square-focused-2.png", 6.0, 7.0],
+ ["textfield-bezel-square-focused-3.png", 6.0, 1.0],
+ ["textfield-bezel-square-focused-4.png", 1.0, 1.0],
+ ["textfield-bezel-square-focused-5.png", 6.0, 1.0],
+ ["textfield-bezel-square-focused-6.png", 6.0, 5.0],
+ ["textfield-bezel-square-focused-7.png", 1.0, 5.0],
+ ["textfield-bezel-square-focused-8.png", 6.0, 5.0]
+ ]),
+
+ placeholderColor = [CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0],
+
+ themeValues =
+ [
+ [@"bezel-color", bezelColor, CPThemeStateBezeled],
+ [@"bezel-color", bezelFocusedColor, CPThemeStateBezeled | CPThemeStateEditing],
+ [@"font", [CPFont systemFontOfSize:12.0], CPThemeStateBezeled],
+
+ [@"content-inset", CGInsetMake(9.0, 7.0, 5.0, 8.0), CPThemeStateBezeled],
+ [@"bezel-inset", CGInsetMake(4.0, 4.0, 3.0, 4.0), CPThemeStateBezeled],
+ [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBezeled | CPThemeStateEditing],
+
+ [@"text-color", placeholderColor, CPTextFieldStatePlaceholder],
+
+ [@"line-break-mode", CPLineBreakByTruncatingTail, CPThemeStateTableDataView],
+ [@"vertical-alignment", CPCenterVerticalTextAlignment, CPThemeStateTableDataView],
+ [@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 5.0), CPThemeStateTableDataView],
+
+ [@"text-color", [CPColor colorWithCalibratedWhite:51.0 / 255.0 alpha:1.0], CPThemeStateTableDataView],
+ [@"text-color", [CPColor whiteColor], CPThemeStateTableDataView | CPThemeStateSelectedTableDataView],
+ [@"font", [CPFont boldSystemFontOfSize:12.0], CPThemeStateTableDataView | CPThemeStateSelectedTableDataView],
+ ];
+
+ [self registerThemeValues:themeValues forView:textfield];
[textfield setBezeled:YES];
- [textfield setValue:bezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBezeled];
- [textfield setValue:bezelFocusedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBezeled|CPThemeStateEditing];
- [textfield setValue:[CPFont systemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBezeled];
- [textfield setValue:CGInsetMake(9.0, 7.0, 5.0, 8.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBezeled];
-
- [textfield setValue:CGInsetMake(4.0, 4.0, 3.0, 4.0) forThemeAttribute:@"bezel-inset" inState:CPThemeStateBezeled];
- [textfield setValue:CGInsetMake(0.0, 0.0, 0.0, 0.0) forThemeAttribute:@"bezel-inset" inState:CPThemeStateBezeled|CPThemeStateEditing];
-
- [textfield setValue:[CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color" inState:CPTextFieldStatePlaceholder];
-
[textfield setPlaceholderString:"placeholder"];
[textfield setStringValue:""];
[textfield setEditable:YES];
@@ -359,196 +750,158 @@
}
+ (CPTextField)themedRoundedTextField
-{
+{
var textfield = [[CPTextField alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 30.0)],
- bezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ bezelColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-rounded-left.png" size:CGSizeMake(13.0, 22.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-rounded-center.png" size:CGSizeMake(1.0, 22.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-rounded-right.png" size:CGSizeMake(13.0, 22.0)]
- ] isVertical:NO]],
+ ["textfield-bezel-rounded-left.png", 13.0, 22.0],
+ ["textfield-bezel-rounded-center.png", 1.0, 22.0],
+ ["textfield-bezel-rounded-right.png", 13.0, 22.0]
+ ],
+ PatternIsHorizontal),
- bezelFocusedColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+ bezelFocusedColor = PatternColor(
[
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-rounded-focused-left.png" size:CGSizeMake(17.0, 30.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-rounded-focused-center.png" size:CGSizeMake(1.0, 30.0)],
- [_CPCibCustomResource imageResourceWithName:"textfield-bezel-rounded-focused-right.png" size:CGSizeMake(17.0, 30.0)]
- ] isVertical:NO]];
+ ["textfield-bezel-rounded-focused-left.png", 17.0, 30.0],
+ ["textfield-bezel-rounded-focused-center.png", 1.0, 30.0],
+ ["textfield-bezel-rounded-focused-right.png", 17.0, 30.0]
+ ],
+ PatternIsHorizontal),
+
+ placeholderColor = [CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0],
+
+ themeValues =
+ [
+ [@"bezel-color", bezelColor, CPTextFieldStateRounded | CPThemeStateBezeled],
+ [@"bezel-color", bezelFocusedColor, CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing],
+ [@"font", [CPFont systemFontOfSize:12.0]],
+
+ [@"content-inset", CGInsetMake(9.0, 14.0, 6.0, 14.0), CPTextFieldStateRounded | CPThemeStateBezeled],
+ [@"bezel-inset", CGInsetMake(4.0, 4.0, 4.0, 4.0), CPTextFieldStateRounded | CPThemeStateBezeled],
+ [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing],
+
+ [@"text-color", placeholderColor, CPTextFieldStateRounded | CPTextFieldStatePlaceholder],
+
+ [@"min-size", CGSizeMake(0.0, 30.0), CPTextFieldStateRounded | CPThemeStateBezeled],
+ [@"max-size", CGSizeMake(-1.0, 30.0), CPTextFieldStateRounded | CPThemeStateBezeled]
+ ];
+
+ [self registerThemeValues:themeValues forView:textfield];
[textfield setBezeled:YES];
[textfield setBezelStyle:CPTextFieldRoundedBezel];
- [textfield setValue:bezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBezeled | CPTextFieldStateRounded];
- [textfield setValue:bezelFocusedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBezeled | CPTextFieldStateRounded | CPThemeStateEditing];
-
- [textfield setValue:[CPFont systemFontOfSize:12.0] forThemeAttribute:@"font"];
- [textfield setValue:CGInsetMake(9.0, 14.0, 6.0, 14.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBezeled | CPTextFieldStateRounded];
-
- [textfield setValue:CGInsetMake(4.0, 4.0, 4.0, 4.0) forThemeAttribute:@"bezel-inset" inState:CPThemeStateBezeled|CPTextFieldStateRounded];
- [textfield setValue:CGInsetMake(0.0, 0.0, 0.0, 0.0) forThemeAttribute:@"bezel-inset" inState:CPThemeStateBezeled|CPTextFieldStateRounded|CPThemeStateEditing];
-
- [textfield setValue:[CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color" inState:CPTextFieldStatePlaceholder];
-
[textfield setPlaceholderString:"placeholder"];
[textfield setStringValue:""];
[textfield setEditable:YES];
- [textfield setValue:CGSizeMake(0.0, 30.0) forThemeAttribute:@"min-size" inState:CPThemeStateBezeled|CPTextFieldStateRounded];
- [textfield setValue:CGSizeMake(-1.0, 30.0) forThemeAttribute:@"max-size" inState:CPThemeStateBezeled|CPTextFieldStateRounded];
-
return textfield;
}
+ (CPRadioButton)themedRadioButton
{
- var button = [[CPRadio alloc] initWithFrame:CGRectMake(0.0, 0.0, 120.0, 17.0)];
+ var button = [CPRadio radioWithTitle:@"Hello Friend!"],
- [button setTitle:@"Hello Friend!"];
+ imageNormal = PatternImage("radio-image.png", 17.0, 17.0),
+ imageSelected = PatternImage("radio-image-selected.png", 17.0, 17.0),
+ imageSelectedHighlighted = PatternImage("radio-image-selected-highlighted.png", 17.0, 17.0),
+ imageSelectedDisabled = PatternImage("radio-image-selected-disabled.png", 17.0, 17.0),
+ imageDisabled = PatternImage("radio-image-disabled.png", 17.0, 17.0),
+ imageHighlighted = PatternImage("radio-image-highlighted.png", 17.0, 17.0),
- [button setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled];
+ themeValues =
+ [
+ [@"alignment", CPLeftTextAlignment, CPThemeStateNormal],
+ [@"font", [CPFont systemFontOfSize:12.0], CPThemeStateNormal],
+ [@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateNormal],
- var bezelColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"radio-bezel.png" size:CGSizeMake(17.0, 17.0)], nil, nil
- ]
- isVertical:NO]),
- bezelColorSelected = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"radio-bezel-selected.png" size:CGSizeMake(17.0, 17.0)], nil, nil
- ]
- isVertical:NO]),
- bezelColorSelectedHighlighted = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"radio-bezel-selected-highlighted.png" size:CGSizeMake(17.0, 17.0)], nil, nil
- ]
- isVertical:NO]),
- bezelColorSelectedDisabled = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"radio-bezel-selected-disabled.png" size:CGSizeMake(17.0, 17.0)], nil, nil
- ]
- isVertical:NO]),
- bezelColorDisabled = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"radio-bezel-disabled.png" size:CGSizeMake(17.0, 17.0)], nil, nil
- ]
- isVertical:NO]),
- bezelColorHighlighted = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"radio-bezel-highlighted.png" size:CGSizeMake(17.0, 17.0)], nil, nil
- ]
- isVertical:NO]);
+ [@"image", imageNormal, CPThemeStateNormal],
+ [@"image", imageSelected, CPThemeStateSelected],
+ [@"image", imageSelectedHighlighted, CPThemeStateSelected | CPThemeStateHighlighted],
+ [@"image", imageHighlighted, CPThemeStateHighlighted],
+ [@"image", imageDisabled, CPThemeStateDisabled],
+ [@"image", imageSelectedDisabled, CPThemeStateSelected | CPThemeStateDisabled],
+ [@"image-offset", CPRadioImageOffset],
- [button setValue:CPLeftTextAlignment forThemeAttribute:@"alignment" inState:CPThemeStateBordered];
- [button setValue:[CPFont systemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBordered];
- [button setValue:CGInsetMake(0.0, 0.0, 0.0, 20.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered];
- [button setValue:bezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
- [button setValue:bezelColorSelected forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateSelected];
- [button setValue:bezelColorSelectedHighlighted forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateSelected | CPThemeStateHighlighted];
- [button setValue:bezelColorHighlighted forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
- [button setValue:bezelColorDisabled forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
- [button setValue:bezelColorSelectedDisabled forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled | CPThemeStateSelected];
+ [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0], CPThemeStateDisabled],
- [button setValue:CGSizeMake(0.0, 17.0) forThemeAttribute:@"min-size"];
+ [@"min-size", CGSizeMake(0.0, 17.0)],
+ [@"max-size", CGSizeMake(-1.0, -1.0)]
+ ];
+
+ [self registerThemeValues:themeValues forView:button];
return button;
}
-+ (CPRadioButton)themedCheckBoxButton
++ (CPCheckBox)themedCheckBoxButton
{
- var button = [[CPCheckBox alloc] initWithFrame:CGRectMake(0.0, 0.0, 120.0, 17.0)];
-
- [button setTitle:@"Another option"];
+ var button = [CPCheckBox checkBoxWithTitle:@"Another Option"],
- [button setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled];
+ imageNormal = PatternImage("check-box-image.png", 15.0, 16.0),
+ imageSelected = PatternImage("check-box-image-selected.png", 15.0, 16.0),
+ imageSelectedHighlighted = PatternImage("check-box-image-selected-highlighted.png", 15.0, 16.0),
+ imageSelectedDisabled = PatternImage("check-box-image-selected-disabled.png", 15.0, 16.0),
+ imageDisabled = PatternImage("check-box-image-disabled.png", 15.0, 16.0),
+ imageHighlighted = PatternImage("check-box-image-highlighted.png", 15.0, 16.0),
- var bezelColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"check-box-bezel.png" size:CGSizeMake(15.0, 16.0)], nil, nil
- ]
- isVertical:NO]),
- bezelColorSelected = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"check-box-bezel-selected.png" size:CGSizeMake(15.0, 16.0)], nil, nil
- ]
- isVertical:NO]),
- bezelColorSelectedHighlighted = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"check-box-bezel-selected-highlighted.png" size:CGSizeMake(15.0, 16.0)], nil, nil
- ]
- isVertical:NO]),
- bezelColorSelectedDisabled = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"check-box-bezel-selected-disabled.png" size:CGSizeMake(15.0, 16.0)], nil, nil
- ]
- isVertical:NO]),
- bezelColorDisabled = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"check-box-bezel-disabled.png" size:CGSizeMake(15.0, 16.0)], nil, nil
- ]
- isVertical:NO]),
- bezelColorHighlighted = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"check-box-bezel-highlighted.png" size:CGSizeMake(15.0, 16.0)], nil, nil
- ]
- isVertical:NO]);
-
- [button setValue:CPLeftTextAlignment forThemeAttribute:@"alignment" inState:CPThemeStateBordered];
- [button setValue:[CPFont systemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBordered];
- [button setValue:CGInsetMake(0.0, 0.0, 0.0, 20.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered];
- [button setValue:bezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
- [button setValue:bezelColorSelected forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateSelected];
- [button setValue:bezelColorSelectedHighlighted forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateSelected | CPThemeStateHighlighted];
- [button setValue:bezelColorHighlighted forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
- [button setValue:bezelColorDisabled forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
- [button setValue:bezelColorSelectedDisabled forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled | CPThemeStateSelected];
+ themeValues =
+ [
+ [@"alignment", CPLeftTextAlignment, CPThemeStateNormal],
+ [@"font", [CPFont systemFontOfSize:12.0], CPThemeStateNormal],
+ [@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateNormal],
- [button setValue:CGSizeMake(0.0, 17.0) forThemeAttribute:@"min-size"];
+ [@"image", imageNormal, CPThemeStateNormal],
+ [@"image", imageSelected, CPThemeStateSelected],
+ [@"image", imageSelectedHighlighted, CPThemeStateSelected | CPThemeStateHighlighted],
+ [@"image", imageHighlighted, CPThemeStateHighlighted],
+ [@"image", imageDisabled, CPThemeStateDisabled],
+ [@"image", imageSelectedDisabled, CPThemeStateSelected | CPThemeStateDisabled],
+ [@"image-offset", CPCheckBoxImageOffset],
+
+ [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0], CPThemeStateDisabled],
+
+ [@"min-size", CGSizeMake(0.0, 17.0)],
+ [@"max-size", CGSizeMake(-1.0, -1.0)]
+ ];
+
+ [self registerThemeValues:themeValues forView:button];
return button;
}
-+ (CPRadioButton)themedMixedCheckBoxButton
++ (CPCheckBox)themedMixedCheckBoxButton
{
- button = [self themedCheckBoxButton];
+ var button = [self themedCheckBoxButton];
[button setAllowsMixedState:YES];
[button setState:CPMixedState];
- var mixedSelectedColor =
- PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"check-box-bezel-mixed-highlighted.png" size:CGSizeMake(15.0, 16.0)], nil, nil
- ]
- isVertical:NO]),
-
- mixedDisabledColor =
- PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"check-box-bezel-mixed-disabled.png" size:CGSizeMake(15.0, 16.0)], nil, nil
- ]
- isVertical:NO]),
-
- mixedColor =
- PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"check-box-bezel-mixed.png" size:CGSizeMake(15.0, 16.0)], nil, nil
- ]
- isVertical:NO]);
+ var mixedHighlightedImage = PatternImage("check-box-image-mixed-highlighted.png", 15.0, 16.0),
+ mixedDisabledImage = PatternImage("check-box-image-mixed-disabled.png", 15.0, 16.0),
+ mixedImage = PatternImage("check-box-image-mixed.png", 15.0, 16.0),
+
+ themeValues =
+ [
+ [@"image", mixedImage, CPButtonStateMixed],
+ [@"image", mixedHighlightedImage, CPButtonStateMixed | CPThemeStateHighlighted],
+ [@"image", mixedDisabledImage, CPButtonStateMixed | CPThemeStateDisabled],
+ [@"image-offset", CPCheckBoxImageOffset, CPButtonStateMixed],
+ [@"max-size", CGSizeMake(-1.0, -1.0)]
+ ];
+
+ [self registerThemeValues:themeValues forView:button];
- [button setValue:mixedSelectedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeState("mixed")|CPThemeStateHighlighted];
- [button setValue:mixedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeState("mixed")];
- [button setValue:mixedDisabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeState("mixed")|CPThemeStateDisabled];
-
return button;
}
-+ (CPPopUpButton)themedSegmentedControl
++ (CPSegmentedControl)makeSegmentedControl
{
var segmentedControl = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 24.0)];
-
+
[segmentedControl setTrackingMode:CPSegmentSwitchTrackingSelectAny];
[segmentedControl setSegmentCount:3];
-
+
[segmentedControl setWidth:40.0 forSegment:0];
[segmentedControl setLabel:@"foo" forSegment:0];
[segmentedControl setTag:1 forSegment:0];
@@ -560,209 +913,218 @@
[segmentedControl setWidth:35.0 forSegment:2];
[segmentedControl setLabel:@"1" forSegment:2];
[segmentedControl setTag:3 forSegment:2];
-
- //various colors
- var centerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-center.png" size:CGSizeMake(1.0, 24.0)]),
- dividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-divider.png" size:CGSizeMake(1.0, 24.0)]),
- centerHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-center.png" size:CGSizeMake(1.0, 24.0)]),
- dividerHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-divider.png" size:CGSizeMake(1.0, 24.0)]),
- leftHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-left.png" size:CGSizeMake(4.0, 24.0)]),
- rightHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]),
- inactiveCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)]),
- inactiveDividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-disabled-divider.png" size:CGSizeMake(1.0, 24.0)]),
- inactiveLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)]),
- inactiveRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)]),
- inactiveHighlightedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-disabled-center.png" size:CGSizeMake(1.0, 24.0)]),
- inactiveHighlightedDividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-disabled-divider.png" size:CGSizeMake(1.0, 24.0)]),
- inactiveHighlightedLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-disabled-left.png" size:CGSizeMake(4.0, 24.0)]),
- inactiveHighlightedRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-highlighted-disabled-right.png" size:CGSizeMake(4.0, 24.0)]),
- leftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-left.png" size:CGSizeMake(4.0, 24.0)]),
- rightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-right.png" size:CGSizeMake(4.0, 24.0)]),
- pushedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-pushed-center.png" size:CGSizeMake(1.0, 24.0)]),
- pushedLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-pushed-left.png" size:CGSizeMake(4.0, 24.0)]),
- pushedRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-pushed-right.png" size:CGSizeMake(4.0, 24.0)]);
- pushedHighlightedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-pushed-highlighted-center.png" size:CGSizeMake(1.0, 24.0)]),
- pushedHighlightedLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-pushed-highlighted-left.png" size:CGSizeMake(4.0, 24.0)]),
- pushedHighlightedRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"segmented-control-bezel-pushed-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]);
- [segmentedControl setValue:centerBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateNormal];
- [segmentedControl setValue:inactiveCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateDisabled];
- [segmentedControl setValue:inactiveHighlightedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
- [segmentedControl setValue:centerHighlightedBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateSelected];
- [segmentedControl setValue:pushedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateHighlighted];
- [segmentedControl setValue:pushedHighlightedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected];
+ return segmentedControl;
+};
- [segmentedControl setValue:dividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateNormal];
- [segmentedControl setValue:inactiveDividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateDisabled];
- [segmentedControl setValue:inactiveHighlightedDividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
- [segmentedControl setValue:dividerHighlightedBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateSelected];
- [segmentedControl setValue:dividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateHighlighted];
++ (CPSegmentedControl)themedSegmentedControl
+{
+ var segmentedControl = [self makeSegmentedControl],
- [segmentedControl setValue:rightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateNormal];
- [segmentedControl setValue:inactiveRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateDisabled];
- [segmentedControl setValue:inactiveHighlightedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
- [segmentedControl setValue:rightHighlightedBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateSelected];
- [segmentedControl setValue:pushedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateHighlighted];
- [segmentedControl setValue:pushedHighlightedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected];
+ centerBezelColor = PatternColor("segmented-control-bezel-center.png", 1.0, 24.0),
+ dividerBezelColor = PatternColor("segmented-control-bezel-divider.png", 1.0, 24.0),
+ centerHighlightedBezelColor = PatternColor("segmented-control-bezel-highlighted-center.png", 1.0, 24.0),
+ dividerHighlightedBezelColor = PatternColor("segmented-control-bezel-highlighted-divider.png", 1.0, 24.0),
+ leftHighlightedBezelColor = PatternColor("segmented-control-bezel-highlighted-left.png", 4.0, 24.0),
+ rightHighlightedBezelColor = PatternColor("segmented-control-bezel-highlighted-right.png", 4.0, 24.0),
+ inactiveCenterBezelColor = PatternColor("segmented-control-bezel-disabled-center.png", 1.0, 24.0),
+ inactiveDividerBezelColor = PatternColor("segmented-control-bezel-disabled-divider.png", 1.0, 24.0),
+ inactiveLeftBezelColor = PatternColor("segmented-control-bezel-disabled-left.png", 4.0, 24.0),
+ inactiveRightBezelColor = PatternColor("segmented-control-bezel-disabled-right.png", 4.0, 24.0),
+ inactiveHighlightedCenterBezelColor = PatternColor("segmented-control-bezel-highlighted-disabled-center.png", 1.0, 24.0),
+ inactiveHighlightedDividerBezelColor = PatternColor("segmented-control-bezel-highlighted-disabled-divider.png", 1.0, 24.0),
+ inactiveHighlightedLeftBezelColor = PatternColor("segmented-control-bezel-highlighted-disabled-left.png", 4.0, 24.0),
+ inactiveHighlightedRightBezelColor = PatternColor("segmented-control-bezel-highlighted-disabled-right.png", 4.0, 24.0),
+ leftBezelColor = PatternColor("segmented-control-bezel-left.png", 4.0, 24.0),
+ rightBezelColor = PatternColor("segmented-control-bezel-right.png", 4.0, 24.0),
+ pushedCenterBezelColor = PatternColor("segmented-control-bezel-pushed-center.png", 1.0, 24.0),
+ pushedLeftBezelColor = PatternColor("segmented-control-bezel-pushed-left.png", 4.0, 24.0),
+ pushedRightBezelColor = PatternColor("segmented-control-bezel-pushed-right.png", 4.0, 24.0),
+ pushedHighlightedCenterBezelColor = PatternColor("segmented-control-bezel-pushed-highlighted-center.png", 1.0, 24.0),
+ pushedHighlightedLeftBezelColor = PatternColor("segmented-control-bezel-pushed-highlighted-left.png", 4.0, 24.0),
+ pushedHighlightedRightBezelColor = PatternColor("segmented-control-bezel-pushed-highlighted-right.png", 4.0, 24.0);
- [segmentedControl setValue:leftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateNormal];
- [segmentedControl setValue:inactiveLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateDisabled];
- [segmentedControl setValue:inactiveHighlightedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
- [segmentedControl setValue:leftHighlightedBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateSelected];
- [segmentedControl setValue:pushedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateHighlighted];
- [segmentedControl setValue:pushedHighlightedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected];
+ themedSegmentedControlValues =
+ [
+ [@"center-segment-bezel-color", centerBezelColor, CPThemeStateNormal],
+ [@"center-segment-bezel-color", inactiveCenterBezelColor, CPThemeStateDisabled],
+ [@"center-segment-bezel-color", inactiveHighlightedCenterBezelColor, CPThemeStateSelected | CPThemeStateDisabled],
+ [@"center-segment-bezel-color", centerHighlightedBezelColor, CPThemeStateSelected],
+ [@"center-segment-bezel-color", pushedCenterBezelColor, CPThemeStateHighlighted],
+ [@"center-segment-bezel-color", pushedHighlightedCenterBezelColor, CPThemeStateHighlighted | CPThemeStateSelected],
- [segmentedControl setValue:CGInsetMake(0.0, 4.0, 0.0, 4.0) forThemeAttribute:@"content-inset" inState:CPThemeStateNormal];
+ [@"divider-bezel-color", dividerBezelColor, CPThemeStateNormal],
+ [@"divider-bezel-color", inactiveDividerBezelColor, CPThemeStateDisabled],
+ [@"divider-bezel-color", inactiveHighlightedDividerBezelColor, CPThemeStateSelected | CPThemeStateDisabled],
+ [@"divider-bezel-color", dividerHighlightedBezelColor, CPThemeStateSelected],
- [segmentedControl setValue:CGInsetMake(0.0, 0.0, 0.0, 0.0) forThemeAttribute:@"bezel-inset" inState:CPThemeStateNormal];
+ [@"left-segment-bezel-color", leftBezelColor, CPThemeStateNormal],
+ [@"left-segment-bezel-color", inactiveLeftBezelColor, CPThemeStateDisabled],
+ [@"left-segment-bezel-color", inactiveHighlightedLeftBezelColor, CPThemeStateSelected | CPThemeStateDisabled],
+ [@"left-segment-bezel-color", leftHighlightedBezelColor, CPThemeStateSelected],
+ [@"left-segment-bezel-color", pushedLeftBezelColor, CPThemeStateHighlighted],
+ [@"left-segment-bezel-color", pushedHighlightedLeftBezelColor, CPThemeStateHighlighted | CPThemeStateSelected],
- [segmentedControl setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font"];
- [segmentedControl setValue:[CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"];
- [segmentedControl setValue:[CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-shadow-color"];
- [segmentedControl setValue:CGSizeMake(0.0, 1.0) forThemeAttribute:@"text-shadow-offset"];
- [segmentedControl setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
+ [@"right-segment-bezel-color", rightBezelColor, CPThemeStateNormal],
+ [@"right-segment-bezel-color", inactiveRightBezelColor, CPThemeStateDisabled],
+ [@"right-segment-bezel-color", inactiveHighlightedRightBezelColor, CPThemeStateSelected | CPThemeStateDisabled],
+ [@"right-segment-bezel-color", rightHighlightedBezelColor, CPThemeStateSelected],
+ [@"right-segment-bezel-color", pushedRightBezelColor, CPThemeStateHighlighted],
+ [@"right-segment-bezel-color", pushedHighlightedRightBezelColor, CPThemeStateHighlighted | CPThemeStateSelected],
- [segmentedControl setValue:1.0 forThemeAttribute:@"divider-thickness"];
- [segmentedControl setValue:24.0 forThemeAttribute:@"default-height"];
+ [@"content-inset", CGInsetMake(0.0, 4.0, 0.0, 4.0), CPThemeStateNormal],
+ [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateNormal],
+
+ [@"font", [CPFont boldSystemFontOfSize:12.0]],
+ [@"text-color", [CPColor colorWithCalibratedWhite:79.0 / 255.0 alpha:1.0]],
+ [@"text-color", [CPColor colorWithCalibratedWhite:0.6 alpha:1.0], CPThemeStateDisabled],
+ [@"text-shadow-color", [CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0]],
+ [@"text-shadow-color", [CPColor colorWithCalibratedWhite:240.0 / 255.0 alpha:1.0], CPThemeStateDisabled],
+ [@"text-shadow-offset", CGSizeMake(0.0, 1.0)],
+ [@"line-break-mode", CPLineBreakByTruncatingTail],
+
+ [@"divider-thickness", 1.0],
+ [@"default-height", 24.0]
+ ];
+
+ [self registerThemeValues:themedSegmentedControlValues forView:segmentedControl];
return segmentedControl;
}
++ (CPSlider)makeHorizontalSlider
+{
+ return [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 50.0, 24.0)];
+}
+
+ (CPSlider)themedHorizontalSlider
{
- var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 50.0, 24.0)],
- trackColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"horizontal-track-left.png" size:CGSizeMake(4.0, 5.0)],
- [_CPCibCustomResource imageResourceWithName:"horizontal-track-center.png" size:CGSizeMake(1.0, 5.0)],
- [_CPCibCustomResource imageResourceWithName:"horizontal-track-right.png" size:CGSizeMake(4.0, 5.0)]
- ]
- isVertical:NO]],
-
- trackDisabledColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"horizontal-track-disabled-left.png" size:CGSizeMake(4.0, 5.0)],
- [_CPCibCustomResource imageResourceWithName:"horizontal-track-disabled-center.png" size:CGSizeMake(1.0, 5.0)],
- [_CPCibCustomResource imageResourceWithName:"horizontal-track-disabled-right.png" size:CGSizeMake(4.0, 5.0)]
- ]
- isVertical:NO]];
+ var slider = [self makeHorizontalSlider],
- [slider setValue:5.0 forThemeAttribute:@"track-width"];
- [slider setValue:trackColor forThemeAttribute:@"track-color"];
- [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled];
-
- var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob.png" size:CGSizeMake(23.0, 24.0)]],
- knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]],
- knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-disabled.png" size:CGSizeMake(23.0, 24.0)]];
+ trackColor = PatternColor(
+ [
+ ["horizontal-track-left.png", 4.0, 5.0],
+ ["horizontal-track-center.png", 1.0, 5.0],
+ ["horizontal-track-right.png", 4.0, 5.0]
+ ],
+ PatternIsHorizontal),
+
+ trackDisabledColor = PatternColor(
+ [
+ ["horizontal-track-disabled-left.png", 4.0, 5.0],
+ ["horizontal-track-disabled-center.png", 1.0, 5.0],
+ ["horizontal-track-disabled-right.png", 4.0, 5.0]
+ ],
+ PatternIsHorizontal),
+
+ knobColor = PatternColor("knob.png", 23.0, 24.0),
+ knobHighlightedColor = PatternColor("knob-highlighted.png", 23.0, 24.0),
+ knobDisabledColor = PatternColor("knob-disabled.png", 23.0, 24.0);
+
+ themedHorizontalSliderValues =
+ [
+ [@"track-width", 5.0],
+ [@"track-color", trackColor],
+ [@"track-color", trackDisabledColor, CPThemeStateDisabled],
+
+ [@"knob-size", CGSizeMake(23.0, 24.0)],
+ [@"knob-color", knobColor],
+ [@"knob-color", knobHighlightedColor, CPThemeStateHighlighted],
+ [@"knob-color", knobDisabledColor, CPThemeStateDisabled]
+ ];
+
+ [self registerThemeValues:themedHorizontalSliderValues forView:slider];
- [slider setValue:CGSizeMake(23.0, 24.0) forThemeAttribute:@"knob-size"];
- [slider setValue:knobColor forThemeAttribute:@"knob-color"];
- [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateHighlighted];
- [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled];
-
return slider;
}
++ (CPSlider)makeVerticalSlider
+{
+ return [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 24.0, 50.0)];
+}
+
+ (CPSlider)themedVerticalSlider
{
- var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 24.0, 50.0)],
- trackColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"vertical-track-top.png" size:CGSizeMake(5.0, 6.0)],
- [_CPCibCustomResource imageResourceWithName:"vertical-track-center.png" size:CGSizeMake(5.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"vertical-track-bottom.png" size:CGSizeMake(5.0, 4.0)]
- ]
- isVertical:YES]],
- trackDisabledColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"vertical-track-disabled-top.png" size:CGSizeMake(5.0, 6.0)],
- [_CPCibCustomResource imageResourceWithName:"vertical-track-disabled-center.png" size:CGSizeMake(5.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"vertical-track-disabled-bottom.png" size:CGSizeMake(5.0, 4.0)]
- ]
- isVertical:YES]];
-
- [slider setValue:5.0 forThemeAttribute:@"track-width"];
- [slider setValue:trackColor forThemeAttribute:@"track-color" inState:CPThemeStateVertical];
- [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled];
-
- var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob.png" size:CGSizeMake(23.0, 24.0)]],
- knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]],
- knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-disabled.png" size:CGSizeMake(23.0, 24.0)]];
+ var slider = [self makeVerticalSlider],
+
+ trackColor = PatternColor(
+ [
+ ["vertical-track-top.png", 5.0, 6.0],
+ ["vertical-track-center.png", 5.0, 1.0],
+ ["vertical-track-bottom.png", 5.0, 4.0]
+ ],
+ PatternIsVertical),
+
+ trackDisabledColor = PatternColor(
+ [
+ ["vertical-track-disabled-top.png", 5.0, 6.0],
+ ["vertical-track-disabled-center.png", 5.0, 1.0],
+ ["vertical-track-disabled-bottom.png", 5.0, 4.0]
+ ],
+ PatternIsVertical),
+
+ knobColor = PatternColor("knob.png", 23.0, 24.0),
+ knobHighlightedColor = PatternColor("knob-highlighted.png", 23.0, 24.0),
+ knobDisabledColor = PatternColor("knob-disabled.png", 23.0, 24.0);
+
+ themedVerticalSliderValues =
+ [
+ [@"track-width", 5.0],
+ [@"track-color", trackColor, CPThemeStateVertical],
+ [@"track-color", trackDisabledColor, CPThemeStateDisabled],
+
+ [@"knob-size", CGSizeMake(23.0, 24.0)],
+ [@"knob-color", knobColor],
+ [@"knob-color", knobHighlightedColor, CPThemeStateHighlighted],
+ [@"knob-color", knobDisabledColor, CPThemeStateDisabled]
+ ];
+
+ [self registerThemeValues:themedVerticalSliderValues forView:slider];
+
+ return slider;
+}
+
++ (CPSlider)makeCircularSlider
+{
+ var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 34.0, 34.0)];
+
+ [slider setSliderType:CPCircularSlider];
- [slider setValue:CGSizeMake(23.0, 24.0) forThemeAttribute:@"knob-size"];
- [slider setValue:knobColor forThemeAttribute:@"knob-color"];
- [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateHighlighted];
- [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled];
-
return slider;
}
+ (CPSlider)themedCircularSlider
{
- var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 34.0, 34.0)],
- trackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"slider-circular-bezel.png" size:CGSizeMake(34.0, 34.0)]),
- trackDisabledColor = PatternColor([_CPCibCustomResource imageResourceWithName:"slider-circular-disabled-bezel.png" size:CGSizeMake(34.0, 34.0)]);
+ var slider = [self makeCircularSlider],
- [slider setSliderType:CPCircularSlider];
- [slider setValue:trackColor forThemeAttribute:@"track-color" inState:CPThemeStateCircular];
- [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled|CPThemeStateCircular];
-
- var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"slider-circular-knob.png" size:CGSizeMake(5.0, 5.0)]],
- knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"slider-circular-disabled-knob.png" size:CGSizeMake(5.0, 5.0)]],
+ trackColor = PatternColor("slider-circular-bezel.png", 34.0, 34.0),
+ trackDisabledColor = PatternColor("slider-circular-disabled-bezel.png", 34.0, 34.0),
+ knobColor = PatternColor("slider-circular-knob.png", 5.0, 5.0),
+ knobDisabledColor = PatternColor("slider-circular-disabled-knob.png", 5.0, 5.0),
knobHighlightedColor = knobColor;
- [slider setValue:CGSizeMake(5.0, 5.0) forThemeAttribute:@"knob-size" inState:CPThemeStateCircular];
- [slider setValue:knobColor forThemeAttribute:@"knob-color" inState:CPThemeStateCircular];
- [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled|CPThemeStateCircular];
- [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateCircular|CPThemeStateHighlighted];
+ themedCircularSliderValues =
+ [
+ [@"track-color", trackColor, CPThemeStateCircular],
+ [@"track-color", trackDisabledColor, CPThemeStateCircular | CPThemeStateDisabled],
+
+ [@"knob-size", CGSizeMake(5.0, 5.0), CPThemeStateCircular],
+ [@"knob-color", knobColor, CPThemeStateCircular],
+ [@"knob-color", knobHighlightedColor, CPThemeStateCircular | CPThemeStateHighlighted],
+ [@"knob-color", knobDisabledColor, CPThemeStateCircular | CPThemeStateDisabled]
+ ];
+
+ [self registerThemeValues:themedCircularSliderValues forView:slider];
return slider;
}
-+ (CPButtonBar)themedButtonBar
++ (CPButtonBar)makeButtonBar
{
- var buttonBar = [[CPButtonBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 147.0, 26.0)],
- color = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"buttonbar-bezel.png" size:CGSizeMake(1.0, 26.0)]];
+ var buttonBar = [[CPButtonBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 147.0, 26.0)];
[buttonBar setHasResizeControl:YES];
- [buttonBar setValue:color forThemeAttribute:@"bezel-color"];
-
- var resizeColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"buttonbar-resize-control.png" size:CGSizeMake(5.0, 10.0)]];
-
- [buttonBar setValue:CGSizeMake(5.0, 10.0) forThemeAttribute:@"resize-control-size"];
- [buttonBar setValue:CGInsetMake(9.0, 4.0, 7.0, 4.0) forThemeAttribute:@"resize-control-inset"];
- [buttonBar setValue:resizeColor forThemeAttribute:@"resize-control-color"];
-
- var buttonBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"buttonbar-button-bezel-left.png" size:CGSizeMake(2.0, 25.0)],
- [_CPCibCustomResource imageResourceWithName:"buttonbar-button-bezel-center.png" size:CGSizeMake(1.0, 25.0)],
- [_CPCibCustomResource imageResourceWithName:"buttonbar-button-bezel-right.png" size:CGSizeMake(2.0, 25.0)]
- ]
- isVertical:NO]],
-
- buttonBezelHighlightedColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"buttonbar-button-bezel-highlighted-left.png" size:CGSizeMake(2.0, 25.0)],
- [_CPCibCustomResource imageResourceWithName:"buttonbar-button-bezel-highlighted-center.png" size:CGSizeMake(1.0, 25.0)],
- [_CPCibCustomResource imageResourceWithName:"buttonbar-button-bezel-highlighted-right.png" size:CGSizeMake(2.0, 25.0)]
- ]
- isVertical:NO]],
-
- buttonBezelDisabledColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"buttonbar-button-bezel-disabled-left.png" size:CGSizeMake(2.0, 25.0)],
- [_CPCibCustomResource imageResourceWithName:"buttonbar-button-bezel-disabled-center.png" size:CGSizeMake(1.0, 25.0)],
- [_CPCibCustomResource imageResourceWithName:"buttonbar-button-bezel-disabled-right.png" size:CGSizeMake(2.0, 25.0)]
- ]
- isVertical:NO]];
-
- [buttonBar setValue:buttonBezelColor forThemeAttribute:@"button-bezel-color"];
- [buttonBar setValue:buttonBezelHighlightedColor forThemeAttribute:@"button-bezel-color" inState:CPThemeStateHighlighted];
- [buttonBar setValue:buttonBezelDisabledColor forThemeAttribute:@"button-bezel-color" inState:CPThemeStateDisabled];
- [buttonBar setValue:[CPColor blackColor] forThemeAttribute:@"button-text-color"];
-
var popup = [CPButtonBar actionPopupButton];
[popup addItemWithTitle:"Item 1"];
[popup addItemWithTitle:"Item 2"];
@@ -772,6 +1134,214 @@
return buttonBar;
}
++ (CPButtonBar)themedButtonBar
+{
+ var buttonBar = [self makeButtonBar],
+
+ color = PatternColor("buttonbar-bezel.png", 1.0, 26.0),
+ resizeColor = PatternColor("buttonbar-resize-control.png", 5.0, 10.0),
+ buttonBezelColor = PatternColor(
+ [
+ ["buttonbar-button-bezel-left.png", 2.0, 25.0],
+ ["buttonbar-button-bezel-center.png", 1.0, 25.0],
+ ["buttonbar-button-bezel-right.png", 2.0, 25.0]
+ ],
+ PatternIsHorizontal),
+
+ buttonBezelHighlightedColor = PatternColor(
+ [
+ ["buttonbar-button-bezel-highlighted-left.png", 2.0, 25.0],
+ ["buttonbar-button-bezel-highlighted-center.png", 1.0, 25.0],
+ ["buttonbar-button-bezel-highlighted-right.png", 2.0, 25.0]
+ ],
+ PatternIsHorizontal),
+
+ buttonBezelDisabledColor = PatternColor(
+ [
+ ["buttonbar-button-bezel-disabled-left.png", 2.0, 25.0],
+ ["buttonbar-button-bezel-disabled-center.png", 1.0, 25.0],
+ ["buttonbar-button-bezel-disabled-right.png", 2.0, 25.0]
+ ],
+ PatternIsHorizontal);
+
+ themedButtonBarValues =
+ [
+ [@"bezel-color", color],
+
+ [@"resize-control-size", CGSizeMake(5.0, 10.0)],
+ [@"resize-control-inset", CGInsetMake(9.0, 4.0, 7.0, 4.0)],
+ [@"resize-control-color", resizeColor],
+
+ [@"button-bezel-color", buttonBezelColor],
+ [@"button-bezel-color", buttonBezelHighlightedColor, CPThemeStateHighlighted],
+ [@"button-bezel-color", buttonBezelDisabledColor, CPThemeStateDisabled],
+ [@"button-text-color", [CPColor blackColor]]
+ ];
+
+ [self registerThemeValues:themedButtonBarValues forView:buttonBar];
+
+ return buttonBar;
+}
+
++ (_CPTableColumnHeaderView)makeColumnHeader
+{
+ var header = [[_CPTableColumnHeaderView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 24.0)];
+
+ [header setStringValue:@"Table Header"];
+
+ return header;
+}
+
++ (_CPTableColumnHeaderView)themedColumnHeader
+{
+ var header = [self makeColumnHeader],
+ highlightedPressed = PatternColor("tableview-headerview-highlighted-pressed.png", 1.0, 23.0),
+ highlighted = PatternColor("tableview-headerview-highlighted.png", 1.0, 23.0),
+ pressed = PatternColor("tableview-headerview-pressed.png", 1.0, 23.0),
+ normal = PatternColor("tableview-headerview.png", 1.0, 23.0),
+
+ themedColumnHeaderValues =
+ [
+ [@"background-color", normal],
+
+ [@"text-inset", CGInsetMake(0, 5, 0, 5)],
+ [@"text-color", [CPColor colorWithCalibratedWhite:51.0 / 255.0 alpha:1.0]],
+ [@"text-font", [CPFont boldSystemFontOfSize:12.0]],
+ [@"text-shadow-color", [CPColor whiteColor]],
+ [@"text-shadow-offset", CGSizeMake(0.0, 1.0)],
+ [@"text-alignment", CPLeftTextAlignment],
+
+ [@"background-color", pressed, CPThemeStateHighlighted],
+ [@"background-color", highlighted, CPThemeStateSelected],
+ [@"background-color", highlightedPressed, CPThemeStateHighlighted | CPThemeStateSelected]
+ ];
+
+ [self registerThemeValues:themedColumnHeaderValues forView:header];
+
+ return header;
+}
+
++ (CPTableHeaderView)themedTableHeaderRow
+{
+ var header = [[CPTableHeaderView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 23.0)],
+ normal = PatternColor("tableview-headerview.png", 1.0, 23.0),
+ gridColor = [CPColor colorWithHexString:@"dce0e2"];
+
+ [header setValue:normal forThemeAttribute:@"background-color"];
+ [header setValue:gridColor forThemeAttribute:@"divider-color"];
+
+ return header;
+}
+
++ (_CPCornerView)themedCornerview
+{
+ var scrollerWidth = [CPScroller scrollerWidth],
+ corner = [[_CPCornerView alloc] initWithFrame:CGRectMake(0.0, 0.0, scrollerWidth, 23.0)],
+ normal = PatternColor("tableview-headerview.png", 1.0, 23.0);
+
+ [corner setValue:normal forThemeAttribute:"background-color"];
+
+ return corner;
+}
+
++ (CPTableView)themedTableView
+{
+ // This is a bit more complicated than the rest because we actually set theme values for several different (table related) controls in this method
+
+ var tableview = [[CPTableView alloc] initWithFrame:CGRectMake(0.0, 0.0, 150.0, 150.0)],
+
+ sortImage = PatternImage("tableview-headerview-ascending.png", 9.0, 8.0),
+ sortImageReversed = PatternImage("tableview-headerview-descending.png", 9.0, 8.0),
+ alternatingRowColors = [[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]],
+ gridColor = [CPColor colorWithHexString:@"dce0e2"],
+ selectionColor = [CPColor colorWithHexString:@"5f83b9"],
+ sourceListSelectionColor = [CPDictionary dictionaryWithObjects: [CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [89.0/255.0, 153.0/255.0, 209.0/255.0,1.0, 33.0/255.0, 94.0/255.0, 208.0/255.0,1.0], [0,1], 2),
+ [CPColor colorWithCalibratedRed:(61.0/255.0) green:(123.0/255.0) blue:(218.0/255.0) alpha:1.0],
+ [CPColor colorWithCalibratedRed:(31.0/255.0) green:(92.0/255.0) blue:(207.0/255.0) alpha:1.0]
+ ]
+ forKeys: [CPSourceListGradient, CPSourceListTopLineColor, CPSourceListBottomLineColor]],
+
+ themedTableViewValues =
+ [
+ [@"alternating-row-colors", alternatingRowColors],
+ [@"grid-color", gridColor],
+ [@"highlighted-grid-color", [CPColor whiteColor]],
+ [@"selection-color", selectionColor],
+ [@"sourcelist-selection-color", sourceListSelectionColor],
+ [@"sort-image", sortImage],
+ [@"sort-image-reversed", sortImageReversed]
+ ];
+
+ [self registerThemeValues:themedTableViewValues forView:tableview];
+
+ return tableview;
+}
+
++ (CPTextField)themedTableDataView
+{
+ var view = [self themedStandardTextField];
+
+ [view setBezeled:NO];
+ [view setEditable:NO];
+ [view setThemeState:CPThemeStateTableDataView];
+
+ return view;
+}
+
++ (CPSplitView)themedSplitView
+{
+ var splitView = [[CPSplitView alloc] initWithFrame:CGRectMake(0.0, 0.0, 200.0, 200.0)],
+ leftView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 75.0, 150.0)],
+ rightView = [[CPView alloc] initWithFrame:CGRectMake(75.0, 0.0, 75.0, 150.0)];
+
+ [splitView addSubview:leftView];
+ [splitView addSubview:rightView];
+
+
+ var themedSplitViewValues =
+ [
+ [@"divider-thickness", 10.0],
+ [@"pane-divider-thickness", 1.0]
+ ];
+
+ [self registerThemeValues:themedSplitViewValues forView:splitView];
+
+ return splitView;
+}
+
++ (CPAlert)themedAlert
+{
+ var alert = [CPAlert new],
+ size = CGSizeMake(400.0, 110.0),
+ inset = CGInsetMake(15, 15, 15, 80),
+ imageOffset = CGPointMake(15, 18),
+ messageFont = [CPFont boldSystemFontOfSize:13.0],
+ informativeFont = [CPFont systemFontOfSize:12.0],
+ informationIcon = PatternImage("alert-info.png", 53.0, 46.0),
+ warningIcon = PatternImage("alert-warning.png", 53.0, 46.0),
+ errorIcon = PatternImage("alert-error.png", 53.0, 46.0);
+
+ themedAlertValues =
+ [
+ [@"size", size],
+ [@"content-inset", inset],
+ [@"message-text-alignment", CPJustifiedTextAlignment],
+ [@"message-text-color", [CPColor blackColor]],
+ [@"message-text-font", messageFont],
+ [@"informative-text-alignment", CPJustifiedTextAlignment],
+ [@"informative-text-color", [CPColor blackColor]],
+ [@"informative-text-font", informativeFont],
+ [@"image-offset", imageOffset],
+ [@"information-image", informationIcon],
+ [@"warning-image", warningIcon],
+ [@"error-image", errorIcon]
+ ];
+
+ [self registerThemeValues:themedAlertValues forView:alert];
+
+ return alert;
+}
+
@end
@implementation AristoHUDThemeDescriptor : BKThemeDescriptor
@@ -783,172 +1353,46 @@
return @"Aristo-HUD";
}
++ (CPArray)themeShowcaseExcludes
+{
+ return ["alert"];
+}
+
+ (CPColor)defaultShowcaseBackgroundColor
{
return [CPColor blackColor];
}
++ (CPArray)defaultThemeOverridesAddedTo:(CPArray)themeValues
+{
+ var overrides = [CPArray arrayWithObjects:
+ [@"text-color", [CPColor whiteColor]],
+ [@"text-color", [CPColor colorWithCalibratedWhite:1.0 alpha:0.6], CPThemeStateDisabled],
+ [@"text-shadow-color", [CPColor blackColor]],
+ [@"text-shadow-color", [CPColor blackColor], CPThemeStateDisabled],
+ [@"text-shadow-offset", CGSizeMake(-1.0, -1.0)]
+ ];
+
+ if (themeValues)
+ [overrides addObjectsFromArray:themeValues];
+
+ return overrides;
+}
+
+ (CPPopUpButton)themedSegmentedControl
{
- var segmentedControl = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 24.0)];
-
- [segmentedControl setTrackingMode:CPSegmentSwitchTrackingSelectAny];
- [segmentedControl setSegmentCount:3];
-
- [segmentedControl setWidth:40.0 forSegment:0];
- [segmentedControl setLabel:@"foo" forSegment:0];
- [segmentedControl setTag:1 forSegment:0];
+ var segmentedControl = [AristoThemeDescriptor makeSegmentedControl];
- [segmentedControl setWidth:60.0 forSegment:1];
- [segmentedControl setLabel:@"bar" forSegment:1];
- [segmentedControl setTag:2 forSegment:1];
-
- [segmentedControl setWidth:35.0 forSegment:2];
- [segmentedControl setLabel:@"1" forSegment:2];
- [segmentedControl setTag:3 forSegment:2];
-
- //various colors
- var centerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-center.png" size:CGSizeMake(1.0, 24.0)]),
- dividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-divider.png" size:CGSizeMake(1.0, 24.0)]),
- centerHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-center.png" size:CGSizeMake(1.0, 24.0)]),
- dividerHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-divider.png" size:CGSizeMake(1.0, 24.0)]),
- leftHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-left.png" size:CGSizeMake(4.0, 24.0)]),
- rightHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]),
- inactiveCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)]),
- inactiveDividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-disabled-divider.png" size:CGSizeMake(1.0, 24.0)]),
- inactiveLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)]),
- inactiveRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)]),
- inactiveHighlightedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-disabled-center.png" size:CGSizeMake(1.0, 24.0)]),
- inactiveHighlightedDividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-disabled-divider.png" size:CGSizeMake(1.0, 24.0)]),
- inactiveHighlightedLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-disabled-left.png" size:CGSizeMake(4.0, 24.0)]),
- inactiveHighlightedRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-disabled-right.png" size:CGSizeMake(4.0, 24.0)]),
- leftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-left.png" size:CGSizeMake(4.0, 24.0)]),
- rightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-right.png" size:CGSizeMake(4.0, 24.0)]),
- pushedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-center.png" size:CGSizeMake(1.0, 24.0)]),
- pushedLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-left.png" size:CGSizeMake(4.0, 24.0)]),
- pushedRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-right.png" size:CGSizeMake(4.0, 24.0)]);
- pushedHighlightedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-highlighted-center.png" size:CGSizeMake(1.0, 24.0)]),
- pushedHighlightedLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-highlighted-left.png" size:CGSizeMake(4.0, 24.0)]),
- pushedHighlightedRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]);
-
- [segmentedControl setValue:centerBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateNormal];
- [segmentedControl setValue:inactiveCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateDisabled];
- [segmentedControl setValue:inactiveHighlightedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
- [segmentedControl setValue:centerHighlightedBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateSelected];
- [segmentedControl setValue:pushedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateHighlighted];
- [segmentedControl setValue:pushedHighlightedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected];
-
- [segmentedControl setValue:dividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateNormal];
- [segmentedControl setValue:inactiveDividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateDisabled];
- [segmentedControl setValue:inactiveHighlightedDividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
- [segmentedControl setValue:dividerHighlightedBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateSelected];
- [segmentedControl setValue:dividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateHighlighted];
-
- [segmentedControl setValue:rightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateNormal];
- [segmentedControl setValue:inactiveRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateDisabled];
- [segmentedControl setValue:inactiveHighlightedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
- [segmentedControl setValue:rightHighlightedBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateSelected];
- [segmentedControl setValue:pushedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateHighlighted];
- [segmentedControl setValue:pushedHighlightedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected];
-
- [segmentedControl setValue:leftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateNormal];
- [segmentedControl setValue:inactiveLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateDisabled];
- [segmentedControl setValue:inactiveHighlightedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
- [segmentedControl setValue:leftHighlightedBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateSelected];
- [segmentedControl setValue:pushedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateHighlighted];
- [segmentedControl setValue:pushedHighlightedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected];
-
- [segmentedControl setValue:CGInsetMake(0.0, 4.0, 0.0, 4.0) forThemeAttribute:@"content-inset" inState:CPThemeStateNormal];
-
- [segmentedControl setValue:CGInsetMake(0.0, 0.0, 0.0, 0.0) forThemeAttribute:@"bezel-inset" inState:CPThemeStateNormal];
-
- [segmentedControl setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font"];
- [segmentedControl setValue:[CPColor colorWithCalibratedWhite:255.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"];
- [segmentedControl setValue:[CPColor colorWithCalibratedWhite:0.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-shadow-color"];
- [segmentedControl setValue:CGSizeMake(-1.0, -1.0) forThemeAttribute:@"text-shadow-offset"];
- [segmentedControl setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
-
- [segmentedControl setValue:1.0 forThemeAttribute:@"divider-thickness"];
- [segmentedControl setValue:24.0 forThemeAttribute:@"default-height"];
+ [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:segmentedControl inherit:themedSegmentedControlValues];
return segmentedControl;
}
+ (CPButton)button
{
- var button = [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 24.0)],
+ var button = [AristoThemeDescriptor makeButton];
- bezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]],
-
- highlightedBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-highlighted-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-highlighted-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]],
-
- defaultBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]],
-
- defaultHighlightedBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-highlighted-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-highlighted-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]],
-
- defaultDisabledBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]],
-
- disabledBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)]
- ]
- isVertical:NO]];
-
- [button setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBordered];
-
- [button setValue:[CPColor colorWithCalibratedWhite:255.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"];
- [button setValue:[CPColor colorWithCalibratedWhite:0.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered];
- [button setValue:[CPColor colorWithCalibratedWhite:0.0 / 255.0 alpha:0.6] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
- [button setValue:CGSizeMake(-1.0,-1.0) forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateBordered];
- [button setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
-
- [button setValue:bezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
- [button setValue:highlightedBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateHighlighted];
- [button setValue:CGInsetMake(0.0, 5.0, 0.0, 5.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered];
-
- [button setValue:[CPColor colorWithCalibratedWhite:0.97 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled];
- [button setValue:disabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
- [button setValue:defaultDisabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDefault|CPThemeStateDisabled];
-
- [button setValue:defaultBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDefault];
- [button setValue:defaultHighlightedBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateHighlighted|CPThemeStateDefault];
- [button setValue:[CPColor colorWithCalibratedRed:250.0/255.0 green:250.0/255.0 blue:250.0/255.0 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDefault];
- [button setValue:[CPColor colorWithCalibratedRed:250.0/255.0 green:250.0/255.0 blue:250.0/255.0 alpha:0.6] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled|CPThemeStateDefault];
-
- [button setValue:CGSizeMake(0.0, 24.0) forThemeAttribute:@"min-size"];
- [button setValue:CGSizeMake(-1.0, 24.0) forThemeAttribute:@"max-size"];
+ [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:button inherit:themedButtonValues];
return button;
}
@@ -967,216 +1411,77 @@
var button = [self button];
[button setTitle:@"OK"];
- [button setDefaultButton:YES];
+ [button setThemeState:CPThemeStateDefault];
return button;
}
+ (CPScroller)themedVerticalScroller
{
- var scroller = [[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, 15.0, 170.0)],
- trackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-vertical-track.png" size:CGSizeMake(15.0, 1.0)]),
- disabledTrackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-vertical-track-disabled.png" size:CGSizeMake(15.0, 1.0)]);
-
- [scroller setValue:21.0 forThemeAttribute:@"minimum-knob-length" inState:CPThemeStateVertical];
- [scroller setValue:CGInsetMake(0.0, 0.0, 0.0, 0.0) forThemeAttribute:@"knob-inset" inState:CPThemeStateVertical];
- [scroller setValue:CGInsetMake(-9.0, 0.0, -9.0, 0.0) forThemeAttribute:@"track-inset" inState:CPThemeStateVertical];
+ var scroller = [AristoThemeDescriptor makeVerticalScroller],
+ overrides =
+ [
+ [@"knob-color", nil, CPThemeStateVertical | CPThemeStateDisabled]
+ ];
- [scroller setValue:trackColor forThemeAttribute:@"knob-slot-color" inState:CPThemeStateVertical];
- [scroller setValue:disabledTrackColor forThemeAttribute:@"knob-slot-color" inState:CPThemeStateVertical | CPThemeStateDisabled];
-
- var arrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-up-arrow.png" size:CGSizeMake(15.0, 24.0)]),
- highlightedArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-up-arrow-highlighted.png" size:CGSizeMake(15.0, 24.0)]),
- disabledArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-up-arrow-disabled.png" size:CGSizeMake(15.0, 24.0)]);
-
- [scroller setValue:CGSizeMake(15.0, 24.0) forThemeAttribute:@"decrement-line-size" inState:CPThemeStateVertical];
- [scroller setValue:arrowColor forThemeAttribute:@"decrement-line-color" inState:CPThemeStateVertical];
- [scroller setValue:highlightedArrowColor forThemeAttribute:@"decrement-line-color" inState:CPThemeStateVertical | CPThemeStateHighlighted],
- [scroller setValue:disabledArrowColor forThemeAttribute:@"decrement-line-color" inState:CPThemeStateVertical | CPThemeStateDisabled];
-
- var arrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-down-arrow.png" size:CGSizeMake(15.0, 24.0)]),
- highlightedArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-down-arrow-highlighted.png" size:CGSizeMake(15.0, 24.0)]),
- disabledArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-down-arrow-disabled.png" size:CGSizeMake(15.0, 24.0)]);
-
- [scroller setValue:CGSizeMake(15.0, 24.0) forThemeAttribute:@"increment-line-size" inState:CPThemeStateVertical];
- [scroller setValue:arrowColor forThemeAttribute:@"increment-line-color" inState:CPThemeStateVertical];
- [scroller setValue:highlightedArrowColor forThemeAttribute:@"increment-line-color" inState:CPThemeStateVertical | CPThemeStateHighlighted];
- [scroller setValue:disabledArrowColor forThemeAttribute:@"increment-line-color" inState:CPThemeStateVertical | CPThemeStateDisabled];
-
- var knobColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-vertical-knob-top.png" size:CGSizeMake(15.0, 10.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-vertical-knob-center.png" size:CGSizeMake(15.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-vertical-knob-bottom.png" size:CGSizeMake(15.0, 10.0)]
- ]
- isVertical:YES]);
-
- /*var knobDisabledColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-vertical-knob-disabled-top.png" size:CGSizeMake(15.0, 10.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-vertical-knob-disabled-center.png" size:CGSizeMake(15.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-vertical-knob-disabled-bottom.png" size:CGSizeMake(15.0, 10.0)]
- ]
- isVertical:YES]);*/
-
- [scroller setValue:knobColor forThemeAttribute:@"knob-color" inState:CPThemeStateVertical];
- //[scroller setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateVertical|CPThemeStateDisabled];
-
- [scroller setFloatValue:0.1];
- [scroller setKnobProportion:0.5];
+ [self registerThemeValues:[self defaultThemeOverridesAddedTo:overrides] forView:scroller inherit:themedVerticalScrollerValues];
return scroller;
}
+ (CPScroller)themedHorizontalScroller
{
- var scroller = [[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, 170.0, 15.0)],
- trackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-horizontal-track.png" size:CGSizeMake(1.0, 15.0)]),
- disabledTrackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-horizontal-track-disabled.png" size:CGSizeMake(1.0, 15.0)]);
+ var scroller = [AristoThemeDescriptor makeHorizontalScroller],
+ overrides =
+ [
+ [@"knob-color", nil, CPThemeStateDisabled]
+ ];
- [scroller setValue:21.0 forThemeAttribute:@"minimum-knob-length"];
- [scroller setValue:CGInsetMake(0.0, 0.0, 0.0, 0.0) forThemeAttribute:@"knob-inset"];
- [scroller setValue:CGInsetMake(0.0, -7.0, 0.0, -9.0) forThemeAttribute:@"track-inset"];
-
- [scroller setValue:trackColor forThemeAttribute:@"knob-slot-color"];
- [scroller setValue:disabledTrackColor forThemeAttribute:@"knob-slot-color" inState:CPThemeStateDisabled];
-
- var arrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-left-arrow.png" size:CGSizeMake(24.0, 15.0)]),
- highlightedArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-left-arrow-highlighted.png" size:CGSizeMake(24.0, 15.0)]),
- disabledArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-left-arrow-disabled.png" size:CGSizeMake(24.0, 15.0)]);
-
- [scroller setValue:CGSizeMake(24.0, 15.0) forThemeAttribute:@"decrement-line-size"];
- [scroller setValue:arrowColor forThemeAttribute:@"decrement-line-color"];
- [scroller setValue:highlightedArrowColor forThemeAttribute:@"decrement-line-color" inState:CPThemeStateHighlighted],
- [scroller setValue:disabledArrowColor forThemeAttribute:@"decrement-line-color" inState:CPThemeStateDisabled];
-
- var arrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-right-arrow.png" size:CGSizeMake(24.0, 15.0)]),
- highlightedArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-right-arrow-highlighted.png" size:CGSizeMake(24.0, 15.0)]),
- disabledArrowColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/scroller-right-arrow-disabled.png" size:CGSizeMake(24.0, 15.0)]);
-
- [scroller setValue:CGSizeMake(24.0, 15.0) forThemeAttribute:@"increment-line-size"];
- [scroller setValue:arrowColor forThemeAttribute:@"increment-line-color"];
- [scroller setValue:highlightedArrowColor forThemeAttribute:@"increment-line-color" inState:CPThemeStateHighlighted];
- [scroller setValue:disabledArrowColor forThemeAttribute:@"increment-line-color" inState:CPThemeStateDisabled];
-
- var knobColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-horizontal-knob-left.png" size:CGSizeMake(10.0, 15.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-horizontal-knob-center.png" size:CGSizeMake(1.0, 15.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-horizontal-knob-right.png" size:CGSizeMake(10.0, 15.0)]
- ]
- isVertical:NO]);
-
- /*var knobDisabledColor = PatternColor([[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-horizontal-knob-disabled-left.png" size:CGSizeMake(10.0, 15.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-horizontal-knob-disabled-center.png" size:CGSizeMake(1.0, 15.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/scroller-horizontal-knob-disabled-right.png" size:CGSizeMake(10.0, 15.0)]
- ]
- isVertical:NO]);*/
-
- [scroller setValue:knobColor forThemeAttribute:@"knob-color"];
- //[scroller setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled];
-
- [scroller setFloatValue:0.1];
- [scroller setKnobProportion:0.5];
+ [self registerThemeValues:[self defaultThemeOverridesAddedTo:overrides] forView:scroller inherit:themedHorizontalScrollerValues];
return scroller;
}
+ (CPSlider)themedHorizontalSlider
{
- var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 50.0, 24.0)],
- trackColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-left.png" size:CGSizeMake(4.0, 5.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-center.png" size:CGSizeMake(1.0, 5.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-right.png" size:CGSizeMake(4.0, 5.0)]
- ]
- isVertical:NO]],
-
- trackDisabledColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-disabled-left.png" size:CGSizeMake(4.0, 5.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-disabled-center.png" size:CGSizeMake(1.0, 5.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-disabled-right.png" size:CGSizeMake(4.0, 5.0)]
- ]
- isVertical:NO]];
+ var slider = [AristoThemeDescriptor makeHorizontalSlider];
- [slider setValue:5.0 forThemeAttribute:@"track-width"];
- [slider setValue:trackColor forThemeAttribute:@"track-color"];
- [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled];
-
- var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob.png" size:CGSizeMake(23.0, 24.0)]],
- knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]],
- knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob-disabled.png" size:CGSizeMake(23.0, 24.0)]];
+ [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:slider inherit:themedHorizontalSliderValues];
- [slider setValue:CGSizeMake(23.0, 24.0) forThemeAttribute:@"knob-size"];
- [slider setValue:knobColor forThemeAttribute:@"knob-color"];
- [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateHighlighted];
- [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled];
-
return slider;
}
+ (CPSlider)themedVerticalSlider
{
- var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 24.0, 50.0)],
- trackColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-top.png" size:CGSizeMake(5.0, 6.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-center.png" size:CGSizeMake(5.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-bottom.png" size:CGSizeMake(5.0, 4.0)]
- ]
- isVertical:YES]],
- trackDisabledColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
- [
- [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-disabled-top.png" size:CGSizeMake(5.0, 6.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-disabled-center.png" size:CGSizeMake(5.0, 1.0)],
- [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-disabled-bottom.png" size:CGSizeMake(5.0, 4.0)]
- ]
- isVertical:YES]];
-
- [slider setValue:5.0 forThemeAttribute:@"track-width"];
- [slider setValue:trackColor forThemeAttribute:@"track-color" inState:CPThemeStateVertical];
- [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled];
-
- var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob.png" size:CGSizeMake(23.0, 24.0)]],
- knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]],
- knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob-disabled.png" size:CGSizeMake(23.0, 24.0)]];
+ var slider = [AristoThemeDescriptor makeVerticalSlider];
+
+ [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:slider inherit:themedVerticalSliderValues];
- [slider setValue:CGSizeMake(23.0, 24.0) forThemeAttribute:@"knob-size"];
- [slider setValue:knobColor forThemeAttribute:@"knob-color"];
- [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateHighlighted];
- [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled];
-
return slider;
}
+
+ (CPSlider)themedCircularSlider
{
- var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 34.0, 34.0)],
- trackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/slider-circular-bezel.png" size:CGSizeMake(34.0, 34.0)]),
- trackDisabledColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/slider-circular-disabled-bezel.png" size:CGSizeMake(34.0, 34.0)]);
+ var slider = [AristoThemeDescriptor makeCircularSlider];
- [slider setSliderType:CPCircularSlider];
- [slider setValue:trackColor forThemeAttribute:@"track-color" inState:CPThemeStateCircular];
- [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled|CPThemeStateCircular];
-
- var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/slider-circular-knob.png" size:CGSizeMake(5.0, 5.0)]],
- knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/slider-circular-disabled-knob.png" size:CGSizeMake(5.0, 5.0)]],
- knobHighlightedColor = knobColor;
-
- [slider setValue:CGSizeMake(5.0, 5.0) forThemeAttribute:@"knob-size" inState:CPThemeStateCircular];
- [slider setValue:knobColor forThemeAttribute:@"knob-color" inState:CPThemeStateCircular];
- [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled|CPThemeStateCircular];
- [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateCircular|CPThemeStateHighlighted];
+ [self registerThemeValues:[self defaultThemeOverridesAddedTo:nil] forView:slider inherit:themedCircularSliderValues];
return slider;
}
++ (CPAlert)themedAlert
+{
+ var alert = [CPAlert new],
+
+ hudSpecificValues =
+ [
+ [@"message-text-color", [CPColor whiteColor]],
+ [@"informative-text-color", [CPColor whiteColor]],
+ ];
+
+ [self registerThemeValues:hudSpecificValues forView:alert inherit:themedAlertValues];
+
+ return alert;
+}
+
@end
-
-function PatternColor(anImage)
-{
- return [CPColor colorWithPatternImage:anImage];
-}
diff --git a/AppKit/Themes/BlendKit/BKShowcaseController.j b/AppKit/Themes/BlendKit/BKShowcaseController.j
index 36e663b4f..78bf30c78 100644
--- a/AppKit/Themes/BlendKit/BKShowcaseController.j
+++ b/AppKit/Themes/BlendKit/BKShowcaseController.j
@@ -36,14 +36,17 @@ var BKLearnMoreToolbarItemIdentifier = @"BKLearnMoreToolbarItemId
CPCollectionView _themesCollectionView;
CPCollectionView _themedObjectsCollectionView;
+
+ CPWindow theWindow;
}
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
_themeDescriptorClasses = [BKThemeDescriptor allThemeDescriptorClasses];
- var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
- toolbar = [[CPToolbar alloc] initWithIdentifier:@"Toolbar"];
+ theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask];
+
+ var toolbar = [[CPToolbar alloc] initWithIdentifier:@"Toolbar"];
[toolbar setDelegate:self];
[theWindow setToolbar:toolbar];
@@ -118,7 +121,7 @@ var BKLearnMoreToolbarItemIdentifier = @"BKLearnMoreToolbarItemId
[_themesCollectionView setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]];
- [theWindow setFullBridge:YES];
+ [theWindow setFullPlatformWindow:YES];
[theWindow makeKeyAndOrderFront:self];
}
@@ -134,7 +137,7 @@ var BKLearnMoreToolbarItemIdentifier = @"BKLearnMoreToolbarItemId
[_themedObjectsCollectionView setMinItemSize:itemSize];
[_themedObjectsCollectionView setMaxItemSize:itemSize];
- [_themedObjectsCollectionView setContent:[themeDescriptorClass themedObjectTemplates]];
+ [_themedObjectsCollectionView setContent:[themeDescriptorClass themedShowcaseObjectTemplates]];
[BKShowcaseCell setBackgroundColor:[themeDescriptorClass showcaseBackgroundColor]];
}
@@ -180,7 +183,7 @@ var BKLearnMoreToolbarItemIdentifier = @"BKLearnMoreToolbarItemId
[toolbarItem setMinSize:CGSizeMake(width + 20.0, 24.0)];
[toolbarItem setMaxSize:CGSizeMake(width + 20.0, 24.0)];
}
-
+
else if (anItemIdentifier === BKBackgroundColorToolbarItemIdentifier)
{
var popUpButton = [CPPopUpButton buttonWithTitle:@"Window Background"];
@@ -218,7 +221,7 @@ var BKLearnMoreToolbarItemIdentifier = @"BKLearnMoreToolbarItemId
var button = [CPButton buttonWithTitle:title];
- [button setDefaultButton:YES];
+ [theWindow setDefaultButton:button];
[toolbarItem setView:button];
[toolbarItem setLabel:@"Learn More"];
@@ -246,12 +249,12 @@ var BKLearnMoreToolbarItemIdentifier = @"BKLearnMoreToolbarItemId
- (void)changeState:(id)aSender
{
- var themedObjectTemplates = [[self selectedThemeDescriptor] themedObjectTemplates],
- count = [themedObjectTemplates count];
+ var themedShowcaseObjectTemplates = [[self selectedThemeDescriptor] themedShowcaseObjectTemplates],
+ count = [themedShowcaseObjectTemplates count];
while (count--)
{
- var themedObject = [themedObjectTemplates[count] valueForKey:@"themedObject"];
+ var themedObject = [themedShowcaseObjectTemplates[count] valueForKey:@"themedObject"];
if ([themedObject respondsToSelector:@selector(setEnabled:)])
[themedObject setEnabled:[aSender title] === @"Enabled" ? YES : NO];
@@ -320,8 +323,8 @@ var SelectionColor = nil;
[self addSubview:_label];
}
-
- [_label setStringValue:[aThemeDescriptor themeName] + " (" + [[aThemeDescriptor themedObjectTemplates] count] + ")"];
+
+ [_label setStringValue:[aThemeDescriptor themeName] + " (" + [[aThemeDescriptor themedShowcaseObjectTemplates] count] + ")"];
}
- (void)setSelected:(BOOL)isSelected
@@ -383,7 +386,7 @@ var BKShowcaseCellBackgroundColorDidChangeNotification = @"BKShowcaseCellBackgr
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
-
+
if (self)
[[CPNotificationCenter defaultCenter]
addObserver:self
@@ -419,7 +422,7 @@ var BKShowcaseCellBackgroundColorDidChangeNotification = @"BKShowcaseCellBackgr
[_label setStringValue:[anObject valueForKey:@"label"]];
[_label sizeToFit];
- [_label setFrame:CGRectMake(0.0, CGRectGetHeight([self bounds]) - CGRectGetHeight([_label frame]),
+ [_label setFrame:CGRectMake(0.0, CGRectGetHeight([self bounds]) - CGRectGetHeight([_label frame]),
CGRectGetWidth([self bounds]), CGRectGetHeight([_label frame]))];
if (!_backgroundView)
diff --git a/AppKit/Themes/BlendKit/BKThemeDescriptor.j b/AppKit/Themes/BlendKit/BKThemeDescriptor.j
index 4c4801ffb..96fa4cff9 100644
--- a/AppKit/Themes/BlendKit/BKThemeDescriptor.j
+++ b/AppKit/Themes/BlendKit/BKThemeDescriptor.j
@@ -25,6 +25,7 @@
var ItemSizes = { },
ThemedObjects = { },
+ ThemedShowcaseObjects = { },
BackgroundColors = { },
LightCheckersColor = nil,
@@ -120,15 +121,40 @@ var ItemSizes = { },
return ThemedObjects[className];
}
++ (CPArray)themedShowcaseObjectTemplates
+{
+ var className = [self className];
+
+ if (!ThemedShowcaseObjects[className])
+ [self calculateThemedObjectTemplates];
+
+ return ThemedShowcaseObjects[className];
+}
+
+ (void)calculateThemedObjectTemplates
{
var templates = [],
+ showcaseTemplates = [],
itemSize = CGSizeMake(0.0, 0.0),
methods = class_copyMethodList([self class].isa),
index = 0,
- count = [methods count];
+ count = [methods count],
+ excludes = [];
- for (; index < count; ++index)
+ if ([self respondsToSelector:@selector(themeShowcaseExcludes)])
+ excludes = [self themeShowcaseExcludes];
+
+ for (; index < excludes.length; ++index)
+ {
+ var name = excludes[index].toLowerCase();
+
+ if (name && name.indexOf("themed") !== 0)
+ excludes[index] = "themed" + name;
+ else
+ excludes[index] = name;
+ }
+
+ for (index = 0; index < count; ++index)
{
var method = methods[index],
selector = method_getName(method);
@@ -142,26 +168,32 @@ var ItemSizes = { },
if (!object)
continue;
- var template = [[BKThemedObjectTemplate alloc] init];
+ var template = [[BKThemedObjectTemplate alloc] init],
+ excluded = [excludes containsObject:selector.toLowerCase()];
[template setValue:object forKey:@"themedObject"];
[template setValue:BKLabelFromIdentifier(selector) forKey:@"label"];
[templates addObject:template];
- if ([object isKindOfClass:[CPView class]])
+ if (!excluded)
{
- var size = [object frame].size,
- labelWidth = [[template valueForKey:@"label"] sizeWithFont:[CPFont boldSystemFontOfSize:12.0]].width + 20.0;
+ if ([object isKindOfClass:[CPView class]])
+ {
+ var size = [object frame].size,
+ labelWidth = [[template valueForKey:@"label"] sizeWithFont:[CPFont boldSystemFontOfSize:12.0]].width + 20.0;
- if (size.width > itemSize.width)
- itemSize.width = size.width;
+ if (size.width > itemSize.width)
+ itemSize.width = size.width;
- if (labelWidth > itemSize.width)
- itemSize.width = labelWidth;
+ if (labelWidth > itemSize.width)
+ itemSize.width = labelWidth;
- if (size.height > itemSize.height)
- itemSize.height = size.height;
+ if (size.height > itemSize.height)
+ itemSize.height = size.height;
+ }
+
+ [showcaseTemplates addObject:template];
}
}
@@ -169,6 +201,7 @@ var ItemSizes = { },
ItemSizes[className] = itemSize;
ThemedObjects[className] = templates;
+ ThemedShowcaseObjects[className] = showcaseTemplates;
}
+ (int)compare:(BKThemeDescriptor)aThemeDescriptor
@@ -176,11 +209,99 @@ var ItemSizes = { },
return [[self themeName] compare:[aThemeDescriptor themeName]];
}
++ (void)registerThemeValues:(CPArray)themeValues forView:(CPView)aView
+{
+ for (var i = 0; i < themeValues.length; ++i)
+ {
+ var attributeValueState = themeValues[i],
+ attribute = attributeValueState[0],
+ value = attributeValueState[1],
+ state = attributeValueState[2];
+
+ if (state)
+ [aView setValue:value forThemeAttribute:attribute inState:state];
+ else
+ [aView setValue:value forThemeAttribute:attribute];
+ }
+}
+
++ (void)registerThemeValues:(CPArray)themeValues forView:aView inherit:(CPArray)inheritedValues
+{
+ // Register inherited values first, then override those with the subtheme values.
+ if (inheritedValues)
+ {
+ // Check the class name to see if it is a subtheme of another theme. If so,
+ // use the subtheme name as a relative path to image patterns.
+ var themeName = [self themeName],
+ index = themeName.indexOf("-");
+
+ if (index < 0)
+ {
+ // This theme is a subtheme, register the inherited values directly
+ [self registerThemeValues:inheritedValues forView:aView];
+ }
+ else
+ {
+ var themePath = themeName.substr(index + 1) + "/";
+
+ for (var i = 0; i < inheritedValues.length; ++i)
+ {
+ var attributeValueState = inheritedValues[i],
+ attribute = attributeValueState[0],
+ value = attributeValueState[1],
+ state = attributeValueState[2],
+ pattern = nil;
+
+ if (typeof(value) === "object" &&
+ value.hasOwnProperty("isa") &&
+ [value isKindOfClass:CPColor] &&
+ (pattern = [value patternImage]))
+ {
+ if ([pattern isThreePartImage] || [pattern isNinePartImage])
+ {
+ var slices = [pattern imageSlices],
+ newSlices = [];
+
+ for (var sliceIndex = 0; sliceIndex < slices.length; ++sliceIndex)
+ {
+ var slice = slices[sliceIndex],
+ filename = themePath + [[slice filename] lastPathComponent],
+ size = [slice size];
+
+ newSlices.push([filename, size.width, size.height]);
+ }
+
+ if ([pattern isThreePartImage])
+ value = PatternColor(newSlices, [pattern isVertical]);
+ else
+ value = PatternColor(newSlices);
+ }
+ else
+ {
+ var filename = themePath + [[pattern filename] lastPathComponent],
+ size = [pattern size];
+
+ value = PatternColor(filename, size.width, size.height);
+ }
+ }
+
+ if (state)
+ [aView setValue:value forThemeAttribute:attribute inState:state];
+ else
+ [aView setValue:value forThemeAttribute:attribute];
+ }
+ }
+ }
+
+ if (themeValues)
+ [self registerThemeValues:themeValues forView:aView];
+}
+
@end
function BKLabelFromIdentifier(anIdentifier)
{
- var string = anIdentifier.substr("themed".length);
+ var string = anIdentifier.substr("themed".length),
index = 0,
count = string.length,
label = "",
@@ -193,7 +314,7 @@ function BKLabelFromIdentifier(anIdentifier)
isCapital = /^[A-Z]/.test(character);
if (isCapital)
- {
+ {
if (!isLeadingCapital)
{
if (lastCapital === null)
@@ -219,3 +340,59 @@ function BKLabelFromIdentifier(anIdentifier)
return label;
}
+
+
+PatternIsVertical = YES,
+PatternIsHorizontal = NO;
+
+/*
+ To create a simple color with a pattern image:
+ PatternColor(name, width, height)
+
+ To create a color with a three part pattern image:
+ PatternColor(slices, orientation)
+
+ where slices is an array of three [name, width, height] arrays,
+ and orientation is PatternIsVertical or PatternIsHorizontal.
+
+ To create a color with a nine part pattern image:
+ PatternColor(slices);
+
+ where slices is an array of nine [name, width, height] arrays.
+*/
+function PatternColor()
+{
+ if (arguments.length < 3)
+ {
+ var slices = arguments[0],
+ imageSlices = [];
+
+ for (var i = 0; i < slices.length; ++i)
+ {
+ var slice = slices[i];
+
+ imageSlices.push(slice ? [_CPCibCustomResource imageResourceWithName:slice[0] size:CGSizeMake(slice[1], slice[2])] : nil);
+ }
+
+ if (arguments.length == 2)
+ return [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:imageSlices isVertical:arguments[1]]];
+ else
+ return [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:imageSlices]];
+ }
+ else if (arguments.length == 3)
+ {
+ return [CPColor colorWithPatternImage:PatternImage(arguments[0], arguments[1], arguments[2])];
+ }
+ else
+ {
+ return nil;
+ }
+}
+
+/*
+ Like the 3 argument PatternColor, but return an image instead of a color.
+*/
+function PatternImage(name, width, height)
+{
+ return [_CPCibCustomResource imageResourceWithName:name size:CGSizeMake(width, height)];
+}
diff --git a/AppKit/_CPCornerView.j b/AppKit/_CPCornerView.j
index a5e4f9a65..33925e5c2 100644
--- a/AppKit/_CPCornerView.j
+++ b/AppKit/_CPCornerView.j
@@ -1,21 +1,36 @@
-
@import "CPView.j"
@implementation _CPCornerView : CPView
{
}
++ (CPString)themeClass
+{
+ return @"cornerview";
+}
+
++ (id)themeAttributes
+{
+ return [CPDictionary dictionaryWithObjects:[[CPNull null]]
+ forKeys:[@"background-color"]];
+}
+
+- (void)layoutSubviews
+{
+ [self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
+}
+
- (void)_init
{
- [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview.png", CGSizeMake(1.0, 23.0))]];
+ [self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
}
- (id)initWithFrame:(CGRect)aFrame
{
- if (self = [super initWithFrame:aFrame])
- {
+ self = [super initWithFrame:aFrame]
+
+ if (self)
[self _init];
- }
return self;
}
@@ -23,9 +38,9 @@
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
- {
+
+ if (self)
[self _init];
- }
return self;
}
diff --git a/AppKit/_CPImageAndTextView.j b/AppKit/_CPImageAndTextView.j
index 79a65f2ac..5c1d17dba 100644
--- a/AppKit/_CPImageAndTextView.j
+++ b/AppKit/_CPImageAndTextView.j
@@ -45,9 +45,6 @@ var _CPimageAndTextViewFrameSizeChangedFlag = 1 << 0,
_CPImageAndTextViewImagePositionChangedFlag = 1 << 9,
_CPImageAndTextViewImageScalingChangedFlag = 1 << 10;
-var HORIZONTAL_MARGIN = 3.0,
- VERTICAL_MARGIN = 5.0;
-
/* @ignore */
@implementation _CPImageAndTextView : CPView
{
@@ -63,6 +60,7 @@ var HORIZONTAL_MARGIN = 3.0,
CPCellImagePosition _imagePosition;
CPImageScaling _imageScaling;
+ float _imageOffset;
BOOL _shouldDimImage;
CPImage _image;
@@ -97,6 +95,7 @@ var HORIZONTAL_MARGIN = 3.0,
[self setFont:[aControl font]];
[self setImagePosition:[aControl imagePosition]];
[self setImageScaling:[aControl imageScaling]];
+ [self setImageOffset:[aControl imageOffset]];
}
else
{
@@ -185,6 +184,11 @@ var HORIZONTAL_MARGIN = 3.0,
if (_imagePosition == anImagePosition)
return;
+ // If the position was CPNoImage, there is an image now,
+ // so mark the flags accordingly so that the image will load.
+ if (_imagePosition == CPNoImage)
+ _flags |= _CPImageAndTextViewImageChangedFlag;
+
_imagePosition = anImagePosition;
_flags |= _CPImageAndTextViewImagePositionChangedFlag;
@@ -305,6 +309,20 @@ var HORIZONTAL_MARGIN = 3.0,
[self setNeedsLayout];
}
+- (void)setImageOffset:(float)theImageOffset
+{
+ if (_imageOffset === theImageOffset)
+ return;
+
+ _imageOffset = theImageOffset;
+ [self setNeedsLayout];
+}
+
+- (float)imageOffset
+{
+ return _imageOffset;
+}
+
- (void)imageDidLoad:(id)anImage
{
if (anImage === _image)
@@ -599,31 +617,32 @@ var HORIZONTAL_MARGIN = 3.0,
imageStyle.left = FLOOR(centerX - imageWidth / 2.0) + "px";
imageStyle.top = FLOOR(size.height - imageHeight) + "px";
- textRect.size.height = size.height - imageHeight - VERTICAL_MARGIN;
+ textRect.size.height = size.height - imageHeight - _imageOffset;
}
else if (_imagePosition === CPImageAbove)
{
- CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, FLOOR(centerX - imageWidth / 2.0), 0);
+ imageStyle.left = FLOOR(centerX - imageWidth / 2.0) + "px";
+ imageStyle.top = 0 + "px";
- textRect.origin.y += imageHeight + VERTICAL_MARGIN;
- textRect.size.height = size.height - imageHeight - VERTICAL_MARGIN;
+ textRect.origin.y += imageHeight + _imageOffset;
+ textRect.size.height = size.height - imageHeight - _imageOffset;
}
else if (_imagePosition === CPImageLeft)
{
imageStyle.top = FLOOR(centerY - imageHeight / 2.0) + "px";
imageStyle.left = "0px";
- textRect.origin.x = imageWidth + HORIZONTAL_MARGIN;
- textRect.size.width -= imageWidth + HORIZONTAL_MARGIN;
+ textRect.origin.x = imageWidth + _imageOffset;
+ textRect.size.width -= imageWidth + _imageOffset;
}
else if (_imagePosition === CPImageRight)
{
imageStyle.top = FLOOR(centerY - imageHeight / 2.0) + "px";
imageStyle.left = FLOOR(size.width - imageWidth) + "px";
- textRect.size.width -= imageWidth + HORIZONTAL_MARGIN;
+ textRect.size.width -= imageWidth + _imageOffset;
}
- else if (_imagePosition === CPImageOnly)
+ else if (_imagePosition === CPImageOnly || _imagePosition == CPImageOverlaps)
{
imageStyle.top = FLOOR(centerY - imageHeight / 2.0) + "px";
imageStyle.left = FLOOR(centerX - imageWidth / 2.0) + "px";
@@ -701,13 +720,13 @@ var HORIZONTAL_MARGIN = 3.0,
if (_imagePosition === CPImageLeft || _imagePosition === CPImageRight)
{
- size.width += _textSize.width + HORIZONTAL_MARGIN;
+ size.width += _textSize.width + _imageOffset;
size.height = MAX(size.height, _textSize.height);
}
else if (_imagePosition === CPImageAbove || _imagePosition === CPImageBelow)
{
size.width = MAX(size.width, _textSize.width);
- size.height += _textSize.height + VERTICAL_MARGIN;
+ size.height += _textSize.height + _imageOffset;
}
else // if (_imagePosition == CPImageOverlaps)
{
diff --git a/CommonJS/bin/flatten b/CommonJS/bin/flatten
index 1b746853d..f47fb1e19 100755
--- a/CommonJS/bin/flatten
+++ b/CommonJS/bin/flatten
@@ -112,8 +112,18 @@ function main(args)
print("Loading default theme.");
flattener.require("objective-j").objj_eval("("+(function() {
- var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[[CPBundle bundleForClass:[CPApplication class]] pathForResource:[CPApplication defaultThemeName] + ".blend"]];
+
+ var defaultThemeName = [CPApplication defaultThemeName],
+ bundle = nil;
+
+ if (defaultThemeName === @"Aristo")
+ bundle = [CPBundle bundleForClass:[CPApplication class]];
+ else
+ bundle = [CPBundle mainBundle];
+
+ var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[bundle pathForResource:defaultThemeName + @".blend"]];
[blend loadWithDelegate:nil];
+
})+")")();
var applicationJSs = flattener.buildApplicationJS();
diff --git a/CommonJS/bin/press b/CommonJS/bin/press
index b36277bc3..10b6dadf1 100755
--- a/CommonJS/bin/press
+++ b/CommonJS/bin/press
@@ -130,7 +130,12 @@ function press(rootPath, outputPath, options) {
// read in the default theme name, and attempt to get its size
var themeName = outputInfoPlist.valueForKey("CPDefaultTheme") || "Aristo",
+ themePath = nil;
+
+ if (themeName === "Aristo")
themePath = FILE.join(outputPath, options.frameworks, "AppKit", "Resources", themeName+".blend");
+ else
+ themePath = FILE.join(outputPath, "Resources", themeName + ".blend");
if (FILE.exists(themePath))
{
diff --git a/CommonJS/lib/cappuccino/cib-analysis-tools.j b/CommonJS/lib/cappuccino/cib-analysis-tools.j
index cab6a919a..2427987f8 100644
--- a/CommonJS/lib/cappuccino/cib-analysis-tools.j
+++ b/CommonJS/lib/cappuccino/cib-analysis-tools.j
@@ -17,7 +17,7 @@ function findCibClassDependencies(cibPath) {
}
// make sure CPApp is init'd
- [CPApplication sharedApplication]
+ [CPApplication sharedApplication];
try {
var x = [cib pressInstantiate];
@@ -61,7 +61,7 @@ function findCibClassDependencies(cibPath) {
var topLevelObjects = nil;//[anExternalNameTable objectForKey:CPCibTopLevelObjects];
- [objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects]
+ [objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects];
// [objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects];
// [objectData awakeWithOwner:owner topLevelObjects:topLevelObjects];
diff --git a/Foundation/CPArray+KVO.j b/Foundation/CPArray+KVO.j
index 49ff5d621..2d51447a6 100644
--- a/Foundation/CPArray+KVO.j
+++ b/Foundation/CPArray+KVO.j
@@ -120,7 +120,7 @@
_insertManySEL = sel_getName(@"insertObjects:in"+capitalizedKey+"AtIndexes:");
if ([_proxyObject respondsToSelector:_insertManySEL])
- _insert = [_proxyObject methodForSelector:_insertManySEL];
+ _insertMany = [_proxyObject methodForSelector:_insertManySEL];
_removeManySEL = sel_getName(@"removeObjectsFrom"+capitalizedKey+"AtIndexes:");
if ([_proxyObject respondsToSelector:_removeManySEL])
@@ -234,13 +234,7 @@
- (void)addObject:(id)anObject
{
- if (_insert)
- return _insert(_proxyObject, _insertSEL, anObject, [self count]);
-
- var target = [[self _representedObject] copy];
-
- [target addObject:anObject];
- [self _setRepresentedObject:target];
+ [self insertObject:anObject atIndex:[self count]];
}
- (void)addObjectsFromArray:(CPArray)anArray
@@ -248,19 +242,38 @@
var index = 0,
count = [anArray count];
- for (; index < count; ++index)
- [self addObject:[anArray objectAtIndex:index]];
+ [self insertObjects:anArray atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange([self count], count)]];
}
- (void)insertObject:(id)anObject atIndex:(unsigned)anIndex
{
- if (_insert)
- return _insert(_proxyObject, _insertSEL, anObject, anIndex);
+ [self insertObjects:[anObject] atIndexes:[CPIndexSet indexSetWithIndex:anIndex]];
+}
- var target = [[self _representedObject] copy];
+- (void)insertObjects:(CPArray)theObjects atIndexes:(CPIndexSet)theIndexes
+{
+ if (_insertMany)
+ _insertMany(_proxyObject, _insertManySEL, theObjects, theIndexes);
+ else if (_insert)
+ {
+ var indexesArray = [];
+ [theIndexes getIndexes:indexesArray maxCount:-1 inIndexRange:nil];
- [target insertObject:anObject atIndex:anIndex];
- [self _setRepresentedObject:target];
+ for (var index = 0; index < [indexesArray count]; index++)
+ {
+ var objectIndex = [indexesArray objectAtIndex:index],
+ object = [theObjects objectAtIndex:index];
+
+ _insert(_proxyObject, _insertSEL, object, objectIndex);
+ }
+ }
+ else
+ {
+ var target = [[self _representedObject] copy];
+
+ [target insertObjects:theObjects atIndexes:theIndexes];
+ [self _setRepresentedObject:target];
+ }
}
- (void)removeObject:(id)anObject
diff --git a/Foundation/CPArray.j b/Foundation/CPArray.j
index a35e34026..7815f32da 100755
--- a/Foundation/CPArray.j
+++ b/Foundation/CPArray.j
@@ -26,6 +26,9 @@
@import "CPRange.j"
@import "CPSortDescriptor.j"
+CPEnumerationNormal = 0;
+CPEnumerationConcurrent = 1 << 0;
+CPEnumerationReverse = 1 << 1;
/* @ignore */
@implementation _CPArrayEnumerator : CPEnumerator
@@ -402,6 +405,98 @@
return CPNotFound;
}
+/*!
+ Returns the index of the first object in the receiver that passes a test in a given Javascript function.
+ @param predicate The function to apply to elements of the array. The function receives two arguments:
+ object The element in the array.
+ index The index of the element in the array.
+ The predicate function should either return a Boolean value that indicates whether the object passed the test,
+ or nil to stop the search, which will return CPNotFound to the sender.
+ @return The index of the first matching object, or \c CPNotFound if there is no matching object.
+*/
+- (unsigned)indexOfObjectPassingTest:(Function)predicate
+{
+ return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:predicate context:undefined];
+}
+
+/*!
+ Returns the index of the first object in the receiver that passes a test in a given Javascript function.
+ @param predicate The function to apply to elements of the array. The function receives two arguments:
+ object The element in the array.
+ index The index of the element in the array.
+ context The object passed to the receiver in the aContext parameter.
+ The predicate function should either return a Boolean value that indicates whether the object passed the test,
+ or nil to stop the search, which will return CPNotFound to the sender.
+ @param context An object that contains context information you want passed to the predicate function.
+ @return The index of the first matching object, or \c CPNotFound if there is no matching object.
+*/
+- (unsigned)indexOfObjectPassingTest:(Function)predicate context:(id)aContext
+{
+ return [self indexOfObjectWithOptions:CPEnumerationNormal passingTest:predicate context:aContext];
+}
+
+/*!
+ Returns the index of the first object in the receiver that passes a test in a given Javascript function.
+ @param opts Specifies the direction in which the array is searched. Pass CPEnumerationNormal to search forwards
+ or CPEnumerationReverse to search in reverse.
+ @param predicate The function to apply to elements of the array. The function receives two arguments:
+ object The element in the array.
+ index The index of the element in the array.
+ The predicate function should either return a Boolean value that indicates whether the object passed the test,
+ or nil to stop the search, which will return CPNotFound to the sender.
+ @return The index of the first matching object, or \c CPNotFound if there is no matching object.
+*/
+- (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)opts passingTest:(Function)predicate
+{
+ return [self indexOfObjectWithOptions:opts passingTest:predicate context:undefined];
+}
+
+/*!
+ Returns the index of the first object in the receiver that passes a test in a given Javascript function.
+ @param opts Specifies the direction in which the array is searched. Pass CPEnumerationNormal to search forwards
+ or CPEnumerationReverse to search in reverse.
+ @param predicate The function to apply to elements of the array. The function receives two arguments:
+ object The element in the array.
+ index The index of the element in the array.
+ context The object passed to the receiver in the aContext parameter.
+ The predicate function should either return a Boolean value that indicates whether the object passed the test,
+ or nil to stop the search, which will return CPNotFound to the sender.
+ @param context An object that contains context information you want passed to the predicate function.
+ @return The index of the first matching object, or \c CPNotFound if there is no matching object.
+*/
+- (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)opts passingTest:(Function)predicate context:(id)aContext
+{
+ // We don't use an enumerator because they return nil to indicate end of enumeration,
+ // but nil may actually be the value we are looking for, so we have to loop over the array.
+
+ var start, stop, increment;
+
+ if (opts & CPEnumerationReverse)
+ {
+ start = [self count] - 1;
+ stop = -1;
+ increment = -1;
+ }
+ else
+ {
+ start = 0;
+ stop = [self count];
+ increment = 1;
+ }
+
+ for (var i = start; i != stop; i += increment)
+ {
+ var result = predicate([self objectAtIndex:i], i, aContext);
+
+ if (typeof result === 'boolean' && result)
+ return i;
+ else if (typeof result === 'object' && result == nil)
+ return CPNotFound;
+ }
+
+ return CPNotFound;
+}
+
/*!
Returns the index of \c anObject in the array, which must be sorted in the same order as
calling sortUsingSelector: with the selector passed to this method would result in.
@@ -512,6 +607,12 @@
- (unsigned)insertObject:(id)anObject inArraySortedByDescriptors:(CPArray)descriptors
{
+ if (!descriptors || ![descriptors count])
+ {
+ [self addObject:anObject];
+ return [self count] - 1;
+ }
+
var index = [self _insertObject:anObject sortedByFunction:function(lhs, rhs)
{
var i = 0,
@@ -801,7 +902,7 @@
*/
- (CPArray)sortedArrayUsingSelector:(SEL)aSelector
{
- var sorted = [self copy]
+ var sorted = [self copy];
[sorted sortUsingSelector:aSelector];
@@ -1220,19 +1321,7 @@
- (CPArray)sortUsingDescriptors:(CPArray)descriptors
{
- var count = [descriptors count];
-
- sort(function(lhs, rhs)
- {
- var i = 0,
- result = CPOrderedSame;
-
- while (i < count)
- if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame)
- return result;
-
- return result;
- });
+ [self sortUsingFunction:compareObjectsUsingDescriptors context:descriptors];
}
/*!
@@ -1242,7 +1331,37 @@
*/
- (void)sortUsingFunction:(Function)aFunction context:(id)aContext
{
- sort(function(lhs, rhs) { return aFunction(lhs, rhs, aContext); });
+ var h, i, j, k, l, m, n = [self count], o;
+ var A, B = [];
+
+ for (h = 1; h < n; h += h)
+ {
+ for (m = n - 1 - h; m >= 0; m -= h + h)
+ {
+ l = m - h + 1;
+ if (l < 0)
+ l = 0;
+
+ for (i = 0, j = l; j <= m; i++, j++)
+ B[i] = self[j];
+
+ for (i = 0, k = l; k < j && j <= m + h; k++)
+ {
+ A = self[j];
+ o = aFunction(A, B[i], aContext);
+ if (o == CPOrderedDescending || o == CPOrderedSame)
+ self[k] = B[i++];
+ else
+ {
+ self[k] = A;
+ j++;
+ }
+ }
+
+ while (k < j)
+ self[k++] = B[i++];
+ }
+ }
}
/*!
@@ -1251,7 +1370,7 @@
*/
- (void)sortUsingSelector:(SEL)aSelector
{
- sort(function(lhs, rhs) { return objj_msgSend(lhs, aSelector, rhs); });
+ [self sortUsingFunction:selectorCompare context:aSelector];
}
@end
@@ -1281,6 +1400,24 @@
@end
+var selectorCompare = function selectorCompare(object1, object2, selector)
+{
+ return [object1 performSelector:selector withObject:object2];
+}
+
+// sort using sort descriptors
+var compareObjectsUsingDescriptors= function compareObjectsUsingDescriptors(lhs, rhs, descriptors)
+{
+ var result = CPOrderedSame,
+ i = 0,
+ n = [descriptors count];
+
+ while (i < n && result === CPOrderedSame)
+ result = [descriptors[i++] compareObject:lhs withObject:rhs];
+
+ return result;
+}
+
@implementation CPArray (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
diff --git a/Foundation/CPAttributedString.j b/Foundation/CPAttributedString.j
index f61b5c0e1..85bb3588f 100644
--- a/Foundation/CPAttributedString.j
+++ b/Foundation/CPAttributedString.j
@@ -505,8 +505,6 @@
*/
- (void)replaceCharactersInRange:(CPRange)aRange withString:(CPString)aString
{
- [self beginEditing];
-
if (!aString)
aString = "";
@@ -534,8 +532,6 @@
while(endingIndex < _rangeEntries.length)
_rangeEntries[endingIndex++].range.location+=additionalLength;
-
- [self endEditing];
}
/*!
@@ -561,8 +557,6 @@
*/
- (void)setAttributes:(CPDictionary)aDictionary range:(CPRange)aRange
{
- [self beginEditing];
-
var startingEntryIndex = [self _indexOfRangeEntryForIndex:aRange.location splitOnMaxIndex:YES],
endingEntryIndex = [self _indexOfRangeEntryForIndex:CPMaxRange(aRange) splitOnMaxIndex:YES],
current = startingEntryIndex;
@@ -575,8 +569,6 @@
//necessary?
[self _coalesceRangeEntriesFromIndex:startingEntryIndex toIndex:endingEntryIndex];
-
- [self endEditing];
}
/*!
@@ -591,8 +583,6 @@
*/
- (void)addAttributes:(CPDictionary)aDictionary range:(CPRange)aRange
{
- [self beginEditing];
-
var startingEntryIndex = [self _indexOfRangeEntryForIndex:aRange.location splitOnMaxIndex:YES],
endingEntryIndex = [self _indexOfRangeEntryForIndex:CPMaxRange(aRange) splitOnMaxIndex:YES],
current = startingEntryIndex;
@@ -613,8 +603,6 @@
//necessary?
[self _coalesceRangeEntriesFromIndex:startingEntryIndex toIndex:endingEntryIndex];
-
- [self endEditing];
}
/*!
@@ -642,8 +630,6 @@
*/
- (void)removeAttribute:(CPString)anAttribute range:(CPRange)aRange
{
- [self beginEditing];
-
var startingEntryIndex = [self _indexOfRangeEntryForIndex:aRange.location splitOnMaxIndex:YES],
endingEntryIndex = [self _indexOfRangeEntryForIndex:CPMaxRange(aRange) splitOnMaxIndex:YES],
current = startingEntryIndex;
@@ -656,8 +642,6 @@
//necessary?
[self _coalesceRangeEntriesFromIndex:startingEntryIndex toIndex:endingEntryIndex];
-
- [self endEditing];
}
//Changing Characters and Attributes
@@ -682,8 +666,6 @@
*/
- (void)insertAttributedString:(CPAttributedString)aString atIndex:(unsigned)anIndex
{
- [self beginEditing];
-
if (anIndex < 0 || anIndex > [self length])
[CPException raise:CPRangeException reason:"tried to insert attributed string at an invalid index: "+anIndex];
@@ -713,8 +695,6 @@
//necessary?
//[self _coalesceRangeEntriesFromIndex:startingEntryIndex toIndex:startingEntryIndex+rangeEntries.length];
-
- [self endEditing];
}
/*!
@@ -727,12 +707,8 @@
*/
- (void)replaceCharactersInRange:(CPRange)aRange withAttributedString:(CPAttributedString)aString
{
- [self beginEditing];
-
[self deleteCharactersInRange:aRange];
[self insertAttributedString:aString atIndex:aRange.location];
-
- [self endEditing];
}
/*!
@@ -742,8 +718,6 @@
*/
- (void)setAttributedString:(CPAttributedString)aString
{
- [self beginEditing];
-
_string = aString._string;
_rangeEntries = [];
@@ -752,8 +726,6 @@
for (; i < count; i++)
_rangeEntries.push(copyRangeEntry(aString._rangeEntries[i]));
-
- [self endEditing];
}
//Private methods
diff --git a/Foundation/CPBundle.j b/Foundation/CPBundle.j
index 39aae0b29..89c082c4b 100644
--- a/Foundation/CPBundle.j
+++ b/Foundation/CPBundle.j
@@ -118,6 +118,16 @@ var CPBundlesForURLStrings = { };
return className ? CPClassFromString(className) : Nil;
}
+- (CPString)bundleIdentifier
+{
+ return [self objectForInfoDictionaryKey:@"CPBundleIdentifier"];
+}
+
+- (BOOL)isLoaded
+{
+ return _bundle.isLoaded();
+}
+
- (CPString)pathForResource:(CPString)aFilename
{
return _bundle.pathForResource(aFilename);
@@ -133,8 +143,6 @@ var CPBundlesForURLStrings = { };
return _bundle.valueForInfoDictionaryKey(aKey);
}
-//
-
- (void)loadWithDelegate:(id)aDelegate
{
_delegate = aDelegate;
diff --git a/Foundation/CPCharacterSet.j b/Foundation/CPCharacterSet.j
new file mode 100644
index 000000000..48aa76ea4
--- /dev/null
+++ b/Foundation/CPCharacterSet.j
@@ -0,0 +1,2963 @@
+// CPCharacterSet.j
+// © Emanuele Vulcano, 2008.
+//
+// Licensed under the terms of Cappuccino's license
+// (the GNU Lesser General Public License, version 2.1).
+// Please see Cappuccino's LICENSE file for details.
+
+@import
+
+// CPCharacterSet is a class cluster. Concrete implementations
+// follow after the main abstract class.
+
+var _builtInCharacterSets = {};
+
+@implementation CPCharacterSet : CPObject
+{
+ BOOL _inverted;
+}
+
+// Missing methods
+/*
+- (BOOL)isSupersetOfSet:(CPCharacterSet)theOtherSet{}
++ (id)characterSetWithBitmapRepresentation:(CPData)data{}
++ (id)characterSetWithContentsOfFile:(CPString)path{}
+- (CPData)bitmapRepresentation{}
+
+- (void)formIntersectionWithCharacterSet:(CPCharacterSet)otherSet
+- (void)formUnionWithCharacterSet:(CPCharacterSet)otherSet
+- (void)removeCharactersInRange:(CPRange)aRange
+- (void)removeCharactersInString:(CPString)aString
+*/
+
+- (id)init
+{
+ self = [super init];
+ _inverted = NO;
+
+ return self;
+}
+
+- (void)invert
+{
+ _inverted = !_inverted;
+}
+
+- (BOOL)characterIsMember:(CPString)aCharacter
+{
+ // IMPLEMENTED BY SUBCLASSES
+}
+
+- (BOOL)hasMemberInPlane:(int)aPlane
+{
+ // IMPLEMENTED BY SUBCLASSES
+}
+
++ (id)characterSetWithCharactersInString:(CPString)aString
+{
+ return [[_CPStringContentCharacterSet alloc] initWithString:aString];
+}
+
++ (id)characterSetWithRange:(CPRange)aRange
+{
+ return [[_CPRangeCharacterSet alloc] initWithRange:aRange];
+}
+
++ (id)alphanumericCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)controlCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)decimalDigitCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)decomposableCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)illegalCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)letterCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)lowercaseLetterCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)nonBaseCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)punctuationCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)uppercaseLetterCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)whitespaceAndNewlineCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
++ (id)whitespaceCharacterSet
+{
+ return [CPCharacterSet _sharedCharacterSetWithName:_cmd];
+}
+
+// private methods
++ (id)_sharedCharacterSetWithName:(id)csname
+{
+ var cs = _builtInCharacterSets[csname];
+ if(cs == nil)
+ {
+ var i,
+ ranges = [CPArray array],
+ rangeArray = eval(csname);
+
+ for(i = 0; i < rangeArray.length; i+= 2)
+ {
+ var loc = rangeArray[i];
+ var length = rangeArray[i+1];
+ var range = CPMakeRange(loc,length);
+ [ranges addObject:range];
+ }
+ cs = [[_CPRangeCharacterSet alloc] initWithRanges:ranges];
+ _builtInCharacterSets[csname] = cs;
+ }
+
+ return cs;
+}
+
+- (void)_setInverted:flag
+{
+ _inverted = flag;
+}
+
+@end
+
+// A character set that stores a list of ranges of
+// acceptable characters.
+@implementation _CPRangeCharacterSet : CPCharacterSet
+{
+ CPArray _ranges;
+}
+
+// Creates a range character set with a single range.
+- (id)initWithRange:(CPRange)r
+{
+ return [self initWithRanges:[CPArray arrayWithObject:r]];
+}
+
+// Creates a range character set with multiple ranges.
+- (id)initWithRanges:(CPArray)ranges
+{
+ if (self = [super init])
+ {
+ _ranges = ranges;
+ }
+
+ return self;
+}
+
+- (id)copy
+{
+ var set = [[_CPRangeCharacterSet alloc] initWithRanges:_ranges];
+ [set _setInverted:_inverted];
+ return set;
+}
+
+- (id)invertedSet
+{
+ var set = [[_CPRangeCharacterSet alloc] initWithRanges:_ranges];
+ [set invert];
+ return set;
+}
+
+- (BOOL)characterIsMember:(CPString)aCharacter
+{
+ c = aCharacter.charCodeAt(0);
+ var enu = [_ranges objectEnumerator];
+ var range;
+
+ while (range = [enu nextObject])
+ {
+ if (CPLocationInRange(c, range))
+ return !_inverted;
+ }
+
+ return _inverted;
+}
+
+- (BOOL)hasMemberInPlane:(int)plane // TO DO : when inverted
+{
+ // the highest Unicode plane we reach.
+ // (There are 65536 code points in each plane.)
+ var maxPlane = Math.floor((range.start + range.length - 1) / 65536); // should iterate _ranges
+
+ return (plane <= maxPlane);
+}
+
+- (void)addCharactersInRange:(CPRange)aRange // Needs _inverted support
+{
+ [_ranges addObject:aRange];
+}
+
+- (void)addCharactersInString:(CPString)aString // Needs _inverted support
+{
+ var i;
+
+ for(i = 0; i < aString.length; i++)
+ {
+ var code = aString.charCodeAt(i);
+ var range = CPMakeRange(code,1);
+
+ [_ranges addObject:range];
+ }
+}
+
+@end
+
+// A character set that scans a string's contents for
+// acceptable characters.
+@implementation _CPStringContentCharacterSet : CPCharacterSet
+{
+ CPString _string;
+}
+
+- (id)initWithString:(CPString)s
+{
+ if (self = [super init])
+ {
+ _string = s;
+ }
+
+ return self;
+}
+
+- (id)copy
+{
+ var set = [[_CPStringContentCharacterSet alloc] initWithString:_string];
+ [set _setInverted:_inverted];
+
+ return set;
+}
+
+-(id)invertedSet
+{
+ var set = [[_CPStringContentCharacterSet alloc] initWithString:_string];
+ [set invert];
+
+ return set;
+}
+
+- (BOOL)characterIsMember:(CPString)c
+{
+ return (_string.indexOf(c.charAt(0)) != -1) == !_inverted;
+}
+
+- (CPString)description
+{
+ return [super description] + " { string = '" + _string + "'}";
+}
+
+- (BOOL)hasMemberInPlane:(int)plane
+{
+ // JavaScript strings can only return char codes
+ // up to 0xFFFF (per the ECMA standard), so
+ // they all live in the Basic Multilingual Plane
+ // (aka plane 0).
+ // TODO if the above is wrong, this must be changed!
+
+ return plane == 0;
+}
+
+- (void)addCharactersInRange:(CPRange)aRange // Needs _inverted support
+{
+ var i;
+ for(i = aRange.location; i < aRange.location + aRange.length; i++)
+ {
+ var s = String.fromCharCode(i);
+
+ if (![self characterIsMember:s])
+ _string = [_string stringByAppendingString:s];
+ }
+}
+
+- (void)addCharactersInString:(CPString)aString // Needs _inverted support
+{
+ var i;
+
+ for(i = 0; i < aString.length; i++)
+ {
+ var s = aString.charAt(i);
+
+ if (![self characterIsMember:s])
+ _string = [_string stringByAppendingString:s];
+ }
+}
+
+@end
+
+_CPCharacterSetTrimAtBeginning = 1 << 1;
+_CPCharacterSetTrimAtEnd = 1 << 2;
+
+@implementation CPString (CPCharacterSetAdditions)
+
+/*!
+ Tokenizes the receiver string using the charactes
+ in a given set. For example, if the receiver is:
+ \c "Baku baku to jest skład."
+ and the set is [CPCharacterSet whitespaceCharacterSet]
+ the returned array would contain:
+ ["Baku", "baku", "to", "jest", "", "skład."]
+ Adjacent occurences of the separator characters produce empty strings in the result.
+ @author Arkadiusz Młynarczyk
+ @param A character set containing the characters to use to split the receiver. Must not be nil.
+ @return An CPArray object containing substrings from the receiver that have been divided by characters in separator.
+*/
+- (CPArray)componentsSeparatedByCharactersInSet:(CPCharacterSet)separator
+{
+ if (!separator)
+ [CPException raise:CPInvalidArgumentException
+ reason:"componentsSeparatedByCharactersInSet: the separator can't be 'nil'"];
+
+ var components = [CPMutableArray array],
+ componentRange = CPMakeRange(0, 0);
+
+ for (var i=0; i < self.length; i++)
+ {
+ if ([separator characterIsMember:self.charAt(i)])
+ {
+ componentRange.length = i - componentRange.location;
+ [components addObject:[self substringWithRange:componentRange]];
+ componentRange.location += componentRange.length + 1;
+ }
+ }
+
+ componentRange.length = self.length - componentRange.location;
+ [components addObject:[self substringWithRange:componentRange]];
+
+ return components;
+}
+
+// As per the Cocoa method.
+- (id)stringByTrimmingCharactersInSet:(CPCharacterSet)set
+{
+ return [self _stringByTrimmingCharactersInSet:set options:_CPCharacterSetTrimAtBeginning | _CPCharacterSetTrimAtEnd];
+}
+
+// private method evilness!
+// CPScanner's scanUpToString:... methods rely on this
+// method being present.
+- (id)_stringByTrimmingCharactersInSet:(CPCharacterSet)set options:(int)options
+{
+ var str = self;
+
+ if (options & _CPCharacterSetTrimAtBeginning)
+ {
+ var cutEdgeBeginning = 0;
+
+ while (cutEdgeBeginning < self.length && [set characterIsMember:self.charAt(cutEdgeBeginning)])
+ cutEdgeBeginning++;
+
+ str = str.substr(cutEdgeBeginning);
+ }
+
+ if (options & _CPCharacterSetTrimAtEnd)
+ {
+ var cutEdgeEnd = str.length;
+
+ while (cutEdgeEnd > 0 && [set characterIsMember:self.charAt(cutEdgeEnd)])
+ cutEdgeEnd--;
+
+ str = str.substr(0, cutEdgeEnd + 1);
+ }
+
+ return str;
+}
+
+@end
+
+alphanumericCharacterSet = [
+48,10,
+65,26,
+97,26,
+170,1,
+178,2,
+181,1,
+185,2,
+188,3,
+192,23,
+216,31,
+248,458,
+710,12,
+736,5,
+750,1,
+768,112,
+890,4,
+902,1,
+904,3,
+908,1,
+910,20,
+931,44,
+976,38,
+1015,139,
+1155,4,
+1160,140,
+1329,38,
+1369,1,
+1377,39,
+1425,45,
+1471,1,
+1473,2,
+1476,2,
+1479,1,
+1488,27,
+1520,3,
+1552,6,
+1569,26,
+1600,31,
+1632,10,
+1646,102,
+1749,8,
+1758,11,
+1770,19,
+1791,1,
+1808,59,
+1869,33,
+1920,50,
+1984,54,
+2042,1,
+2305,57,
+2364,18,
+2384,5,
+2392,12,
+2406,10,
+2427,5,
+2433,3,
+2437,8,
+2447,2,
+2451,22,
+2474,7,
+2482,1,
+2486,4,
+2492,9,
+2503,2,
+2507,4,
+2519,1,
+2524,2,
+2527,5,
+2534,12,
+2548,6,
+2561,3,
+2565,6,
+2575,2,
+2579,22,
+2602,7,
+2610,2,
+2613,2,
+2616,2,
+2620,1,
+2622,5,
+2631,2,
+2635,3,
+2649,4,
+2654,1,
+2662,15,
+2689,3,
+2693,9,
+2703,3,
+2707,22,
+2730,7,
+2738,2,
+2741,5,
+2748,10,
+2759,3,
+2763,3,
+2768,1,
+2784,4,
+2790,10,
+2817,3,
+2821,8,
+2831,2,
+2835,22,
+2858,7,
+2866,2,
+2869,5,
+2876,8,
+2887,2,
+2891,3,
+2902,2,
+2908,2,
+2911,3,
+2918,10,
+2929,1,
+2946,2,
+2949,6,
+2958,3,
+2962,4,
+2969,2,
+2972,1,
+2974,2,
+2979,2,
+2984,3,
+2990,12,
+3006,5,
+3014,3,
+3018,4,
+3031,1,
+3046,13,
+3073,3,
+3077,8,
+3086,3,
+3090,23,
+3114,10,
+3125,5,
+3134,7,
+3142,3,
+3146,4,
+3157,2,
+3168,2,
+3174,10,
+3202,2,
+3205,8,
+3214,3,
+3218,23,
+3242,10,
+3253,5,
+3260,9,
+3270,3,
+3274,4,
+3285,2,
+3294,1,
+3296,4,
+3302,10,
+3330,2,
+3333,8,
+3342,3,
+3346,23,
+3370,16,
+3390,6,
+3398,3,
+3402,4,
+3415,1,
+3424,2,
+3430,10,
+3458,2,
+3461,18,
+3482,24,
+3507,9,
+3517,1,
+3520,7,
+3530,1,
+3535,6,
+3542,1,
+3544,8,
+3570,2,
+3585,58,
+3648,15,
+3664,10,
+3713,2,
+3716,1,
+3719,2,
+3722,1,
+3725,1,
+3732,4,
+3737,7,
+3745,3,
+3749,1,
+3751,1,
+3754,2,
+3757,13,
+3771,3,
+3776,5,
+3782,1,
+3784,6,
+3792,10,
+3804,2,
+3840,1,
+3864,2,
+3872,20,
+3893,1,
+3895,1,
+3897,1,
+3902,10,
+3913,34,
+3953,20,
+3974,6,
+3984,8,
+3993,36,
+4038,1,
+4096,34,
+4131,5,
+4137,2,
+4140,7,
+4150,4,
+4160,10,
+4176,10,
+4256,38,
+4304,43,
+4348,1,
+4352,90,
+4447,68,
+4520,82,
+4608,73,
+4682,4,
+4688,7,
+4696,1,
+4698,4,
+4704,41,
+4746,4,
+4752,33,
+4786,4,
+4792,7,
+4800,1,
+4802,4,
+4808,15,
+4824,57,
+4882,4,
+4888,67,
+4959,1,
+4969,20,
+4992,16,
+5024,85,
+5121,620,
+5743,8,
+5761,26,
+5792,75,
+5870,3,
+5888,13,
+5902,7,
+5920,21,
+5952,20,
+5984,13,
+5998,3,
+6002,2,
+6016,52,
+6070,30,
+6103,1,
+6108,2,
+6112,10,
+6128,10,
+6155,3,
+6160,10,
+6176,88,
+6272,42,
+6400,29,
+6432,12,
+6448,12,
+6470,40,
+6512,5,
+6528,42,
+6576,26,
+6608,10,
+6656,28,
+6912,76,
+6992,10,
+7019,9,
+7424,203,
+7678,158,
+7840,90,
+7936,22,
+7960,6,
+7968,38,
+8008,6,
+8016,8,
+8025,1,
+8027,1,
+8029,1,
+8031,31,
+8064,53,
+8118,7,
+8126,1,
+8130,3,
+8134,7,
+8144,4,
+8150,6,
+8160,13,
+8178,3,
+8182,7,
+8304,2,
+8308,6,
+8319,11,
+8336,5,
+8400,32,
+8450,1,
+8455,1,
+8458,10,
+8469,1,
+8473,5,
+8484,1,
+8486,1,
+8488,1,
+8490,4,
+8495,11,
+8508,4,
+8517,5,
+8526,1,
+8531,50,
+9312,60,
+9450,22,
+10102,30,
+11264,47,
+11312,47,
+11360,13,
+11380,4,
+11392,101,
+11517,1,
+11520,38,
+11568,54,
+11631,1,
+11648,23,
+11680,7,
+11688,7,
+11696,7,
+11704,7,
+11712,7,
+11720,7,
+11728,7,
+11736,7,
+12293,3,
+12321,15,
+12337,5,
+12344,5,
+12353,86,
+12441,2,
+12445,3,
+12449,90,
+12540,4,
+12549,40,
+12593,94,
+12690,4,
+12704,24,
+12784,16,
+12832,10,
+12881,15,
+12928,10,
+12977,15,
+13312,6582,
+19968,20924,
+40960,1165,
+42775,4,
+43008,40,
+43072,52,
+44032,11172,
+63744,302,
+64048,59,
+64112,106,
+64256,7,
+64275,5,
+64285,12,
+64298,13,
+64312,5,
+64318,1,
+64320,2,
+64323,2,
+64326,108,
+64467,363,
+64848,64,
+64914,54,
+65008,12,
+65024,16,
+65056,4,
+65136,5,
+65142,135,
+65296,10,
+65313,26,
+65345,26,
+65382,89,
+65474,6,
+65482,6,
+65490,6
+];
+
+controlCharacterSet = [
+0,32,
+127,33,
+173,1,
+1536,4,
+1757,1,
+1807,1,
+6068,2,
+8203,5,
+8234,5,
+8288,4,
+8298,6,
+65279,1
+];
+
+decimalDigitCharacterSet = [
+48,10,
+1632,10,
+1776,10,
+1984,10,
+2406,10,
+2534,10,
+2662,10,
+2790,10,
+2918,10,
+3046,10,
+3174,10,
+3302,10,
+3430,10,
+3664,10,
+3792,10,
+3872,10,
+4160,10,
+6112,10,
+6160,10,
+6470,10,
+6608,10,
+6992,10
+];
+
+decomposableCharacterSet = [
+192,6,
+199,9,
+209,6,
+217,5,
+224,6,
+231,9,
+241,6,
+249,5,
+255,17,
+274,20,
+296,9,
+308,4,
+313,6,
+323,6,
+332,6,
+340,18,
+360,23,
+416,2,
+431,2,
+461,16,
+478,6,
+486,11,
+500,2,
+504,36,
+542,2,
+550,14,
+832,2,
+835,2,
+884,1,
+894,1,
+901,6,
+908,1,
+910,3,
+938,7,
+970,5,
+979,2,
+1024,2,
+1027,1,
+1031,1,
+1036,3,
+1049,1,
+1081,1,
+1104,2,
+1107,1,
+1111,1,
+1116,3,
+1142,2,
+1217,2,
+1232,4,
+1238,2,
+1242,6,
+1250,6,
+1258,12,
+1272,2,
+1570,5,
+1728,1,
+1730,1,
+1747,1,
+2345,1,
+2353,1,
+2356,1,
+2392,8,
+2507,2,
+2524,2,
+2527,1,
+2611,1,
+2614,1,
+2649,3,
+2654,1,
+2888,1,
+2891,2,
+2908,2,
+2964,1,
+3018,3,
+3144,1,
+3264,1,
+3271,2,
+3274,2,
+3402,3,
+3546,1,
+3548,3,
+3907,1,
+3917,1,
+3922,1,
+3927,1,
+3932,1,
+3945,1,
+3955,1,
+3957,2,
+3960,1,
+3969,1,
+3987,1,
+3997,1,
+4002,1,
+4007,1,
+4012,1,
+4025,1,
+4134,1,
+6918,1,
+6920,1,
+6922,1,
+6924,1,
+6926,1,
+6930,1,
+6971,1,
+6973,1,
+6976,2,
+6979,1,
+7680,154,
+7835,1,
+7840,90,
+7936,22,
+7960,6,
+7968,38,
+8008,6,
+8016,8,
+8025,1,
+8027,1,
+8029,1,
+8031,31,
+8064,53,
+8118,7,
+8126,1,
+8129,4,
+8134,14,
+8150,6,
+8157,19,
+8178,3,
+8182,8,
+8192,2,
+8486,1,
+8490,2,
+8602,2,
+8622,1,
+8653,3,
+8708,1,
+8713,1,
+8716,1,
+8740,1,
+8742,1,
+8769,1,
+8772,1,
+8775,1,
+8777,1,
+8800,1,
+8802,1,
+8813,5,
+8820,2,
+8824,2,
+8832,2,
+8836,2,
+8840,2,
+8876,4,
+8928,4,
+8938,4,
+9001,2,
+10972,1,
+12364,1,
+12366,1,
+12368,1,
+12370,1,
+12372,1,
+12374,1,
+12376,1,
+12378,1,
+12380,1,
+12382,1,
+12384,1,
+12386,1,
+12389,1,
+12391,1,
+12393,1,
+12400,2,
+12403,2,
+12406,2,
+12409,2,
+12412,2,
+12436,1,
+12446,1,
+12460,1,
+12462,1,
+12464,1,
+12466,1,
+12468,1,
+12470,1,
+12472,1,
+12474,1,
+12476,1,
+12478,1,
+12480,1,
+12482,1,
+12485,1,
+12487,1,
+12489,1,
+12496,2,
+12499,2,
+12502,2,
+12505,2,
+12508,2,
+12532,1,
+12535,4,
+12542,1,
+44032,11172,
+63744,270,
+64016,1,
+64018,1,
+64021,10,
+64032,1,
+64034,1,
+64037,2,
+64042,4,
+64048,59,
+64112,106,
+64285,1,
+64287,1,
+64298,13,
+64312,5,
+64318,1,
+64320,2,
+64323,2
+];
+
+illegalCharacterSet = [
+880,4,
+886,4,
+895,5,
+907,1,
+909,1,
+930,1,
+975,1,
+1159,1,
+1300,29,
+1367,2,
+1376,1,
+1416,1,
+1419,6,
+1480,8,
+1515,5,
+1525,11,
+1540,7,
+1558,5,
+1564,2,
+1568,1,
+1595,5,
+1631,1,
+1806,1,
+1867,2,
+1902,18,
+1970,14,
+2043,262,
+2362,2,
+2382,2,
+2389,3,
+2417,10,
+2432,1,
+2436,1,
+2445,2,
+2449,2,
+2473,1,
+2481,1,
+2483,3,
+2490,2,
+2501,2,
+2505,2,
+2511,8,
+2520,4,
+2526,1,
+2532,2,
+2555,6,
+2564,1,
+2571,4,
+2577,2,
+2601,1,
+2609,1,
+2612,1,
+2615,1,
+2618,2,
+2621,1,
+2627,4,
+2633,2,
+2638,11,
+2653,1,
+2655,7,
+2677,12,
+2692,1,
+2702,1,
+2706,1,
+2729,1,
+2737,1,
+2740,1,
+2746,2,
+2758,1,
+2762,1,
+2766,2,
+2769,15,
+2788,2,
+2800,1,
+2802,15,
+2820,1,
+2829,2,
+2833,2,
+2857,1,
+2865,1,
+2868,1,
+2874,2,
+2884,3,
+2889,2,
+2894,8,
+2904,4,
+2910,1,
+2914,4,
+2930,16,
+2948,1,
+2955,3,
+2961,1,
+2966,3,
+2971,1,
+2973,1,
+2976,3,
+2981,3,
+2987,3,
+3002,4,
+3011,3,
+3017,1,
+3022,9,
+3032,14,
+3067,6,
+3076,1,
+3085,1,
+3089,1,
+3113,1,
+3124,1,
+3130,4,
+3141,1,
+3145,1,
+3150,7,
+3159,9,
+3170,4,
+3184,18,
+3204,1,
+3213,1,
+3217,1,
+3241,1,
+3252,1,
+3258,2,
+3269,1,
+3273,1,
+3278,7,
+3287,7,
+3295,1,
+3300,2,
+3312,1,
+3315,15,
+3332,1,
+3341,1,
+3345,1,
+3369,1,
+3386,4,
+3396,2,
+3401,1,
+3406,9,
+3416,8,
+3426,4,
+3440,18,
+3460,1,
+3479,3,
+3506,1,
+3516,1,
+3518,2,
+3527,3,
+3531,4,
+3541,1,
+3543,1,
+3552,18,
+3573,12,
+3643,4,
+3676,37,
+3715,1,
+3717,2,
+3721,1,
+3723,2,
+3726,6,
+3736,1,
+3744,1,
+3748,1,
+3750,1,
+3752,2,
+3756,1,
+3770,1,
+3774,2,
+3781,1,
+3783,1,
+3790,2,
+3802,2,
+3806,34,
+3912,1,
+3947,6,
+3980,4,
+3992,1,
+4029,1,
+4045,2,
+4050,46,
+4130,1,
+4136,1,
+4139,1,
+4147,3,
+4154,6,
+4186,70,
+4294,10,
+4349,3,
+4442,5,
+4515,5,
+4602,6,
+4681,1,
+4686,2,
+4695,1,
+4697,1,
+4702,2,
+4745,1,
+4750,2,
+4785,1,
+4790,2,
+4799,1,
+4801,1,
+4806,2,
+4823,1,
+4881,1,
+4886,2,
+4955,4,
+4989,3,
+5018,6,
+5109,12,
+5751,9,
+5789,3,
+5873,15,
+5901,1,
+5909,11,
+5943,9,
+5972,12,
+5997,1,
+6001,1,
+6004,12,
+6110,2,
+6122,6,
+6138,6,
+6159,1,
+6170,6,
+6264,8,
+6314,86,
+6429,3,
+6444,4,
+6460,4,
+6465,3,
+6510,2,
+6517,11,
+6570,6,
+6602,6,
+6618,4,
+6684,2,
+6688,224,
+6988,4,
+7037,387,
+7627,51,
+7836,4,
+7930,6,
+7958,2,
+7966,2,
+8006,2,
+8014,2,
+8024,1,
+8026,1,
+8028,1,
+8030,1,
+8062,2,
+8117,1,
+8133,1,
+8148,2,
+8156,1,
+8176,2,
+8181,1,
+8191,1,
+8292,6,
+8306,2,
+8335,1,
+8341,11,
+8374,26,
+8432,16,
+8527,4,
+8581,11,
+9192,24,
+9255,25,
+9291,21,
+9885,3,
+9907,78,
+9989,1,
+9994,2,
+10024,1,
+10060,1,
+10062,1,
+10067,3,
+10071,1,
+10079,2,
+10133,3,
+10160,1,
+10175,1,
+10187,5,
+10220,4,
+11035,5,
+11044,220,
+11311,1,
+11359,1,
+11373,7,
+11384,8,
+11499,14,
+11558,10,
+11622,9,
+11632,16,
+11671,9,
+11687,1,
+11695,1,
+11703,1,
+11711,1,
+11719,1,
+11727,1,
+11735,1,
+11743,33,
+11800,4,
+11806,98,
+11930,1,
+12020,12,
+12246,26,
+12284,4,
+12352,1,
+12439,2,
+12544,5,
+12589,4,
+12687,1,
+12728,8,
+12752,32,
+12831,1,
+12868,12,
+13055,1,
+19894,10,
+40892,68,
+42125,3,
+42183,569,
+42779,5,
+42786,222,
+43052,20,
+43128,904,
+55204,92,
+64046,2,
+64107,5,
+64218,38,
+64263,12,
+64280,5,
+64311,1,
+64317,1,
+64319,1,
+64322,1,
+64325,1,
+64434,33,
+64832,16,
+64912,2,
+64968,40,
+65022,2,
+65050,6,
+65060,12,
+65107,1,
+65127,1,
+65132,4,
+65141,1,
+65277,2,
+65280,1,
+65471,3,
+65480,2,
+65488,2,
+65496,2,
+65501,3,
+65511,1,
+65519,10
+];
+
+letterCharacterSet = [
+65,26,
+97,26,
+170,1,
+181,1,
+186,1,
+192,23,
+216,31,
+248,458,
+710,12,
+736,5,
+750,1,
+768,112,
+890,4,
+902,1,
+904,3,
+908,1,
+910,20,
+931,44,
+976,38,
+1015,139,
+1155,4,
+1160,140,
+1329,38,
+1369,1,
+1377,39,
+1425,45,
+1471,1,
+1473,2,
+1476,2,
+1479,1,
+1488,27,
+1520,3,
+1552,6,
+1569,26,
+1600,31,
+1646,102,
+1749,8,
+1758,11,
+1770,6,
+1786,3,
+1791,1,
+1808,59,
+1869,33,
+1920,50,
+1994,44,
+2042,1,
+2305,57,
+2364,18,
+2384,5,
+2392,12,
+2427,5,
+2433,3,
+2437,8,
+2447,2,
+2451,22,
+2474,7,
+2482,1,
+2486,4,
+2492,9,
+2503,2,
+2507,4,
+2519,1,
+2524,2,
+2527,5,
+2544,2,
+2561,3,
+2565,6,
+2575,2,
+2579,22,
+2602,7,
+2610,2,
+2613,2,
+2616,2,
+2620,1,
+2622,5,
+2631,2,
+2635,3,
+2649,4,
+2654,1,
+2672,5,
+2689,3,
+2693,9,
+2703,3,
+2707,22,
+2730,7,
+2738,2,
+2741,5,
+2748,10,
+2759,3,
+2763,3,
+2768,1,
+2784,4,
+2817,3,
+2821,8,
+2831,2,
+2835,22,
+2858,7,
+2866,2,
+2869,5,
+2876,8,
+2887,2,
+2891,3,
+2902,2,
+2908,2,
+2911,3,
+2929,1,
+2946,2,
+2949,6,
+2958,3,
+2962,4,
+2969,2,
+2972,1,
+2974,2,
+2979,2,
+2984,3,
+2990,12,
+3006,5,
+3014,3,
+3018,4,
+3031,1,
+3073,3,
+3077,8,
+3086,3,
+3090,23,
+3114,10,
+3125,5,
+3134,7,
+3142,3,
+3146,4,
+3157,2,
+3168,2,
+3202,2,
+3205,8,
+3214,3,
+3218,23,
+3242,10,
+3253,5,
+3260,9,
+3270,3,
+3274,4,
+3285,2,
+3294,1,
+3296,4,
+3330,2,
+3333,8,
+3342,3,
+3346,23,
+3370,16,
+3390,6,
+3398,3,
+3402,4,
+3415,1,
+3424,2,
+3458,2,
+3461,18,
+3482,24,
+3507,9,
+3517,1,
+3520,7,
+3530,1,
+3535,6,
+3542,1,
+3544,8,
+3570,2,
+3585,58,
+3648,15,
+3713,2,
+3716,1,
+3719,2,
+3722,1,
+3725,1,
+3732,4,
+3737,7,
+3745,3,
+3749,1,
+3751,1,
+3754,2,
+3757,13,
+3771,3,
+3776,5,
+3782,1,
+3784,6,
+3804,2,
+3840,1,
+3864,2,
+3893,1,
+3895,1,
+3897,1,
+3902,10,
+3913,34,
+3953,20,
+3974,6,
+3984,8,
+3993,36,
+4038,1,
+4096,34,
+4131,5,
+4137,2,
+4140,7,
+4150,4,
+4176,10,
+4256,38,
+4304,43,
+4348,1,
+4352,90,
+4447,68,
+4520,82,
+4608,73,
+4682,4,
+4688,7,
+4696,1,
+4698,4,
+4704,41,
+4746,4,
+4752,33,
+4786,4,
+4792,7,
+4800,1,
+4802,4,
+4808,15,
+4824,57,
+4882,4,
+4888,67,
+4959,1,
+4992,16,
+5024,85,
+5121,620,
+5743,8,
+5761,26,
+5792,75,
+5888,13,
+5902,7,
+5920,21,
+5952,20,
+5984,13,
+5998,3,
+6002,2,
+6016,52,
+6070,30,
+6103,1,
+6108,2,
+6155,3,
+6176,88,
+6272,42,
+6400,29,
+6432,12,
+6448,12,
+6480,30,
+6512,5,
+6528,42,
+6576,26,
+6656,28,
+6912,76,
+7019,9,
+7424,203,
+7678,158,
+7840,90,
+7936,22,
+7960,6,
+7968,38,
+8008,6,
+8016,8,
+8025,1,
+8027,1,
+8029,1,
+8031,31,
+8064,53,
+8118,7,
+8126,1,
+8130,3,
+8134,7,
+8144,4,
+8150,6,
+8160,13,
+8178,3,
+8182,7,
+8305,1,
+8319,1,
+8336,5,
+8400,32,
+8450,1,
+8455,1,
+8458,10,
+8469,1,
+8473,5,
+8484,1,
+8486,1,
+8488,1,
+8490,4,
+8495,11,
+8508,4,
+8517,5,
+8526,1,
+8579,2,
+11264,47,
+11312,47,
+11360,13,
+11380,4,
+11392,101,
+11520,38,
+11568,54,
+11631,1,
+11648,23,
+11680,7,
+11688,7,
+11696,7,
+11704,7,
+11712,7,
+11720,7,
+11728,7,
+11736,7,
+12293,2,
+12330,6,
+12337,5,
+12347,2,
+12353,86,
+12441,2,
+12445,3,
+12449,90,
+12540,4,
+12549,40,
+12593,94,
+12704,24,
+12784,16,
+13312,6582,
+19968,20924,
+40960,1165,
+42775,4,
+43008,40,
+43072,52,
+44032,11172,
+63744,302,
+64048,59,
+64112,106,
+64256,7,
+64275,5,
+64285,12,
+64298,13,
+64312,5,
+64318,1,
+64320,2,
+64323,2,
+64326,108,
+64467,363,
+64848,64,
+64914,54,
+65008,12,
+65024,16,
+65056,4,
+65136,5,
+65142,135,
+65313,26,
+65345,26,
+65382,89,
+65474,6,
+65482,6,
+65490,6
+];
+
+lowercaseLetterCharacterSet = [
+97,26,
+170,1,
+181,1,
+186,1,
+223,24,
+248,8,
+257,1,
+259,1,
+261,1,
+263,1,
+265,1,
+267,1,
+269,1,
+271,1,
+273,1,
+275,1,
+277,1,
+279,1,
+281,1,
+283,1,
+285,1,
+287,1,
+289,1,
+291,1,
+293,1,
+295,1,
+297,1,
+299,1,
+301,1,
+303,1,
+305,1,
+307,1,
+309,1,
+311,2,
+314,1,
+316,1,
+318,1,
+320,1,
+322,1,
+324,1,
+326,1,
+328,2,
+331,1,
+333,1,
+335,1,
+337,1,
+339,1,
+341,1,
+343,1,
+345,1,
+347,1,
+349,1,
+351,1,
+353,1,
+355,1,
+357,1,
+359,1,
+361,1,
+363,1,
+365,1,
+367,1,
+369,1,
+371,1,
+373,1,
+375,1,
+378,1,
+380,1,
+382,3,
+387,1,
+389,1,
+392,1,
+396,2,
+402,1,
+405,1,
+409,3,
+414,1,
+417,1,
+419,1,
+421,1,
+424,1,
+426,2,
+429,1,
+432,1,
+436,1,
+438,1,
+441,2,
+445,3,
+454,1,
+457,1,
+460,1,
+462,1,
+464,1,
+466,1,
+468,1,
+470,1,
+472,1,
+474,1,
+476,2,
+479,1,
+481,1,
+483,1,
+485,1,
+487,1,
+489,1,
+491,1,
+493,1,
+495,2,
+499,1,
+501,1,
+505,1,
+507,1,
+509,1,
+511,1,
+513,1,
+515,1,
+517,1,
+519,1,
+521,1,
+523,1,
+525,1,
+527,1,
+529,1,
+531,1,
+533,1,
+535,1,
+537,1,
+539,1,
+541,1,
+543,1,
+545,1,
+547,1,
+549,1,
+551,1,
+553,1,
+555,1,
+557,1,
+559,1,
+561,1,
+563,7,
+572,1,
+575,2,
+578,1,
+583,1,
+585,1,
+587,1,
+589,1,
+591,69,
+661,27,
+891,3,
+912,1,
+940,35,
+976,2,
+981,3,
+985,1,
+987,1,
+989,1,
+991,1,
+993,1,
+995,1,
+997,1,
+999,1,
+1001,1,
+1003,1,
+1005,1,
+1007,5,
+1013,1,
+1016,1,
+1019,2,
+1072,48,
+1121,1,
+1123,1,
+1125,1,
+1127,1,
+1129,1,
+1131,1,
+1133,1,
+1135,1,
+1137,1,
+1139,1,
+1141,1,
+1143,1,
+1145,1,
+1147,1,
+1149,1,
+1151,1,
+1153,1,
+1163,1,
+1165,1,
+1167,1,
+1169,1,
+1171,1,
+1173,1,
+1175,1,
+1177,1,
+1179,1,
+1181,1,
+1183,1,
+1185,1,
+1187,1,
+1189,1,
+1191,1,
+1193,1,
+1195,1,
+1197,1,
+1199,1,
+1201,1,
+1203,1,
+1205,1,
+1207,1,
+1209,1,
+1211,1,
+1213,1,
+1215,1,
+1218,1,
+1220,1,
+1222,1,
+1224,1,
+1226,1,
+1228,1,
+1230,2,
+1233,1,
+1235,1,
+1237,1,
+1239,1,
+1241,1,
+1243,1,
+1245,1,
+1247,1,
+1249,1,
+1251,1,
+1253,1,
+1255,1,
+1257,1,
+1259,1,
+1261,1,
+1263,1,
+1265,1,
+1267,1,
+1269,1,
+1271,1,
+1273,1,
+1275,1,
+1277,1,
+1279,1,
+1281,1,
+1283,1,
+1285,1,
+1287,1,
+1289,1,
+1291,1,
+1293,1,
+1295,1,
+1297,1,
+1299,1,
+1377,39,
+7424,44,
+7522,22,
+7545,34,
+7681,1,
+7683,1,
+7685,1,
+7687,1,
+7689,1,
+7691,1,
+7693,1,
+7695,1,
+7697,1,
+7699,1,
+7701,1,
+7703,1,
+7705,1,
+7707,1,
+7709,1,
+7711,1,
+7713,1,
+7715,1,
+7717,1,
+7719,1,
+7721,1,
+7723,1,
+7725,1,
+7727,1,
+7729,1,
+7731,1,
+7733,1,
+7735,1,
+7737,1,
+7739,1,
+7741,1,
+7743,1,
+7745,1,
+7747,1,
+7749,1,
+7751,1,
+7753,1,
+7755,1,
+7757,1,
+7759,1,
+7761,1,
+7763,1,
+7765,1,
+7767,1,
+7769,1,
+7771,1,
+7773,1,
+7775,1,
+7777,1,
+7779,1,
+7781,1,
+7783,1,
+7785,1,
+7787,1,
+7789,1,
+7791,1,
+7793,1,
+7795,1,
+7797,1,
+7799,1,
+7801,1,
+7803,1,
+7805,1,
+7807,1,
+7809,1,
+7811,1,
+7813,1,
+7815,1,
+7817,1,
+7819,1,
+7821,1,
+7823,1,
+7825,1,
+7827,1,
+7829,7,
+7841,1,
+7843,1,
+7845,1,
+7847,1,
+7849,1,
+7851,1,
+7853,1,
+7855,1,
+7857,1,
+7859,1,
+7861,1,
+7863,1,
+7865,1,
+7867,1,
+7869,1,
+7871,1,
+7873,1,
+7875,1,
+7877,1,
+7879,1,
+7881,1,
+7883,1,
+7885,1,
+7887,1,
+7889,1,
+7891,1,
+7893,1,
+7895,1,
+7897,1,
+7899,1,
+7901,1,
+7903,1,
+7905,1,
+7907,1,
+7909,1,
+7911,1,
+7913,1,
+7915,1,
+7917,1,
+7919,1,
+7921,1,
+7923,1,
+7925,1,
+7927,1,
+7929,1,
+7936,8,
+7952,6,
+7968,8,
+7984,8,
+8000,6,
+8016,8,
+8032,8,
+8048,14,
+8064,8,
+8080,8,
+8096,8,
+8112,5,
+8118,2,
+8126,1,
+8130,3,
+8134,2,
+8144,4,
+8150,2,
+8160,8,
+8178,3,
+8182,2,
+8305,1,
+8319,1,
+8458,1,
+8462,2,
+8467,1,
+8495,1,
+8500,1,
+8505,1,
+8508,2,
+8518,4,
+8526,1,
+8580,1,
+11312,47,
+11361,1,
+11365,2,
+11368,1,
+11370,1,
+11372,1,
+11380,1,
+11382,2,
+11393,1,
+11395,1,
+11397,1,
+11399,1,
+11401,1,
+11403,1,
+11405,1,
+11407,1,
+11409,1,
+11411,1,
+11413,1,
+11415,1,
+11417,1,
+11419,1,
+11421,1,
+11423,1,
+11425,1,
+11427,1,
+11429,1,
+11431,1,
+11433,1,
+11435,1,
+11437,1,
+11439,1,
+11441,1,
+11443,1,
+11445,1,
+11447,1,
+11449,1,
+11451,1,
+11453,1,
+11455,1,
+11457,1,
+11459,1,
+11461,1,
+11463,1,
+11465,1,
+11467,1,
+11469,1,
+11471,1,
+11473,1,
+11475,1,
+11477,1,
+11479,1,
+11481,1,
+11483,1,
+11485,1,
+11487,1,
+11489,1,
+11491,2,
+11520,38,
+64256,7,
+64275,5
+];
+
+nonBaseCharacterSet = [
+768,112,
+1155,4,
+1160,2,
+1425,45,
+1471,1,
+1473,2,
+1476,2,
+1479,1,
+1552,6,
+1611,20,
+1648,1,
+1750,7,
+1758,7,
+1767,2,
+1770,4,
+1809,1,
+1840,27,
+1958,11,
+2027,9,
+2305,3,
+2364,1,
+2366,16,
+2385,4,
+2402,2,
+2433,3,
+2492,1,
+2494,7,
+2503,2,
+2507,3,
+2519,1,
+2530,2,
+2561,3,
+2620,1,
+2622,5,
+2631,2,
+2635,3,
+2672,2,
+2689,3,
+2748,1,
+2750,8,
+2759,3,
+2763,3,
+2786,2,
+2817,3,
+2876,1,
+2878,6,
+2887,2,
+2891,3,
+2902,2,
+2946,1,
+3006,5,
+3014,3,
+3018,4,
+3031,1,
+3073,3,
+3134,7,
+3142,3,
+3146,4,
+3157,2,
+3202,2,
+3260,1,
+3262,7,
+3270,3,
+3274,4,
+3285,2,
+3298,2,
+3330,2,
+3390,6,
+3398,3,
+3402,4,
+3415,1,
+3458,2,
+3530,1,
+3535,6,
+3542,1,
+3544,8,
+3570,2,
+3633,1,
+3636,7,
+3655,8,
+3761,1,
+3764,6,
+3771,2,
+3784,6,
+3864,2,
+3893,1,
+3895,1,
+3897,1,
+3902,2,
+3953,20,
+3974,2,
+3984,8,
+3993,36,
+4038,1,
+4140,7,
+4150,4,
+4182,4,
+4959,1,
+5906,3,
+5938,3,
+5970,2,
+6002,2,
+6070,30,
+6109,1,
+6155,3,
+6313,1,
+6432,12,
+6448,12,
+6576,17,
+6600,2,
+6679,5,
+6912,5,
+6964,17,
+7019,9,
+7616,11,
+7678,2,
+8400,32,
+12330,6,
+12441,2,
+43010,1,
+43014,1,
+43019,1,
+43043,5,
+64286,1,
+65024,16
+];
+
+punctuationCharacterSet = [
+33,3,
+37,6,
+44,4,
+58,2,
+63,2,
+91,3,
+95,1,
+123,1,
+125,1,
+161,1,
+171,1,
+183,1,
+187,1,
+191,1,
+894,1,
+903,1,
+1370,6,
+1417,2,
+1470,1,
+1472,1,
+1475,1,
+1478,1,
+1523,2,
+1548,2,
+1563,1,
+1566,2,
+1642,4,
+1748,1,
+1792,14,
+2039,3,
+2404,2,
+2416,1,
+3572,1,
+3663,1,
+3674,2,
+3844,15,
+3898,4,
+3973,1,
+4048,2,
+4170,6,
+4347,1,
+4961,8,
+5741,2,
+5787,2,
+5867,3,
+5941,2,
+6100,3,
+6104,3,
+6144,11,
+6468,2,
+6622,2,
+6686,2,
+7002,7,
+8208,24,
+8240,20,
+8261,13,
+8275,12,
+8317,2,
+8333,2,
+9001,2,
+10088,14,
+10181,2,
+10214,6,
+10627,22,
+10712,4,
+10748,2,
+11513,4,
+11518,2,
+11776,24,
+11804,2,
+12289,3,
+12296,10,
+12308,12,
+12336,1,
+12349,1,
+12448,1,
+12539,1,
+43124,4,
+64830,2,
+65040,10,
+65072,35,
+65108,14,
+65123,1,
+65128,1,
+65130,2,
+65281,3,
+65285,6,
+65292,4,
+65306,2,
+65311,2,
+65339,3,
+65343,1,
+65371,1,
+65373,1
+];
+
+uppercaseLetterCharacterSet = [
+65,26,
+192,23,
+216,7,
+256,1,
+258,1,
+260,1,
+262,1,
+264,1,
+266,1,
+268,1,
+270,1,
+272,1,
+274,1,
+276,1,
+278,1,
+280,1,
+282,1,
+284,1,
+286,1,
+288,1,
+290,1,
+292,1,
+294,1,
+296,1,
+298,1,
+300,1,
+302,1,
+304,1,
+306,1,
+308,1,
+310,1,
+313,1,
+315,1,
+317,1,
+319,1,
+321,1,
+323,1,
+325,1,
+327,1,
+330,1,
+332,1,
+334,1,
+336,1,
+338,1,
+340,1,
+342,1,
+344,1,
+346,1,
+348,1,
+350,1,
+352,1,
+354,1,
+356,1,
+358,1,
+360,1,
+362,1,
+364,1,
+366,1,
+368,1,
+370,1,
+372,1,
+374,1,
+376,2,
+379,1,
+381,1,
+385,2,
+388,1,
+390,2,
+393,3,
+398,4,
+403,2,
+406,3,
+412,2,
+415,2,
+418,1,
+420,1,
+422,2,
+425,1,
+428,1,
+430,2,
+433,3,
+437,1,
+439,2,
+444,1,
+452,2,
+455,2,
+458,2,
+461,1,
+463,1,
+465,1,
+467,1,
+469,1,
+471,1,
+473,1,
+475,1,
+478,1,
+480,1,
+482,1,
+484,1,
+486,1,
+488,1,
+490,1,
+492,1,
+494,1,
+497,2,
+500,1,
+502,3,
+506,1,
+508,1,
+510,1,
+512,1,
+514,1,
+516,1,
+518,1,
+520,1,
+522,1,
+524,1,
+526,1,
+528,1,
+530,1,
+532,1,
+534,1,
+536,1,
+538,1,
+540,1,
+542,1,
+544,1,
+546,1,
+548,1,
+550,1,
+552,1,
+554,1,
+556,1,
+558,1,
+560,1,
+562,1,
+570,2,
+573,2,
+577,1,
+579,4,
+584,1,
+586,1,
+588,1,
+590,1,
+902,1,
+904,3,
+908,1,
+910,2,
+913,17,
+931,9,
+978,3,
+984,1,
+986,1,
+988,1,
+990,1,
+992,1,
+994,1,
+996,1,
+998,1,
+1000,1,
+1002,1,
+1004,1,
+1006,1,
+1012,1,
+1015,1,
+1017,2,
+1021,51,
+1120,1,
+1122,1,
+1124,1,
+1126,1,
+1128,1,
+1130,1,
+1132,1,
+1134,1,
+1136,1,
+1138,1,
+1140,1,
+1142,1,
+1144,1,
+1146,1,
+1148,1,
+1150,1,
+1152,1,
+1162,1,
+1164,1,
+1166,1,
+1168,1,
+1170,1,
+1172,1,
+1174,1,
+1176,1,
+1178,1,
+1180,1,
+1182,1,
+1184,1,
+1186,1,
+1188,1,
+1190,1,
+1192,1,
+1194,1,
+1196,1,
+1198,1,
+1200,1,
+1202,1,
+1204,1,
+1206,1,
+1208,1,
+1210,1,
+1212,1,
+1214,1,
+1216,2,
+1219,1,
+1221,1,
+1223,1,
+1225,1,
+1227,1,
+1229,1,
+1232,1,
+1234,1,
+1236,1,
+1238,1,
+1240,1,
+1242,1,
+1244,1,
+1246,1,
+1248,1,
+1250,1,
+1252,1,
+1254,1,
+1256,1,
+1258,1,
+1260,1,
+1262,1,
+1264,1,
+1266,1,
+1268,1,
+1270,1,
+1272,1,
+1274,1,
+1276,1,
+1278,1,
+1280,1,
+1282,1,
+1284,1,
+1286,1,
+1288,1,
+1290,1,
+1292,1,
+1294,1,
+1296,1,
+1298,1,
+1329,38,
+4256,38,
+7680,1,
+7682,1,
+7684,1,
+7686,1,
+7688,1,
+7690,1,
+7692,1,
+7694,1,
+7696,1,
+7698,1,
+7700,1,
+7702,1,
+7704,1,
+7706,1,
+7708,1,
+7710,1,
+7712,1,
+7714,1,
+7716,1,
+7718,1,
+7720,1,
+7722,1,
+7724,1,
+7726,1,
+7728,1,
+7730,1,
+7732,1,
+7734,1,
+7736,1,
+7738,1,
+7740,1,
+7742,1,
+7744,1,
+7746,1,
+7748,1,
+7750,1,
+7752,1,
+7754,1,
+7756,1,
+7758,1,
+7760,1,
+7762,1,
+7764,1,
+7766,1,
+7768,1,
+7770,1,
+7772,1,
+7774,1,
+7776,1,
+7778,1,
+7780,1,
+7782,1,
+7784,1,
+7786,1,
+7788,1,
+7790,1,
+7792,1,
+7794,1,
+7796,1,
+7798,1,
+7800,1,
+7802,1,
+7804,1,
+7806,1,
+7808,1,
+7810,1,
+7812,1,
+7814,1,
+7816,1,
+7818,1,
+7820,1,
+7822,1,
+7824,1,
+7826,1,
+7828,1,
+7840,1,
+7842,1,
+7844,1,
+7846,1,
+7848,1,
+7850,1,
+7852,1,
+7854,1,
+7856,1,
+7858,1,
+7860,1,
+7862,1,
+7864,1,
+7866,1,
+7868,1,
+7870,1,
+7872,1,
+7874,1,
+7876,1,
+7878,1,
+7880,1,
+7882,1,
+7884,1,
+7886,1,
+7888,1,
+7890,1,
+7892,1,
+7894,1,
+7896,1,
+7898,1,
+7900,1,
+7902,1,
+7904,1,
+7906,1,
+7908,1,
+7910,1,
+7912,1,
+7914,1,
+7916,1,
+7918,1,
+7920,1,
+7922,1,
+7924,1,
+7926,1,
+7928,1,
+7944,8,
+7960,6,
+7976,8,
+7992,8,
+8008,6,
+8025,1,
+8027,1,
+8029,1,
+8031,1,
+8040,8,
+8072,8,
+8088,8,
+8104,8,
+8120,5,
+8136,5,
+8152,4,
+8168,5,
+8184,5,
+8450,1,
+8455,1,
+8459,3,
+8464,3,
+8469,1,
+8473,5,
+8484,1,
+8486,1,
+8488,1,
+8490,4,
+8496,4,
+8510,2,
+8517,1,
+8579,1,
+11264,47,
+11360,1,
+11362,3,
+11367,1,
+11369,1,
+11371,1,
+11381,1,
+11392,1,
+11394,1,
+11396,1,
+11398,1,
+11400,1,
+11402,1,
+11404,1,
+11406,1,
+11408,1,
+11410,1,
+11412,1,
+11414,1,
+11416,1,
+11418,1,
+11420,1,
+11422,1,
+11424,1,
+11426,1,
+11428,1,
+11430,1,
+11432,1,
+11434,1,
+11436,1,
+11438,1,
+11440,1,
+11442,1,
+11444,1,
+11446,1,
+11448,1,
+11450,1,
+11452,1,
+11454,1,
+11456,1,
+11458,1,
+11460,1,
+11462,1,
+11464,1,
+11466,1,
+11468,1,
+11470,1,
+11472,1,
+11474,1,
+11476,1,
+11478,1,
+11480,1,
+11482,1,
+11484,1,
+11486,1,
+11488,1,
+11490,1
+];
+
+whitespaceAndNewlineCharacterSet = [
+9,5,
+32,1,
+133,1,
+160,1,
+5760,1,
+8192,12,
+8232,2,
+8239,1,
+8287,1
+];
+
+whitespaceCharacterSet = [
+9,1,
+32,1,
+160,1,
+5760,1,
+8192,12,
+8239,1,
+8287,1
+];
diff --git a/Foundation/CPDate.j b/Foundation/CPDate.j
index 6340ef48c..3ecdd21a4 100644
--- a/Foundation/CPDate.j
+++ b/Foundation/CPDate.j
@@ -148,12 +148,21 @@ var CPDateReferenceDate = new Date(Date.UTC(2001, 1, 1, 0, 0, 0, 0));
- (BOOL)isEqual:(CPDate)aDate
{
+ if (self === aDate)
+ return YES;
+
+ if (!aDate || ![aDate isKindOfClass:[CPDate class]])
+ return NO;
+
return [self isEqualToDate:aDate];
}
-- (BOOL)isEqualToDate:(CPDate)anotherDate
+- (BOOL)isEqualToDate:(CPDate)aDate
{
- return self === anotherDate || (anotherDate !== nil && anotherDate.isa && [anotherDate isKindOfClass:CPDate] && !(self < anotherDate || self > anotherDate));
+ if (!aDate)
+ return NO;
+
+ return !(self < aDate || self > aDate);
}
- (CPComparisonResult)compare:(CPDate)anotherDate
@@ -177,10 +186,11 @@ var CPDateReferenceDate = new Date(Date.UTC(2001, 1, 1, 0, 0, 0, 0));
*/
- (CPString)description
{
- var hours = Math.floor(self.getTimezoneOffset() / 60),
+ var positive = self.getTimezoneOffset() >= 0,
+ hours = FLOOR(self.getTimezoneOffset() / 60),
minutes = self.getTimezoneOffset() - hours * 60;
- return [CPString stringWithFormat:@"%04d-%02d-%02d %02d:%02d:%02d +%02d%02d", self.getFullYear(), self.getMonth() + 1, self.getDate(), self.getHours(), self.getMinutes(), self.getSeconds(), hours, minutes];
+ return [CPString stringWithFormat:@"%04d-%02d-%02d %02d:%02d:%02d %s%02d%02d", self.getFullYear(), self.getMonth()+1, self.getDate(), self.getHours(), self.getMinutes(), self.getSeconds(), positive ? "+" : "-", ABS(hours), ABS(minutes)];
}
- (id)copy
diff --git a/Foundation/CPDictionary.j b/Foundation/CPDictionary.j
index 285e52e85..6f92f0c23 100755
--- a/Foundation/CPDictionary.j
+++ b/Foundation/CPDictionary.j
@@ -337,6 +337,35 @@
return values;
}
+/*!
+ Returns a new array containing the keys corresponding to all occurrences of a given object in the receiver.
+ @param anObject The value to look for in the receiver.
+ @return A new array containing the keys corresponding to all occurrences of anObject in the receiver. If no object matching anObject is found, returns an empty array.
+
+ Each object in the receiver is sent an isEqual: message to determine if its equal to anObject.
+ If the check for isEqual fails a check is made to see if the two objects are the same object. This provides compatability for JSObjects.
+*/
+- (CPArray)allKeysForObject:(id)anObject
+{
+ var count = _keys.length,
+ index = 0,
+ matchingKeys = [],
+ thisKey = nil,
+ thisValue = nil;
+
+ for (; index < count; ++index)
+ {
+ thisKey = _keys[index],
+ thisValue = _buckets[thisKey];
+ if (thisValue.isa && anObject && anObject.isa && [thisValue respondsToSelector:@selector(isEqual:)] && [thisValue isEqual:anObject])
+ matchingKeys.push(thisKey);
+ else if (thisValue === anObject)
+ matchingKeys.push(thisKey);
+ }
+
+ return matchingKeys;
+}
+
/*!
Returns an enumerator that enumerates over all the dictionary's keys.
*/
@@ -425,7 +454,7 @@
@param aKey the key for the object's entry
@return the object for the entry
*/
-- (id)objectForKey:(CPString)aKey
+- (id)objectForKey:(id)aKey
{
var object = _buckets[aKey];
diff --git a/Foundation/CPFormatter.j b/Foundation/CPFormatter.j
new file mode 100644
index 000000000..a5465aed8
--- /dev/null
+++ b/Foundation/CPFormatter.j
@@ -0,0 +1,167 @@
+/*
+ * CPFormatter.j
+ * Foundation
+ *
+ * Created by Randall Luecke
+ * Copyright 2010, 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
+ */
+
+/*!
+ @class CPFormatter
+ @ingroup foundation
+ @brief CPFormatter is an abstract class that declares an interface for objects that create, interpret,
+ and validate the textual representation of cell contents. The Foundation framework provides two
+ concrete subclasses of CPFormatter to generate these objects: CPNumberFormatter and CPDateFormatter.
+
+ CPFormatter is intended for subclassing. A custom formatter can restrict the input and enhance the
+ display of data in novel ways. For example, you could have a custom formatter that ensures that serial
+ numbers entered by a user conform to predefined formats. Before you decide to create a custom formatter,
+ make sure that you cannot configure the public subclasses CPDateFormatter and CPNumberFormatter to satisfy your requirements.
+*/
+
+@import
+
+@implementation CPFormatter : CPObject
+
+/*!
+ The default implementation of this method raises an exception.
+
+ When implementing a subclass, return the CPString object that textually represents
+ the view's object for display andif editingStringForObjectValue: is unimplementedfor editing.
+ First test the passed-in object to see if its of the correct class. If it isnt, return nil;
+ but if it is of the right class, return a properly formatted and, if necessary, localized string.
+ (See the specification of the CPString class for formatting and localizing details.)
+
+ @param anObject The object for which a textual representation is returned
+ @return CPSting a formatted string
+*/
+- (CPString)stringForObjectValue:(id)anObject
+{
+ _CPRaiseInvalidAbstractInvocation(self, @selector(stringForObjectValue:));
+ return nil;
+}
+
+
+/*- (CPAttributedString)attributedStringForObjectValue:(id)anObject withDefaultAttributes:(CPDictionary)attributes
+{
+
+}*/
+
+
+/*!
+ The default implementation of this method invokes stringForObjectValue:.
+
+ When implementing a subclass, override this method only when the string that users see and the string
+ that they edit are different. In your implementation, return an CPString object that is used for editing,
+ following the logic recommended for implementing stringForObjectValue:. As an example, you would implement
+ this method if you want the dollar signs in displayed strings removed for editing.
+
+ @param anObject the object for which to return an editing string
+ @return CPString object that is used for editing the textual represntation of an object
+*/
+- (CPString)editingStringForObjectValue:(id)anObject
+{
+ return [self stringForObjectValue:anObject];
+}
+
+
+/*!
+ The default implementation of this method raises an exception.
+
+ When implementing a subclass, return by reference the object anObject after creating it from string.
+ Return YES if the conversion is successful. If you return NO, also return by indirection (in error)
+ a localized user-presentable CPString object that explains the reason why the conversion failed; the delegate
+ (if any) of the CPControl object managing the cell can then respond to the failure in
+ control:didFailToFormatString:errorDescription:. However, if error is nil, the sender is not interested in
+ the error description, and you should not attempt to assign one.
+
+ @param anObject if conversion is successful, upon return contains the object created from the string
+ @param aString the string to parse.
+ @param anError if non-nil, if there is an error durring the conversion, upon return contains an CPString object that describes the problem.
+ @return BOOL YES if the conversion from the string to a view content object was successful, otherwise NO.
+*/
+- (BOOL)getObjectValue:(id)anObject forString:(CPString)aString errorDescription:(CPString)anError
+{
+ _CPRaiseInvalidAbstractInvocation(self, @selector(getObjectValue:forString:errorDescription:));
+ return NO;
+}
+
+
+/*!
+ Returns a Boolean value that indicates whether a partial string is valid.
+
+ This method is invoked each time the user presses a key while the cell has the keyboard focusit lets you verify and
+ edit the cell text as the user types it.
+
+ In a subclass implementation, evaluate partialString according to the context, edit the text if necessary, and return
+ by reference any edited string in newString. Return YES if partialString is acceptable and NO if partialString is unacceptable.
+ If you return NO and newString is nil, the cell displays partialString minus the last character typed. If you return NO, you can
+ also return by indirection an CPString object (in error) that explains the reason why the validation failed; the delegate (if any)
+ of the CPControl object managing the cell can then respond to the failure in control:didFailToValidatePartialString:errorDescription:.
+ The selection range will always be set to the end of the text if replacement occurs.
+
+ This method is a compatibility method. If a subclass overrides this method and does not override
+ isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:, this method will be called as before
+ (isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription: just calls this one by default).
+
+ @param aPartialString the text currently in the view.
+ @param aNewString if aPartialString needs to be modified, upon return contains the replacement string.
+ @param anError if non-nil, if validation fails contains a CPString object that desibes the problem.
+ @return YES if aPartialString is an acceptable value, otherwise NO.
+*/
+- (BOOL)isPartialStringValid:(CPString)aPartialString newEditingString:(CPString)aNewString errorDescription:(CPString)anError
+{
+ _CPRaiseInvalidAbstractInvocation(self, @selector(isPartialStringValid:newEditingString:errorDescription:));
+ return NO;
+}
+
+/*!
+ This method should be implemented in subclasses that want to validate user changes to a string in a field, where the user changes are
+ not necessarily at the end of the string, and preserve the selection (or set a different one, such as selecting the erroneous part of
+ the string the user has typed).
+
+ In a subclass implementation, evaluate partialString according to the context. Return YES if partialStringPtr is acceptable and NO if partialStringPtr
+ is unacceptable. Assign a new string to partialStringPtr and a new range to proposedSelRangePtr and return NO if you want to replace the string and
+ change the selection range. If you return NO, you can also return by indirection an CPString object (in error) that explains the reason why the
+ validation failed; the delegate (if any) of the CPControl object managing the cell can then respond to the failure in
+ control:didFailToValidatePartialString:errorDescription:.
+
+ @param aPartialString The new string to validate.
+ @param aProposedSelectedRange The selection range that will be used if the string is accepted or replaced.
+ @param originalString The original string, before the proposed change.
+ @param originalSelectedRange The selection range over which the change is to take place.
+ @param error If non-nil, if validation fails contains an CPString object that descibes the problem.
+ @return YES if aPartialString is acceptable, otherwise NO.
+
+*/
+- (BOOL)isPartialStringValue:(CPString)aPartialString proposedSelectedRange:(CPRange)aProposedSelectedRange originalString:(CPString)originalString originalSelectedRange:(CPRange)originalSelectedRange errorDescription:(CPString)anError
+{
+ _CPRaiseInvalidAbstractInvocation(self, @selector(isPartialStringValue:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:));
+ return NO;
+}
+
+- (id)initWithCoder:(CPCoder)aCoder
+{
+ return [self init];
+}
+
+- (void)encodeWithCoder:(CPCoder)aCoder
+{
+
+}
+
+@end
\ No newline at end of file
diff --git a/Foundation/CPIndexSet.j b/Foundation/CPIndexSet.j
index 890ee8c0c..adac93215 100644
--- a/Foundation/CPIndexSet.j
+++ b/Foundation/CPIndexSet.j
@@ -130,6 +130,17 @@
return self;
}
+- (BOOL)isEqual:(id)anObject
+{
+ if (self === anObject)
+ return YES;
+
+ if (!anObject || ![anObject isKindOfClass:[CPIndexSet class]])
+ return NO;
+
+ return [self isEqualToIndexSet:anObject];
+}
+
// Querying an Index Set
/*!
Compares the receiver with the provided index set.
@@ -160,6 +171,13 @@
return YES;
}
+- (BOOL)isEqual:(id)anObject
+{
+ return self === anObject ||
+ [anObject isKindOfClass:[self class]] &&
+ [self isEqualToIndexSet:anObject];
+}
+
/*!
Returns \c YES if the index set contains the specified index.
@param anIndex the index to check for in the set
@@ -698,12 +716,12 @@
var range = _ranges[i],
maximum = CPMaxRange(range);
- if (anIndex > maximum)
+ if (anIndex >= maximum)
break;
// If our index is within our range, but not the first index,
// then this range will be split.
- if (anIndex > range.location && anIndex < maximum)
+ if (anIndex > range.location)
{
// Split the range into shift and unshifted.
shifted = CPMakeRange(anIndex + aDelta, maximum - anIndex);
diff --git a/Foundation/CPKeyValueCoding.j b/Foundation/CPKeyValueCoding.j
index 104a6931f..1449899e4 100644
--- a/Foundation/CPKeyValueCoding.j
+++ b/Foundation/CPKeyValueCoding.j
@@ -33,6 +33,9 @@ CPUndefinedKeyException = @"CPUndefinedKeyException";
CPTargetObjectUserInfoKey = @"CPTargetObjectUserInfoKey";
CPUnknownUserInfoKey = @"CPUnknownUserInfoKey";
+var CPObjectAccessorsForClassKey = @"$CPObjectAccessorsForClassKey",
+ CPObjectModifiersForClassKey = @"$CPObjectModifiersForClassKey";
+
@implementation CPObject (CPKeyValueCoding)
+ (BOOL)accessInstanceVariablesDirectly
@@ -43,26 +46,18 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey";
/* @ignore */
+ (SEL)_accessorForKey:(CPString)aKey
{
- if (!CPObjectAccessorsForClass)
- CPObjectAccessorsForClass = [CPDictionary dictionary];
-
- var UID = [isa UID],
- selector = nil,
- accessors = [CPObjectAccessorsForClass objectForKey:UID];
+ var selector = nil,
+ accessors = isa[CPObjectAccessorsForClassKey];
if (accessors)
{
- selector = [accessors objectForKey:aKey];
+ selector = accessors[aKey];
if (selector)
return selector === [CPNull null] ? nil : selector;
}
else
- {
- accessors = [CPDictionary dictionary];
-
- [CPObjectAccessorsForClass setObject:accessors forKey:UID];
- }
+ accessors = isa[CPObjectAccessorsForClassKey] = {};
var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substr(1);
@@ -73,12 +68,12 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey";
[self instancesRespondToSelector:selector = CPSelectorFromString("_" + aKey)] ||
[self instancesRespondToSelector:selector = CPSelectorFromString("_is" + capitalizedKey)])
{
- [accessors setObject:selector forKey:aKey];
+ accessors[aKey] = selector;
return selector;
}
- [accessors setObject:[CPNull null] forKey:aKey];
+ accessors[aKey] = [CPNull null];
return nil;
}
@@ -156,7 +151,7 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey";
- (id)valueForKey:(CPString)aKey
{
var theClass = [self class],
- selector = [theClass _accessorForKey:aKey];
+ selector = _accessorForKey(theClass, aKey);
if (selector)
return objj_msgSend(self, selector);
@@ -264,6 +259,41 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey";
@end
+var Null = [CPNull null];
+var _accessorForKey = function(theClass, aKey)
+{
+ var selector = nil,
+ accessors = theClass.isa[CPObjectAccessorsForClassKey];
+
+ if (accessors)
+ {
+ selector = accessors[aKey];
+
+ if (selector)
+ return selector === Null ? nil : selector;
+ }
+ else
+ accessors = theClass.isa[CPObjectAccessorsForClassKey] = {};
+
+ var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substr(1);
+
+ if ([theClass instancesRespondToSelector:selector = CPSelectorFromString("get" + capitalizedKey)] ||
+ [theClass instancesRespondToSelector:selector = CPSelectorFromString(aKey)] ||
+ [theClass instancesRespondToSelector:selector = CPSelectorFromString("is" + capitalizedKey)] ||
+ [theClass instancesRespondToSelector:selector = CPSelectorFromString("_get" + capitalizedKey)] ||
+ [theClass instancesRespondToSelector:selector = CPSelectorFromString("_" + aKey)] ||
+ [theClass instancesRespondToSelector:selector = CPSelectorFromString("_is" + capitalizedKey)])
+ {
+ accessors[aKey] = selector;
+
+ return selector;
+ }
+
+ accessors[aKey] = Null;
+
+ return nil;
+}
+
@implementation CPDictionary (KeyValueCoding)
- (id)valueForKey:(CPString)aKey
diff --git a/Foundation/CPKeyValueObserving.j b/Foundation/CPKeyValueObserving.j
index 66d08fbb0..a08100481 100644
--- a/Foundation/CPKeyValueObserving.j
+++ b/Foundation/CPKeyValueObserving.j
@@ -27,7 +27,6 @@
@import "CPObject.j"
@import "CPSet.j"
-
@implementation CPObject (KeyValueObserving)
- (void)willChangeValueForKey:(CPString)aKey
@@ -213,7 +212,6 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
_targetObject = aTarget;
_nativeClass = [aTarget class];
- _replacedKeys = [CPSet set];
_observersForKey = {};
_changesForKey = {};
_observersForKeyLength = 0;
@@ -230,6 +228,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
if (existingKVOClass)
{
_targetObject.isa = existingKVOClass;
+ _replacedKeys = existingKVOClass._replacedKeys;
return;
}
@@ -237,6 +236,9 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
objj_registerClassPair(kvoClass);
+ _replacedKeys = [CPSet set];
+ kvoClass._replacedKeys = _replacedKeys;
+
//copy in the methods from our model subclass
var methodList = _CPKVOModelSubclass.method_list,
count = methodList.length,
@@ -293,6 +295,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
var theMethod = class_getInstanceMethod(_nativeClass, theSelector);
class_addMethod(_targetObject.isa, theSelector, theReplacementMethod(aKey, theMethod), "");
+ [_replacedKeys addObject:aKey];
}
}
@@ -390,6 +393,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
- (void)_sendNotificationsForKey:(CPString)aKey changeOptions:(CPDictionary)changeOptions isBefore:(BOOL)isBefore
{
+ // CPLog.warn("_sendNotificationsForKey: " + aKey + " ...isBefore: " + isBefore);
var changes = _changesForKey[aKey];
if (isBefore)
@@ -426,6 +430,11 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
}
else
{
+ // The isBefore path may not have been called as would happen if didChangeX
+ // was called alone.
+ if (!changes)
+ changes = [CPDictionary new];
+
[changes removeObjectForKey:CPKeyValueChangeNotificationIsPriorKey];
var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey];
@@ -483,6 +492,9 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
{
var keyPath = dependentKeyPaths[index];
+ // CPLog.warn("firing dependepent key " + index + " for " + aKey + ": "+keyPath);
+ // objj_backtrace_print(CPLog.error);
+
[self _sendNotificationsForKey:keyPath
changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath]
isBefore:isBefore];
@@ -738,7 +750,7 @@ var _kvoInsertMethodForMethod = function _kvoInsertMethodForMethod(theKey, theMe
{
[self willChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
theMethod.method_imp(self, _cmd, object, index);
- [self didChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]
+ [self didChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
}
}
@@ -748,7 +760,7 @@ var _kvoReplaceMethodForMethod = function _kvoReplaceMethodForMethod(theKey, the
{
[self willChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
theMethod.method_imp(self, _cmd, index, object);
- [self didChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]
+ [self didChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
}
}
@@ -758,7 +770,7 @@ var _kvoRemoveMethodForMethod = function _kvoRemoveMethodForMethod(theKey, theMe
{
[self willChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
theMethod.method_imp(self, _cmd, index);
- [self didChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey]
+ [self didChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
}
}
diff --git a/Foundation/CPKeyedUnarchiver.j b/Foundation/CPKeyedUnarchiver.j
index 8da173959..4737a1705 100644
--- a/Foundation/CPKeyedUnarchiver.j
+++ b/Foundation/CPKeyedUnarchiver.j
@@ -235,7 +235,7 @@ var CPArrayClass = Ni
*/
- (BOOL)decodeBoolForKey:(CPString)aKey
{
- return [self decodeObjectForKey:aKey];
+ return !![self decodeObjectForKey:aKey];
}
/*
diff --git a/Foundation/CPNull.j b/Foundation/CPNull.j
index 9fa42ac55..5af892cfa 100644
--- a/Foundation/CPNull.j
+++ b/Foundation/CPNull.j
@@ -44,6 +44,7 @@ var CPNullSharedNull = nil;
return [super alloc];
}*/
+
/*!
Returns the singleton instance of the CPNull
object. While CPNull and \c nil should
@@ -57,6 +58,14 @@ var CPNullSharedNull = nil;
return CPNullSharedNull;
}
+- (BOOL)isEqual:(id)anObject
+{
+ if (self === anObject)
+ return YES;
+
+ return [anObject isKindOfClass:[CPNull class]];
+}
+
/*!
Returns CPNull null.
@param aCoder the coder from which to do nothing
diff --git a/Foundation/CPObject.j b/Foundation/CPObject.j
index c518f4f70..34d023421 100644
--- a/Foundation/CPObject.j
+++ b/Foundation/CPObject.j
@@ -548,7 +548,7 @@ CPLog(@"Got some class: %@", inst);
objj_class.prototype.toString = objj_object.prototype.toString = function()
{
if (this.isa && class_getInstanceMethod(this.isa, "description") != NULL)
- return [this description]
+ return [this description];
else
return String(this) + " (-description not implemented)";
}
diff --git a/Foundation/CPPredicate/CPComparisonPredicate.j b/Foundation/CPPredicate/CPComparisonPredicate.j
new file mode 100644
index 000000000..8f5a9726b
--- /dev/null
+++ b/Foundation/CPPredicate/CPComparisonPredicate.j
@@ -0,0 +1,605 @@
+@import "CPArray.j"
+@import "CPNull.j"
+@import "CPString.j"
+@import "CPEnumerator.j"
+@import "CPPredicate.j"
+@import "CPExpression.j"
+@import "CPExpression_operator.j"
+
+/*!
+ A predicate to compare directly the left and right hand sides.
+ @global
+ @class CPComparisonPredicate
+*/
+CPDirectPredicateModifier = 0;
+/*!
+ A predicate to compare all entries in the destination of a to-many relationship.
+
+ The left hand side must be a collection. The corresponding predicate compares each value in the left hand side with the right hand side, and returns NO when it finds the first mismatch—or YES if all match.
+ @global
+ @class CPComparisonPredicate
+*/
+CPAllPredicateModifier = 1;
+/*!
+ A predicate to match with any entry in the destination of a to-many relationship.
+
+ The left hand side must be a collection. The corresponding predicate compares each value in the left hand side against the right hand side and returns YES when it finds the first match—or NO if no match is found.
+ @global
+ @class CPComparisonPredicate
+*/
+CPAnyPredicateModifier = 2;
+
+/*!
+ A case-insensitive predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPCaseInsensitivePredicateOption = 1;
+/*!
+ A diacritic-insensitive predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPDiacriticInsensitivePredicateOption = 2;
+CPDiacriticInsensitiveSearch = 128;
+
+/*!
+ A less-than predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPLessThanPredicateOperatorType = 0;
+/*!
+ A less-than-or-equal-to predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPLessThanOrEqualToPredicateOperatorType = 1;
+/*!
+ A greater-than predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPGreaterThanPredicateOperatorType = 2;
+/*!
+ A greater-than-or-equal-to predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPGreaterThanOrEqualToPredicateOperatorType = 3;
+/*!
+ An equal-to predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPEqualToPredicateOperatorType = 4;
+/*!
+ A not-equal-to predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPNotEqualToPredicateOperatorType = 5;
+/*!
+ A full regular expression matching predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPMatchesPredicateOperatorType = 6;
+/*!
+ A simple subset of the matches predicate, similar in behavior to SQL LIKE.
+ @global
+ @class CPComparisonPredicate
+*/
+CPLikePredicateOperatorType = 7;
+/*!
+ A begins-with predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPBeginsWithPredicateOperatorType = 8;
+/*!
+ An ends-with predicate.
+ @global
+ @class CPComparisonPredicate
+*/
+CPEndsWithPredicateOperatorType = 9;
+/*!
+ A predicate to determine if the left hand side is in the right hand side.
+
+ For strings, returns YES if the left hand side is a substring of the right hand side . For collections, returns YES if the left hand side is in the right hand side.
+ @global
+ @class CPComparisonPredicate
+*/
+CPInPredicateOperatorType = 10;
+/*!
+ Predicate that uses a custom selector that takes a single argument and returns a BOOL value.
+
+ The selector is invoked on the left hand side with the right hand side.
+ @global
+ @class CPComparisonPredicate
+*/
+CPCustomSelectorPredicateOperatorType = 11;
+/*!
+ A predicate to determine if the left hand side contains the right hand side.
+
+ Returns YES if [lhs contains rhs]; the left hand side must be a CPExpression object that evaluates to a collection
+ @global
+ @class CPComparisonPredicate
+*/
+CPContainsPredicateOperatorType = 99;
+/*!
+ A predicate to determine if the right hand side lies between bounds specified by the left hand side.
+
+ Returns YES if [lhs between rhs]; the right hand side must be an array in which the first element sets the lower bound and the second element the upper, inclusive. Comparison is performed using compare: or the class-appropriate equivalent.
+ @global
+ @class CPComparisonPredicate
+*/
+CPBetweenPredicateOperatorType = 100;
+
+var CPComparisonPredicateModifier;
+var CPPredicateOperatorType;
+
+/*!
+ @ingroup foundation
+ @class CPComparisonPredicate
+ @brief CPComparisonPredicate is a subclass of CPPredicate used to compare expressions.
+
+ Comparison predicates are predicates used to compare the results of two expressions. Comparison predicates take an operator, a left expression, and a right expression, and return as a BOOL the result of invoking the operator with the results of evaluating the expressions. Expressions are represented by instances of the CPExpression class.
+*/
+@implementation CPComparisonPredicate : CPPredicate
+{
+ CPExpression _left;
+ CPExpression _right;
+
+ CPComparisonPredicateModifier _modifier;
+ CPPredicateOperatorType _type;
+ unsigned int _options;
+ SEL _customSelector;
+}
+
+// Constructors
+/*!
+ Returns a new predicate formed by combining the left and right expressions using a given selector.
+ @param left The left hand side expression.
+ @param right The right hand side expression.
+ @param selector The selector to use for comparison. The method defined by the selector must take a single argument and return a BOOL value.
+ @return A new predicate formed by combining the left and right expressions using selector.
+*/
++ (CPPredicate)predicateWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right customSelector:(SEL)selector
+{
+ return [[self alloc] initWithLeftExpression:left rightExpression:right customSelector:selector];
+}
+
+/*!
+ Creates and returns a predicate of a given type formed by combining given left and right expressions using a given modifier and options.
+ @param left The left hand expression.
+ @param right The right hand expression.
+ @param modifier The modifier to apply.
+ @param type The predicate operator type.
+ @param options The options to apply (see CPComparisonPredicate Options).
+ @return A new predicate of type type formed by combining the given left and right expressions using the modifier and options.
+*/
++ (CPPredicate)predicateWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right modifier:(CPComparisonPredicateModifier)modifier type:(int)type options:(unsigned)options
+{
+ return [[self alloc] initWithLeftExpression:left rightExpression:right modifier:modifier type:type options:options];
+}
+
+/*!
+ Initializes a predicate formed by combining given left and right expressions using a given selector.
+ @param left The left hand side expression.
+ @param right The right hand side expression.
+ @param selector The selector to use for comparison. The method defined by the selector must take a single argument and return a BOOL value.
+ @return The receiver, initialized by combining the left and right expressions using selector.
+*/
+- (id)initWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right customSelector:(SEL)selector
+{
+ _left = left;
+ _right = right;
+ _modifier = CPDirectPredicateModifier;
+ _type = CPCustomSelectorPredicateOperatorType;
+ _options = 0;
+ _customSelector = selector;
+
+ return self;
+}
+
+/*!
+ Initializes a predicate to a given type formed by combining given left and right expressions using a given modifier and options.
+ @param left The left hand expression.
+ @param right The right hand expression.
+ @param modifier The modifier to apply.
+ @param type The predicate operator type.
+ @param options The options to apply (see CPComparisonPredicate Options).
+ @return The receiver, initialized to a predicate of type type formed by combining the left and right expressions using the modifier and options.
+*/
+- (id)initWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right modifier:(CPComparisonPredicateModifier)modifier type:(CPPredicateOperatorType)type options:(unsigned)options
+{
+ _left = left;
+ _right = right;
+ _modifier = modifier;
+ _type = type;
+ _options = (type != CPMatchesPredicateOperatorType &&
+ type != CPLikePredicateOperatorType &&
+ type != CPBeginsWithPredicateOperatorType &&
+ type != CPEndsWithPredicateOperatorType &&
+ type != CPInPredicateOperatorType &&
+ type != CPContainsPredicateOperatorType) ? 0 : options;
+
+ _customSelector = NULL;
+
+ return self;
+}
+
+// Getting Information About a Comparison Predicate
+/*!
+ Returns the comparison predicate modifier for the receiver.
+ @return The comparison predicate modifier for the receiver.
+*/
+- (CPComparisonPredicateModifier)comparisonPredicateModifier
+{
+ return _modifier;
+}
+
+/*!
+ Returns the selector for the receiver.
+ @return The selector for the receiver, or NULL if there is none.
+*/
+- (SEL)customSelector
+{
+ return _customSelector;
+}
+
+/*!
+ Returns the left expression for the receiver.
+ @return The left expression for the receiver, or nil if there is none.
+*/
+- (CPExpression)leftExpression
+{
+ return _left;
+}
+
+/*!
+ Returns the options that are set for the receiver.
+ @return The options that are set for the receiver.
+*/
+- (unsigned)options
+{
+ return _options;
+}
+
+/*!
+ Returns the predicate type for the receiver.
+ @return Returns the predicate type for the receiver.
+*/
+- (CPPredicateOperatorType)predicateOperatorType
+{
+ return _type;
+}
+
+/*!
+ Returns the right expression for the receiver.
+ @return The right expression for the receiver, or nil if there is none.
+*/
+- (CPExpression)rightExpression
+{
+ return _right;
+}
+
+
+- (CPString)predicateFormat
+{
+ var modifier;
+
+ switch (_modifier)
+ {
+ case CPDirectPredicateModifier:
+ modifier = "";
+ break;
+ case CPAllPredicateModifier:
+ modifier = "ALL ";
+ break;
+ case CPAnyPredicateModifier:
+ modifier = "ANY ";
+ break;
+ default:
+ modifier = "";
+ break;
+ }
+
+ var options;
+
+ switch (_options)
+ {
+ case CPCaseInsensitivePredicateOption:
+ options = "[c]";
+ break;
+ case CPDiacriticInsensitivePredicateOption:
+ options = "[d]";
+ break;
+ case CPCaseInsensitivePredicateOption | CPDiacriticInsensitivePredicateOption:
+ options = "[cd]";
+ break;
+ default:
+ options = "";
+ break;
+ }
+
+ var operator;
+
+ switch (_type)
+ {
+ case CPLessThanPredicateOperatorType:
+ operator = "<";
+ break;
+ case CPLessThanOrEqualToPredicateOperatorType:
+ operator = "<=";
+ break;
+ case CPGreaterThanPredicateOperatorType:
+ operator = ">";
+ break;
+ case CPGreaterThanOrEqualToPredicateOperatorType:
+ operator = ">=";
+ break;
+ case CPEqualToPredicateOperatorType:
+ operator = "==";
+ break;
+ case CPNotEqualToPredicateOperatorType:
+ operator = "!=";
+ break;
+ case CPMatchesPredicateOperatorType:
+ operator = "MATCHES";
+ break;
+ case CPLikePredicateOperatorType:
+ operator = "LIKE";
+ break;
+ case CPBeginsWithPredicateOperatorType:
+ operator = "BEGINSWITH";
+ break;
+ case CPEndsWithPredicateOperatorType:
+ operator = "ENDSWITH";
+ break;
+ case CPInPredicateOperatorType:
+ operator = "IN";
+ break;
+ case CPContainsPredicateOperatorType:
+ operator = "CONTAINS";
+ break;
+ case CPCustomSelectorPredicateOperatorType:
+ operator = CPStringFromSelector(_customSelector);
+ break;
+ }
+
+ return [CPString stringWithFormat:@"%s%s %s%s %s",modifier,[_left description],operator,options,[_right description]];
+}
+
+- (CPPredicate)predicateWithSubstitutionVariables:(CPDictionary)variables
+{
+ var left = [_left _expressionWithSubstitutionVariables:variables],
+ right = [_right _expressionWithSubstitutionVariables:variables];
+
+ if (_type != CPCustomSelectorPredicateOperatorType)
+ return [CPComparisonPredicate predicateWithLeftExpression:left rightExpression:right modifier:_modifier type:_type options:_options];
+ else
+ return [CPComparisonPredicate predicateWithLeftExpression:left rightExpression:right customSelector:_customSelector];
+}
+
+- (BOOL)_evaluateValue:lhs rightValue:rhs
+{
+ var leftIsNil = (lhs == nil || [lhs isEqual:[CPNull null]]),
+ rightIsNil = (rhs == nil || [rhs isEqual:[CPNull null]]);
+
+ if ((leftIsNil || rightIsNil) && _type != CPCustomSelectorPredicateOperatorType)
+ return (leftIsNil == rightIsNil &&
+ (_type == CPEqualToPredicateOperatorType ||
+ _type == CPLessThanOrEqualToPredicateOperatorType ||
+ _type == CPGreaterThanOrEqualToPredicateOperatorType));
+
+ var string_compare_options = 0;
+
+ // left and right should be casted first [CAST()] following 10.5 rules.
+ switch (_type)
+ {
+ case CPLessThanPredicateOperatorType:
+ return ([lhs compare:rhs] == CPOrderedAscending);
+ case CPLessThanOrEqualToPredicateOperatorType:
+ return ([lhs compare:rhs] != CPOrderedDescending);
+ case CPGreaterThanPredicateOperatorType:
+ return ([lhs compare:rhs] == CPOrderedDescending);
+ case CPGreaterThanOrEqualToPredicateOperatorType:
+ return ([lhs compare:rhs] != CPOrderedAscending);
+ case CPEqualToPredicateOperatorType:
+ return [lhs isEqual:rhs];
+ case CPNotEqualToPredicateOperatorType:
+ return (![lhs isEqual:rhs]);
+ case CPMatchesPredicateOperatorType:
+ var commut = (_options & CPCaseInsensitivePredicateOption) ? "gi":"g";
+ if (_options & CPDiacriticInsensitivePredicateOption)
+ {
+ lhs = lhs.stripDiacritics();
+ rhs = rhs.stripDiacritics();
+ }
+
+ return (new RegExp(rhs,commut)).test(lhs);
+ case CPLikePredicateOperatorType:
+ if (_options & CPDiacriticInsensitivePredicateOption)
+ {
+ lhs = lhs.stripDiacritics();
+ rhs = rhs.stripDiacritics();
+ }
+ var commut = (_options & CPCaseInsensitivePredicateOption) ? "gi":"g";
+ var reg = new RegExp(rhs.escapeForRegExp(),commut);
+ return reg.test(lhs);
+ case CPBeginsWithPredicateOperatorType:
+ var range = CPMakeRange(0,[rhs length]);
+ if (_options & CPCaseInsensitivePredicateOption) string_compare_options |= CPCaseInsensitiveSearch;
+ if (_options & CPDiacriticInsensitivePredicateOption) string_compare_options |= CPDiacriticInsensitiveSearch;
+
+ return ([lhs compare:rhs options:string_compare_options range:range] == CPOrderedSame);
+ case CPEndsWithPredicateOperatorType:
+ var range = CPMakeRange([lhs length] - [rhs length],[rhs length]);
+ if (_options & CPCaseInsensitivePredicateOption) string_compare_options |= CPCaseInsensitiveSearch;
+ if (_options & CPDiacriticInsensitivePredicateOption) string_compare_options |= CPDiacriticInsensitiveSearch;
+
+ return ([lhs compare:rhs options:string_compare_options range:range] == CPOrderedSame);
+ case CPInPredicateOperatorType:
+ // Handle special case where rhs is a collection and lhs an element of it.
+ if (![rhs isKindOfClass: [CPString class]])
+ {
+ if (![rhs respondsToSelector: @selector(objectEnumerator)])
+ [CPException raise:CPInvalidArgumentException reason:@"The right hand side for an IN operator must be a collection"];
+
+ var e = [rhs objectEnumerator],
+ value;
+ while (value = [e nextObject])
+ if ([value isEqual:lhs])
+ return YES;
+
+ return NO;
+ }
+
+ if (_options & CPCaseInsensitivePredicateOption)
+ string_compare_options |= CPCaseInsensitiveSearch;
+ if (_options & CPDiacriticInsensitivePredicateOption)
+ string_compare_options |= CPDiacriticInsensitiveSearch;
+
+ return ([rhs rangeOfString:lhs options:string_compare_options].location != CPNotFound);
+ case CPCustomSelectorPredicateOperatorType:
+ return [lhs performSelector:_customSelector withObject:rhs];
+ case CPContainsPredicateOperatorType:
+ if (![lhs isKindOfClass: [CPString class]])
+ {
+ if (![lhs respondsToSelector: @selector(objectEnumerator)])
+ [CPException raise:CPInvalidArgumentException reason:@"The left hand side for a CONTAINS operator must be a collection or a string"];
+
+ var e = [lhs objectEnumerator],
+ value;
+ while (value = [e nextObject])
+ if ([value isEqual:rhs])
+ return YES;
+
+ return NO;
+ }
+
+ if (_options & CPCaseInsensitivePredicateOption)
+ string_compare_options |= CPCaseInsensitiveSearch;
+ if (_options & CPDiacriticInsensitivePredicateOption)
+ string_compare_options |= CPDiacriticInsensitiveSearch;
+
+ return ([lhs rangeOfString:rhs options:string_compare_options].location != CPNotFound);
+ case CPBetweenPredicateOperatorType:
+ if ([lhs count] < 2)
+ [CPException raise:CPInvalidArgumentException reason:@"The right hand side for a BETWEEN operator must contain 2 objects"];
+
+ var lower = [rhs objectAtIndex:0],
+ upper = [rhs objectAtIndex:1];
+
+ return ([lhs compare:lower] == CPOrderedDescending && [lhs compare:upper] == CPOrderedAscending);
+ default:
+ return NO;
+ }
+}
+
+- (BOOL)evaluateWithObject:(id)object
+{
+ return [self evaluateWithObject:object substitutionVariables:nil];
+}
+
+- (BOOL)evaluateWithObject:(id)object substitutionVariables:(CPDictionary)variables
+{
+ var left = _left,
+ right = _right;
+
+ if (variables != nil)
+ {
+ left = [left _expressionWithSubstitutionVariables:variables];
+ right = [right _expressionWithSubstitutionVariables:variables];
+ }
+
+ var leftValue = [left expressionValueWithObject:object context:nil],
+ rightValue = [right expressionValueWithObject:object context:nil];
+
+ if (_modifier == CPDirectPredicateModifier)
+ return [self _evaluateValue:leftValue rightValue:rightValue];
+ else
+ {
+ if (![leftValue respondsToSelector:@selector(objectEnumerator)])
+ [CPException raise:CPInvalidArgumentException reason:@"The left hand side for an ALL or ANY operator must be either a CPArray or a CPSet"];
+
+ var e = [leftValue objectEnumerator],
+ result = (_modifier == CPAllPredicateModifier),
+ value;
+
+ while (value = [e nextObject])
+ {
+ var eval = [self _evaluateValue:value rightValue:rightValue];
+ if (eval != result)
+ return eval;
+ }
+
+ return result;
+ }
+}
+
+@end
+
+@implementation CPComparisonPredicate (CPCoding)
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ self = [super init];
+ if (self != nil)
+ {
+ _left = [coder decodeObjectForKey:@"CPComparisonPredicateLeftExpression"];
+ _right = [coder decodeObjectForKey:@"CPComparisonPredicateRightExpression"];
+ _modifier = [coder decodeIntForKey:@"CPComparisonPredicateModifier"];
+ _type = [coder decodeIntForKey:@"CPComparisonPredicateType"];
+ _options = [coder decodeIntForKey:@"CPComparisonPredicateOptions"];
+ _customSelector = [coder decodeObjectForKey:@"CPComparisonPredicateCustomSelector"];
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:_left forKey:@"CPComparisonPredicateLeftExpression"];
+ [coder encodeObject:_right forKey:@"CPComparisonPredicateRightExpression"];
+ [coder encodeInt:_modifier forKey:@"CPComparisonPredicateModifier"];
+ [coder encodeInt:_type forKey:@"CPComparisonPredicateType"];
+ [coder encodeInt:_options forKey:@"CPComparisonPredicateOptions"];
+ [coder encodeObject:_customSelector forKey:@"CPComparisonPredicateCustomSelector"];
+}
+
+@end
+
+var source = ['*','?','(',')','{','}','.','+','|','/','$','^'];
+var dest = ['.*','.?','\\(','\\)','\\{','\\}','\\.','\\+','\\|','\\/','\\$','\\^'];
+
+String.prototype.escapeForRegExp = function()
+{
+ var foundChar = false;
+ for (var i = 0; i < source.length; ++i)
+ {
+ if (this.indexOf(source[i]) !== -1)
+ {
+ foundChar = true;
+ break;
+ }
+ }
+
+ if (!foundChar)
+ return this;
+
+ var result = "",
+ sourceIndex;
+ for (var i = 0; i < this.length; ++i)
+ {
+ var sourceIndex = source.indexOf(this.charAt(i));
+ if (sourceIndex !== -1)
+ result += dest[sourceIndex];
+ else
+ result += this.charAt(i);
+ }
+
+ return result;
+}
diff --git a/Foundation/CPPredicate/CPCompoundPredicate.j b/Foundation/CPPredicate/CPCompoundPredicate.j
new file mode 100644
index 000000000..55cd1ed0b
--- /dev/null
+++ b/Foundation/CPPredicate/CPCompoundPredicate.j
@@ -0,0 +1,227 @@
+@import "CPPredicate.j"
+@import
+@import
+
+/*!
+ A predicate to compare directly the left and right hand sides.
+ @global
+ @class CPCompoundPredicate
+*/
+CPNotPredicateType = 0;
+/*!
+ A predicate to compare directly the left and right hand sides.
+ @global
+ @class CPCompoundPredicate
+*/
+CPAndPredicateType = 1;
+/*!
+ A predicate to compare directly the left and right hand sides.
+ @global
+ @class CPCompoundPredicate
+*/
+CPOrPredicateType = 2;
+
+var CPCompoundPredicateType;
+
+/*!
+ @class CPCompoundPredicate
+ @ingroup foundation
+ @brief CPCompoundPredicate is a subclass of CPPredicate used to represent logical “gate” operations (AND/OR/NOT) and comparison operations.
+
+ Comparison operations are based on two expressions, as represented by instances of the CPExpression class. Expressions are created for constant values, key paths, and so on.
+
+ A compound predicate with 0 elements evaluates to TRUE, and a compound predicate with a single sub-predicate evaluates to the truth of its sole subpredicate.
+*/
+@implementation CPCompoundPredicate : CPPredicate
+{
+ CPCompoundPredicateType _type;
+ CPArray _predicates;
+}
+
+// Constructors
+/*!
+ Returns the receiver initialized to a given type using predicates from a given array.
+ @param type The type of the new predicate.
+ @return The receiver initialized with its type set to type and subpredicates array to subpredicates.
+*/
+- (id)initWithType:(CPCompoundPredicateType)type subpredicates:(CPArray)predicates
+{
+ _type = type;
+ _predicates = predicates;
+
+ return self;
+}
+
+/*!
+ Returns a new predicate formed by NOT-ing the predicates in a given array.
+ @param subpredicates An array of CPPredicate objects.
+ @return A new predicate formed by NOT-ing the predicates specified by subpredicates.
+*/
++ (CPPredicate)notPredicateWithSubpredicate:(CPPredicate)predicate
+{
+ return [[self alloc] initWithType:CPNotPredicateType subpredicates:[CPArray arrayWithObject:predicate]];
+}
+
+/*!
+ Returns a new predicate formed by AND-ing the predicates in a given array.
+ @param subpredicates An array of CPPredicate objects.
+ @return A new predicate formed by AND-ing the predicates specified by subpredicates.
+*/
++ (CPPredicate)andPredicateWithSubpredicates:(CPArray)subpredicates
+{
+ return [[self alloc] initWithType:CPAndPredicateType subpredicates:subpredicates];
+}
+
+/*!
+ Returns a new predicate formed by OR-ing the predicates in a given array.
+ @param subpredicates An array of CPPredicate objects.
+ @return A new predicate formed by OR-ing the predicates specified by subpredicates.
+*/
++ (CPPredicate)orPredicateWithSubpredicates:(CPArray)predicates
+{
+ return [[self alloc] initWithType:CPOrPredicateType subpredicates:predicates];
+}
+
+// Getting Information About a Compound Predicate
+/*!
+ Returns the predicate type for the receiver.
+ @return The predicate type for the receiver.
+*/
+- (CPCompoundPredicateType)compoundPredicateType
+{
+ return _type;
+}
+
+/*!
+ Returns the array of the receiver’s subpredicates.
+ @return The array of the receiver’s subpredicates.
+*/
+- (CPArray)subpredicates
+{
+ return _predicates;
+}
+
+- (CPPredicate)predicateWithSubstitutionVariables:(CPDictionary)variables
+{
+ var subp = [CPArray array],
+ count = [subp count];
+ i;
+
+ for (i = 0; i < count; i++)
+ {
+ var p = [subp objectAtIndex:i],
+ sp = [p predicateWithSubstitutionVariables:variables];
+
+ [subp addObject:sp];
+ }
+
+ return [[CPCompoundPredicate alloc] initWithType:_type subpredicates:subp];
+}
+
+- (CPString)predicateFormat
+{
+ var result = "",
+ args = [CPArray array],
+ count = [_predicates count],
+ i;
+
+ if (count == 0)
+ return @"TRUPREDICATE";
+
+ for (i = 0; i < count; i++)
+ {
+ var subpredicate = [_predicates objectAtIndex:i],
+ precedence = [subpredicate predicateFormat];
+
+ if ([subpredicate isKindOfClass:[CPCompoundPredicate class]] && [[subpredicate subpredicates] count]> 1 && [subpredicate compoundPredicateType] != _type)
+ precedence = [CPString stringWithFormat:@"(%s)",precedence];
+
+ if (precedence != nil)
+ [args addObject:precedence];
+ }
+
+ switch (_type)
+ {
+ case CPNotPredicateType:
+ result += "NOT %s" + [args objectAtIndex:0];
+ break;
+ case CPAndPredicateType:
+ result += [args objectAtIndex:0];
+ var count = [args count];
+ for (var j = 1; j < count; j++)
+ result += " AND " + [args objectAtIndex:j];
+ break;
+ case CPOrPredicateType:
+ result += [args objectAtIndex:0];
+ var count = [args count];
+ for (var j = 1; j < count; j++)
+ result += " OR " + [args objectAtIndex:j];
+ break;
+ }
+
+ return result;
+}
+
+- (BOOL)evaluateWithObject:(id)object
+{
+ return [self evaluateWithObject:object substitutionVariables:nil];
+}
+
+- (BOOL)evaluateWithObject:(id)object substitutionVariables:(CPDictionary)variables
+{
+ var result = NO,
+ count = [_predicates count],
+ i;
+
+ if (count == 0)
+ return YES;
+
+ for (i = 0; i < count; i++)
+ {
+ var predicate = [_predicates objectAtIndex:i];
+
+ switch (_type)
+ {
+ case CPNotPredicateType:
+ return ![predicate evaluateWithObject:object substitutionVariables:variables];
+ case CPAndPredicateType:
+ if (i == 0)
+ result = [predicate evaluateWithObject:object substitutionVariables:variables];
+ else
+ result = result && [predicate evaluateWithObject:object substitutionVariables:variables];
+ if (!result)
+ return NO;
+ break;
+ case CPOrPredicateType:
+ if ([predicate evaluateWithObject:object substitutionVariables:variables])
+ return YES;
+ break;
+ }
+ }
+
+ return result;
+}
+
+@end
+
+@implementation CPCompoundPredicate (CPCoding)
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ self = [super init];
+ if (self != nil)
+ {
+ _predicates = [coder decodeObjectForKey:@"CPCompoundPredicateSubpredicates"];
+ _type = [coder decodeIntForKey:@"CPCompoundPredicateType"];
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:_predicates forKey:@"CPCompoundPredicateSubpredicates"];
+ [coder encodeInt:_type forKey:@"CPCompoundPredicateType"];
+}
+
+@end
diff --git a/Foundation/CPPredicate/CPExpression.j b/Foundation/CPPredicate/CPExpression.j
new file mode 100644
index 000000000..64b43375b
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression.j
@@ -0,0 +1,324 @@
+@import
+@import
+@import
+@import
+@import
+
+/*!
+ An expression that always returns the same value.
+*/
+CPConstantValueExpressionType = 0;
+/*!
+ An expression that always returns the parameter object itself.
+*/
+CPEvaluatedObjectExpressionType = 1;
+/*!
+ An expression that always returns whatever value is associated with the key specified by ‘variable’ in the bindings dictionary.
+*/
+CPVariableExpressionType = 2;
+/*!
+ An expression that returns something that can be used as a key path.
+*/
+CPKeyPathExpressionType = 3;
+/*!
+ An expression that returns the result of evaluating a function.
+*/
+CPFunctionExpressionType = 4;
+/*!
+ An expression that defines an aggregate of NSExpression objects.
+*/
+CPAggregateExpressionType = 5;
+/*!
+ An expression that filters a collection using a subpredicate.
+*/
+CPSubqueryExpressionType = 6;
+/*!
+ An expression that creates a union of the results of two nested expressions.
+*/
+CPUnionSetExpressionType = 7;
+/*!
+ An expression that creates an intersection of the results of two nested expressions.
+*/
+CPIntersectSetExpressionType = 8;
+/*!
+ An expression that combines two nested expression results by set subtraction.
+*/
+CPMinusSetExpressionType = 9;
+
+/*!
+ @ingroup foundation
+ @class CPExpression
+ @brief CPExpression is used to represent expressions in a predicate.
+
+ Comparison operations in an CPPredicate are based on two expressions, as represented by instances of the CPExpression class. Expressions are created for constant values, key paths, and so on.
+
+ Generally, anywhere in the CPExpression class hierarchy where there is composite API and subtypes that may only reasonably respond to a subset of that API, invoking a method that does not make sense for that subtype will cause an exception to be thrown.
+*/
+
+@implementation CPExpression : CPObject
+{
+ int _type;
+}
+
+// Initializing an Expression
+/*!
+ Initializes the receiver with the specified expression type.
+ @param type The type of the new expression, as defined by CPExpressionType.
+ @return An initialized CPExpression object of the type type.
+*/
+- (id)initWithExpressionType:(int)type
+{
+ _type = type;
+
+ return self;
+}
+
+//Creating an Expression for a Value
+/*!
+ Returns a new expression that represents a given constant value.
+ @param value The constant value the new expression is to represent.
+ @return A new expression that represents the constant value.
+*/
++ (CPExpression)expressionForConstantValue:(id)value
+{
+ return [[CPExpression_constant alloc] initWithValue:value];
+}
+
+/*!
+ Returns a new expression that represents the object being evaluated.
+ @return A new expression that represents the object being evaluated.
+*/
++ (CPExpression)expressionForEvaluatedObject
+{
+ return [[CPExpression_self alloc] init];
+}
+
+/*!
+ Returns a new expression that extracts a value from the variable bindings dictionary for a given key.
+ @param string The key for the variable to extract from the variable bindings dictionary.
+ @return A new expression that extracts from the variable bindings dictionary the value for the key string.
+*/
++ (CPExpression)expressionForVariable:(CPString)string
+{
+ return [[CPExpression_variable alloc] initWithVariable:string];
+}
+
+/*!
+ Returns a new expression that invokes valueForKeyPath: with a given key path.
+ @param keyPath The key path that the new expression should evaluate.
+ @return A new expression that invokes valueForKeyPath: with keyPath.
+*/
++ (CPExpression)expressionForKeyPath:(CPString)keyPath
+{
+ return [[CPExpression_keypath alloc] initWithKeyPath:keyPath];
+}
+
+//Creating a Collection Expression
+/*!
+ Returns a new aggregate expression for a given collection.
+ @param collection A collection object (an instance of CPArray, CPSet, or CPDictionary) that contains further expressions.
+ @return A new expression that contains the expressions in collection.
+*/
++ (CPExpression)expressionForAggregate:(CPArray)collection
+{
+ return [[CPExpression_aggregate alloc] initWithAggregate:collection];
+}
+
+/*!
+ Returns a new CPExpression object that represent the union of a given set and collection.
+ @param left An expression that evaluates to an CPSet object.
+ @param right An expression that evaluates to a collection object (an instance of CPArray, CPSet, or CPDictionary).
+ @return A new CPExpression object that represents the union of left and right.
+*/
++ (CPExpression)expressionForUnionSet:(CPExpression)left with:(CPExpression)right
+{
+ return [[CPExpression_unionset alloc] initWithLeft:left right:right];
+}
+
+/*!
+ Returns a new CPExpression object that represent the intersection of a given set and collection.
+ @param left An expression that evaluates to an CPSet object.
+ @param right An expression that evaluates to a collection object (an instance of CPArray, CPSet, or CPDictionary).
+ @return A new CPExpression object that represents the intersection of left and right.
+*/
++ (CPExpression)expressionForIntersectSet:(CPExpression)left with:(CPExpression)right
+{
+ return [[CPExpression_intersectset alloc] initWithLeft:left right:right];
+}
+
+/*!
+ Returns a new CPExpression object that represent the subtraction of a given collection from a given set.
+ @param left An expression that evaluates to an CPSet object.
+ @param left An expression that evaluates to a collection object (an instance of CPArray, CPSet, or CPDictionary).
+ @return A new CPExpression object that represents the subtraction of right from left.
+*/
++ (CPExpression)expressionForMinusSet:(CPExpression)left with:(CPExpression)right
+{
+ return [[CPExpression_minusset alloc] initWithLeft:left right:right];
+}
+
+// Creating an Expression for a Function
+/*!
+ Returns a new expression that will invoke one of the predefined functions.
+ @param function_name The name of the function to invoke.
+ @param parameters An array containing NSExpression objects that will be used as parameters during the invocation of selector.
+
+ For a selector taking no parameters, the array should be empty. For a selector taking one or more parameters, the array should contain one NSExpression object which will evaluate to an instance of the appropriate type for each parameter.
+
+ If there is a mismatch between the number of parameters expected and the number you provide during evaluation, an exception may be raised or missing parameters may simply be replaced by nil (which occurs depends on how many parameters are provided, and whether you have over- or underflow).
+ @return A new expression that invokes the function name using the parameters in parameters.
+
+ The name parameter can be one of the following predefined functions:
+ @verbatim
+ name parameter array contents returns
+ -------------------------------------------------------------------------------------------------------------------------------------
+ sum: CPExpression instances representing numbers CPNumber
+ count: CPExpression instances representing numbers CPNumber
+ min: CPExpression instances representing numbers CPNumber
+ max: CPExpression instances representing numbers CPNumber
+ average: CPExpression instances representing numbers CPNumber
+ median: CPExpression instances representing numbers CPNumber
+ mode: CPExpression instances representing numbers CPArray (returned array will contain all occurrences of the mode)
+ stddev: CPExpression instances representing numbers CPNumber
+ add:to: CPExpression instances representing numbers CPNumber
+ from:subtract: two CPExpression instances representing numbers CPNumber
+ multiply:by: two CPExpression instances representing numbers CPNumber
+ divide:by: two CPExpression instances representing numbers CPNumber
+ modulus:by: two CPExpression instances representing numbers CPNumber
+ sqrt: one CPExpression instance representing numbers CPNumber
+ log: one CPExpression instance representing a number CPNumber
+ ln: one CPExpression instance representing a number CPNumber
+ raise:toPower: one CPExpression instance representing a number CPNumber
+ exp: one CPExpression instance representing a number CPNumber
+ floor: one CPExpression instance representing a number CPNumber
+ ceiling: one CPExpression instance representing a number CPNumber
+ abs: one CPExpression instance representing a number CPNumber
+ trunc: one CPExpression instance representing a number CPNumber
+ uppercase: one CPExpression instance representing a string CPString
+ lowercase: one CPExpression instance representing a string CPString
+ random none CPNumber (integer)
+ random: one CPExpression instance representing a number CPNumber (integer) such that 0 <= rand < param
+ now none [CPDate now]
+ bitwiseAnd:with: two CPExpression instances representing numbers CPNumber (numbers will be treated as CPInteger)
+ bitwiseOr:with: two CPExpression instances representing numbers CPNumber (numbers will be treated as CPInteger)
+ bitwiseXor:with: two CPExpression instances representing numbers CPNumber (numbers will be treated as CPInteger)
+ leftshift:by: two CPExpression instances representing numbers CPNumber (numbers will be treated as CPInteger)
+ rightshift:by: two CPExpression instances representing numbers CPNumber (numbers will be treated as CPInteger)
+ onesComplement: one CPExpression instance representing a numbers CPNumber (numbers will be treated as CPInteger)
+ @endverbatim
+
+ This method raises an exception immediately if the selector is invalid; it raises an exception at runtime if the parameters are incorrect.
+*/
++ (CPExpression)expressionForFunction:(CPString)function_name arguments:(CPArray)parameters
+{
+ return [[CPExpression_function alloc] initWithSelector:CPSelectorFromString(function_name) arguments:parameters];
+}
+
+/*!
+ Returns an expression which will return the result of invoking on a given target a selector with a given name using given arguments.
+ @param target A CPExpression object which will evaluate an object on which the selector identified by name may be invoked.
+ @param function_name The name of the method to be invoked.
+ @param parameters An array containing CPExpression objects which can be evaluated to provide parameters for the method specified by name.
+ @return An expression which will return the result of invoking the selector named name on the result of evaluating the target expression with the parameters specified by evaluating the elements of parameters.
+ See the description of expressionForFunction:arguments: for examples of how to construct the parameter array.
+*/
++ (CPExpression)expressionForFunction:(CPExpression)target selectorName:(CPString)function_name arguments:(CPArray)parameters
+{
+ return [[CPExpression_function alloc] initWithTarget:target selector:CPSelectorFromString(function_name) arguments:parameters];
+}
+
+
++ (CPExpression)expressionForSubquery:(CPExpression)expression usingIteratorVariable:(CPString)variable predicate:(id)predicate
+{
+ return nil; // UNIMPLEMENTED
+}
+
+
+// Getting Information About an Expression
+/*!
+ Returns the expression type for the receiver.
+ @return The expression type for the receiver.
+ This method raises an exception if it is not applicable to the receiver.
+*/
+- (int)expressionType
+{
+ return _type;
+}
+
+/*!
+ Returns the constant value of the receiver.
+ @return The constant value of the receiver.
+ This method raises an exception if it is not applicable to the receiver.
+*/
+- (id)constantValue
+{
+ [CPException raise:CPInvalidArgumentException reason:@"self is not of CPConstantValueExpressionType"];
+ return nil;
+}
+
+/*!
+ Returns the variable for the receiver.
+ @return The variable for the receiver.
+ This method raises an exception if it is not applicable to the receiver.
+*/
+- (CPString)variable
+{
+ [CPException raise:CPInvalidArgumentException reason:@"self is not of CPVariableExpressionType"];
+ return nil;
+}
+
+/*!
+ Returns the key path for the receiver.
+ @return The key path for the receiver.
+ This method raises an exception if it is not applicable to the receiver.
+*/
+- (CPString)keyPath
+{
+ [CPException raise:CPInvalidArgumentException reason:@"self is not of CPKeyPathExpressionType"];
+ return nil;
+}
+
+/*!
+ Returns the function for the receiver.
+ @return The function for the receiver.
+ This method raises an exception if it is not applicable to the receiver.
+*/
+- (CPString)function
+{
+ [CPException raise:CPInvalidArgumentException reason:@"self is not of CPFunctionExpressionType"];
+ return nil;
+}
+
+/*!
+ Returns the arguments for the receiver.
+ @return The arguments for the receiver—that is, the array of expressions that will be passed as parameters during invocation of the selector on the operand of a function expression.
+ This method raises an exception if it is not applicable to the receiver.
+*/
+- (CPArray)arguments
+{
+ [CPException raise:CPInvalidArgumentException reason:@"self is not of CPFunctionExpressionType"];
+ return nil;
+}
+
+/*!
+ Returns the collection of expressions in an aggregate expression, or the collection element of a subquery expression.
+ @return Returns the collection of expressions in an aggregate expression, or the collection element of a subquery expression.
+ This method raises an exception if it is not applicable to the receiver.
+*/
+- (id)collection
+{
+ [CPException raise:CPInvalidArgumentException reason:@"self is not of CPAggregateExpressionType"];
+ return nil;
+}
+
+@end
+
+@import "CPExpression_constant.j"
+@import "CPExpression_self.j"
+@import "CPExpression_variable.j"
+@import "CPExpression_keypath.j"
+@import "CPExpression_function.j"
+@import "CPExpression_aggregate.j"
+@import "CPExpression_unionset.j"
+@import "CPExpression_intersectset.j"
+@import "CPExpression_minusset.j"
diff --git a/Foundation/CPPredicate/CPExpression_aggregate.j b/Foundation/CPPredicate/CPExpression_aggregate.j
new file mode 100644
index 000000000..90b25c8ec
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_aggregate.j
@@ -0,0 +1,107 @@
+
+@import "CPExpression.j"
+@import
+@import
+
+@implementation CPExpression_aggregate : CPExpression
+{
+ CPArray _aggregate;
+}
+
+- (id)initWithAggregate:(CPArray)collection
+{
+ [super initWithExpressionType:CPAggregateExpressionType];
+ _aggregate = collection;
+ return self;
+}
+
++ (CPExpression)expressionForAggregate:(CPArray)collection
+{
+ return [[self alloc] initWithAggregate:collection];
+}
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ var aggregate = [coder decodeObjectForKey:@"CPExpressionAggregate"];
+ return [self initWithAggregate:aggregate];
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:_aggregate forKey:@"CPExpressionAggregate"]; // subexpressions must be CPCoding compliant.
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if (self == object)
+ return YES;
+
+ if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object collection] isEqual:[self collection]])
+ return NO;
+
+ return YES;
+}
+
+- (id)collection
+{
+ return _aggregate;
+}
+
+- (CPExpression)rightExpression
+{
+ if ([_aggregate count] > 0)
+ return [_aggregate lastObject];
+
+ return nil;
+}
+
+- (CPExpression)leftExpression
+{
+ if ([_aggregate count] > 0)
+ return [_aggregate objectAtIndex:0];
+
+ return nil;
+}
+
+- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
+{
+ var eval_array = [CPArray array],
+ collection = [_aggregate objectEnumerator],
+ exp;
+
+ while (exp = [collection nextObject])
+ {
+ var eval = [exp expressionValueWithObject:object context:context];
+ if (eval != nil)[eval_array addObject:eval];
+ }
+
+ return eval_array;
+}
+
+- (CPString)description
+{
+ var i,
+ count = [_aggregate count],
+ result = "{";
+
+ for (i = 0; i < count; i++)
+ result = result + [CPString stringWithFormat:@"%s%s", [[_aggregate objectAtIndex:i] description], (i + 1 < count) ? @", " : @""];
+
+ result = result + "}";
+
+ return result;
+}
+
+- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
+{
+ var subst_array = [CPArray array],
+ count = [_aggregate count],
+ i;
+
+ for (i = 0; i < count; i++)
+ [subst_array addObject:[[_aggregate objectAtIndex:i] _expressionWithSubstitutionVariables:variables]];
+
+ return [CPExpression expressionForAggregate:subst_array];
+}
+
+@end
diff --git a/Foundation/CPPredicate/CPExpression_assignment.j b/Foundation/CPPredicate/CPExpression_assignment.j
new file mode 100644
index 000000000..771119fd4
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_assignment.j
@@ -0,0 +1,92 @@
+
+@import "CPExpression.j"
+@import "CPExpression_variable.j"
+@import
+
+
+@implementation CPExpression_assignment: CPExpression
+{
+ CPExpression_variable _assignmentVariable;
+ CPExpression _subexpression;
+}
+
+- (id)initWithAssignmentVariable:(CPString)variable expression:(CPExpression)expression
+{
+ _assignmentVariable = [CPExpression expressionForVariable:variable];
+ _subexpression = expression;
+
+ return self;
+}
+
+- (id)initWithAssignmentExpression:(CPExpression)variableExpression expression:(CPExpression)expression
+{
+ _assignmentVariable = variableExpression;
+ _subexpression = expression;
+
+ return self;
+}
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ var variable = [coder decodeObjectForKey:@"CPExpressionAssignmentVariable"];
+ var expression = [coder decodeObjectForKey:@"CPExpressionAssignmentExpression"];
+
+ return [self initWithAssignmentVariable:variable expression:expression];
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:_assignmentVariable forKey:@"CPExpressionAssignmentVariable"];
+ [coder encodeObject:_subexpression forKey:@"CPExpressionAssignmentExpression"];
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if (self == object)
+ return YES;
+
+ if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object subexpression] isEqual:[self subexpression]] || ![[object variable] isEqualToString:[self variable]])
+ return NO;
+
+ return YES;
+}
+
+- (CPExpression)assignmentVariable
+{
+ return _assignmentVariable;
+}
+
+- (CPExpression)subexpression
+{
+ return _subexpression;
+}
+
+- (CPString)variable
+{
+ return [_assignmentVariable variable];
+}
+
+- (CPString)description
+{
+ var pretty = [_expression description];
+
+ if ([_subexpression isKindOfClass:[CPExpression_operator class]])
+ pretty = [CPString stringWithFormat:@"(%@)", pretty];
+
+ return [CPString stringWithFormat:@"%@ := %@", [self variable], pretty];
+}
+
+- (id)expressionValueWithObject:(id)object context:(id)context
+{
+ // UNIMPLEMENTED
+ return nil;
+}
+
+- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
+{
+ // UNIMPLEMENTED
+ return nil;
+}
+
+@end
+
diff --git a/Foundation/CPPredicate/CPExpression_constant.j b/Foundation/CPPredicate/CPExpression_constant.j
new file mode 100644
index 000000000..af6ed19d4
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_constant.j
@@ -0,0 +1,65 @@
+
+@import "CPExpression.j"
+@import
+
+@implementation CPExpression_constant : CPExpression
+{
+ id _value;
+}
+
+- (id)initWithValue:(id)value
+{
+ [super initWithExpressionType:CPConstantValueExpressionType];
+ _value = value;
+
+ return self;
+}
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ var value = [coder decodeObjectForKey:@"CPExpressionConstantValue"];
+
+ return [self initWithValue:value];
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:_value forKey:@"CPExpressionConstantValue"];
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if (self == object)
+ return YES;
+
+ if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object constantValue] isEqual:[self constantValue]])
+ return NO;
+
+ return YES;
+}
+
+- (id)constantValue
+{
+ return _value;
+}
+
+- (id)expressionValueWithObject:object context:(CPDictionary)context
+{
+ return _value;
+}
+
+- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
+{
+ return self;
+}
+
+- (CPString)description
+{
+ if ([_value isKindOfClass:[CPString class]])
+ return @"\"" + _value + @"\"";
+
+ return [_value description];
+}
+
+@end
+
diff --git a/Foundation/CPPredicate/CPExpression_function.j b/Foundation/CPPredicate/CPExpression_function.j
new file mode 100644
index 000000000..539f947f0
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_function.j
@@ -0,0 +1,373 @@
+
+@import "CPExpression.j"
+@import
+@import
+@import
+
+@implementation CPExpression_function : CPExpression
+{
+ CPExpression _operand;
+ SEL _selector;
+ CPArray _arguments;
+ int _argc;
+}
+
+- (id)initWithSelector:(SEL)aselector arguments:(CPArray)parameters
+{
+ [super initWithExpressionType:CPFunctionExpressionType];
+
+ if (![self respondsToSelector:aselector])
+ [CPException raise: CPInvalidArgumentException reason:@"Unknown function implementation: " + aselector];
+
+ _selector = aselector;
+ _operand = nil;
+ _arguments = parameters;
+ _argc = [parameters count];
+
+ return self;
+}
+
+- (id)initWithTarget:(CPExpression)targetExpression selector:(SEL)aselector arguments:(CPArray)parameters
+{
+ [super initWithExpressionType:CPFunctionExpressionType];
+
+ var target = [targetExpression expressionValueWithObject:object context:context];
+ if (![target respondsToSelector:aselector])
+ [CPException raise: CPInvalidArgumentException reason:@"Unknown function implementation: " + aselector];
+
+ _selector = aselector;
+ _operand = targetExpression;
+ _arguments = parameters;
+ _argc = [parameters count];
+
+ return self;
+}
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ var selector = CPSelectorFromString([coder decodeObjectForKey:@"CPExpressionFunctionName"]);
+ var arguments = [coder decodeObjectForKey:@"CPExpressionFunctionArguments"];
+
+ return [self initWithSelector:selector arguments:arguments];
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:[self _function] forKey:@"CPExpressionFunctionName"];
+ [coder encodeObject:_arguments forKey:@"CPExpressionArguments"];
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if (self == object)
+ return YES;
+
+ if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object _function] isEqualToString:[self _function]] || ![[object operand] isEqual:[self operand]] || ![[object arguments] isEqualToArray:[self arguments]])
+ return NO;
+
+ return YES;
+}
+
+- (CPString)_function // OBJJ preprocessor does not like function as a method
+{
+ return CPStringFromSelector(_selector);
+}
+
+- (CPString)function
+{
+ return [self _function];
+}
+
+- (CPArray)arguments
+{
+ return _arguments;
+}
+
+- (CPExpression)operand
+{
+ return _operand;
+}
+
+- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
+{
+ var eval_args = [CPArray array],
+ i;
+
+ for (i = 0; i < _argc; i++)
+ {
+ var arg = [[_arguments objectAtIndex:i] expressionValueWithObject:object context:context];
+ if (arg != nil)
+ [eval_args addObject:arg];
+ }
+
+ var target = (_operand == nil) ? self : [_operand expressionValueWithObject:object context:context];
+ return [target performSelector:_selector withObject:eval_args];
+}
+
+- (CPString)description
+{
+ var result = [CPString stringWithFormat:@"%@ %s(", [_operand description], [self _function]],
+ i;
+
+ for (i = 0; i < _argc; i++)
+ result = result + [_arguments objectAtIndex:i] + (i+1<_argc) ? ", " : "";
+
+ result = result + ")";
+
+ return result ;
+}
+
+
+- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
+{
+ var array = [CPArray array],
+ i;
+
+ for (i = 0; i < _argc; i++)
+ [array addObject:[[_arguments objectAtIndex:i] _expressionWithSubstitutionVariables:variables]];
+
+ return [CPExpression expressionForFunction:[self operand] selectorName:[self _function] arguments:array];
+}
+
+- (CPNumber)sum:(CPArray)parameters
+{
+ if (_argc < 1)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var i,
+ sum = 0.0;
+
+ for (i = 0; i < _argc; i++)
+ sum += [[parameters objectAtIndex:i] doubleValue];
+
+ return [CPNumber numberWithDouble: sum];
+}
+
+- (CPNumber)count:(CPArray)parameters
+{
+ if (_argc < 1)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ return [CPNumber numberWithUnsignedInt: [[parameters objectAtIndex:0] count]];
+}
+
+- (CPNumber)min:(CPArray)parameters
+{
+ if (_argc < 1)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ return MIN([parameters objectAtIndex:0],[parameters objectAtIndex:1]);
+}
+
+- (CPNumber)max:(CPArray)parameters
+{
+ if (_argc < 1)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ return MAX([parameters objectAtIndex:0],[parameters objectAtIndex:1]);
+}
+
+- (CPNumber)average:(CPArray)parameters
+{
+ if (_argc < 1)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var i,
+ sum = 0.0;
+
+ for (i = 0; i < _argc; i++)
+ sum += [[parameters objectAtIndex:i] doubleValue];
+
+ return [CPNumber numberWithDouble: sum / _argc];
+}
+
+- (CPNumber)add:to:(CPArray)parameters
+{
+ if (_argc != 2)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var left = [parameters objectAtIndex:0],
+ right = [parameters objectAtIndex:1];
+
+ return [CPNumber numberWithDouble: [left doubleValue] + [right doubleValue]];
+}
+
+- (CPNumber)from:subtract:(CPArray)parameters
+{
+ if (_argc != 2)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var left = [parameters objectAtIndex:0],
+ right = [parameters objectAtIndex:1];
+
+ return [CPNumber numberWithDouble: [left doubleValue] - [right doubleValue]];
+}
+
+- (CPNumber)multiply:by:(CPArray)parameters
+{
+ if (_argc != 2)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var left = [parameters objectAtIndex:0],
+ right = [parameters objectAtIndex:1];
+
+ return [CPNumber numberWithDouble: [left doubleValue] * [right doubleValue]];
+}
+
+- (CPNumber)divide:by:(CPArray)parameters
+{
+ if (_argc != 2)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var left = [parameters objectAtIndex:0],
+ right = [parameters objectAtIndex:1];
+
+ return [CPNumber numberWithDouble: [left doubleValue] / [right doubleValue]];
+}
+
+- (CPNumber)sqrt:(CPArray)parameters
+{
+ if (_argc != 1)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var num = [[parameters objectAtIndex:0] doubleValue];
+
+ return [CPNumber numberWithDouble: SQRT(num)];
+}
+
+- (CPNumber)raise:to:(CPArray)parameters
+{
+ if (_argc < 2)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var num = [[parameters objectAtIndex:0] doubleValue],
+ power = [[parameters objectAtIndex:1] doubleValue];
+
+ return [CPNumber numberWithDouble: POW(num,power)];
+}
+
+- (CPNumber)abs:(CPArray)parameters
+{
+ if (_argc != 1)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var num = [[parameters objectAtIndex:0] doubleValue];
+
+ return [CPNumber numberWithDouble:ABS(num)];
+}
+
+- (CPDate)now
+{
+ return [CPDate date];
+}
+
+- (CPNumber)ln:(CPArray)parameters
+{
+ if (_argc != 1)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var num = [[parameters objectAtIndex:0] doubleValue];
+
+ return [CPNumber numberWithDouble:Math.log(num)];
+}
+
+- (CPNumber)exp:(CPArray)parameters
+{
+ if (_argc != 1)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var num = [[parameters objectAtIndex:0] doubleValue];
+
+ return [CPNumber numberWithDouble:EXP(num)];
+}
+
+- (CPNumber)ceiling:(CPArray)parameters
+{
+ if (_argc != 1)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var num = [[parameters objectAtIndex:0] doubleValue];
+
+ return [CPNumber numberWithDouble:CEIL(num)];
+}
+
+- (CPNumber)random
+{
+ return [CPNumber numberWithDouble:RAND()];
+}
+
+- (CPNumber)modulus:by:(CPArray)parameters
+{
+ if (_argc != 2)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var left = [parameters objectAtIndex:0],
+ right = [parameters objectAtIndex:1];
+
+ return [CPNumber numberWithInt:([left intValue] % [right intValue])];
+}
+
+
+- (id)first:(CPArray)parameters
+{
+ if (_argc == 0)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ return [[parameters objectAtIndex:0] objectAtIndex:0];
+}
+
+- (id)last:(CPArray)parameters
+{
+ if (_argc == 0)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ return [[parameters objectAtIndex:0] lastObject];
+}
+
+- (CPNumber)chs:(CPArray)parameters
+{
+ if (_argc == 0)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ return [CPNumber numberWithInt: - [[parameters objectAtIndex:0] intValue]];
+}
+
+- (id)index:(CPArray)parameters
+{
+ if (_argc < 2)
+ [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
+
+ var left = [parameters objectAtIndex:0],
+ right = [parameters objectAtIndex:1];
+
+ if ([left isKindOfClass: [CPDictionary class]])
+ return [left objectForKey:right];
+ else
+ return [left objectAtIndex: [right intValue]];
+}
+
+/*
+- (CPNumber)median:(CPArray)parameters
+{
+}
+- (CPNumber)mode:(CPArray)parameters
+{
+}
+- (CPNumber)stddev:(CPArray)parameters
+{
+}
+- (CPNumber)log:(CPArray)parameters
+{
+}
+- (CPNumber)raise:to:(CPArray)parameters
+{
+}
+- (CPNumber)trunc:(CPArray)parameters
+{
+}
+
+// These functions are used when parsing
+*/
+
+@end
+
diff --git a/Foundation/CPPredicate/CPExpression_intersectset.j b/Foundation/CPPredicate/CPExpression_intersectset.j
new file mode 100644
index 000000000..f2f6f3a23
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_intersectset.j
@@ -0,0 +1,86 @@
+
+@import "CPExpression.j"
+
+@implementation CPExpression_intersectset : CPExpression
+{
+ CPExpression _left;
+ CPExpression _right;
+}
+
+- (id)initWithLeft:(CPExpression)left right:(CPExpression)right
+{
+ [super initWithExpressionType:CPIntersectSetExpressionType];
+ _left = left ;
+ _right = right;
+
+ return self;
+}
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ var left = [coder decodeObjectForKey:@"CPExpressionUnionSetLeftExpression"];
+ var right = [coder decodeObjectForKey:@"CPExpressionUnionSetRightExpression"];
+
+ return [self initWithLeft:left right:right];
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:_left forKey:@"CPExpressionUnionSetLeftExpression"];
+ [coder encodeObject:_right forKey:@"CPExpressionUnionSetRightExpression"];
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if (self == object)
+ return YES;
+
+ if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object leftExpression] isEqual:[self leftExpression]] || ![[object rightExpression] isEqual:[self rightExpression]])
+ return NO;
+
+ return YES;
+}
+
+- (id)expressionValueWithObject:object context:(CPDictionary)context
+{
+ var right = [_right expressionValueWithObject:object context:context];
+ if (![right respondsToSelector: @selector(objectEnumerator)])
+ [CPException raise:CPInvalidArgumentException reason:@"The right expression for a CPIntersectSetExpressionType expression must be either a CPArray, CPDictionary or CPSet"];
+
+ var left = [_left expressionValueWithObject:object context:context];
+ if (![left isKindOfClass:[CPSet set]])
+ [CPException raise:CPInvalidArgumentException reason:@"The left expression for a CPIntersectSetExpressionType expression must a CPSet"];
+
+ var set = [CPSet setWithSet:left],
+ e = [right objectEnumerator],
+ item;
+
+ while (item = [e nextObject])
+ if ([left containsObject:item])
+ [set addObject:item];
+
+ return [CPExpression expressionForConstantValue:set];
+}
+
+- (CPExpression )_expressionWithSubstitutionVariables:(CPDictionary )variables
+{
+ return self;
+}
+
+- (CPExpression)leftExpression
+{
+ return _left;
+}
+
+- (CPExpression)rightExpression
+{
+ return _right;
+}
+
+- (CPString )description
+{
+ return [_left description] +" INTERSECT "+ [_right description];
+}
+
+@end
+
diff --git a/Foundation/CPPredicate/CPExpression_keypath.j b/Foundation/CPPredicate/CPExpression_keypath.j
new file mode 100644
index 000000000..e74c77b71
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_keypath.j
@@ -0,0 +1,63 @@
+
+@import "CPExpression.j"
+@import
+@import
+
+@implementation CPExpression_keypath : CPExpression
+{
+ CPString _keyPath;
+}
+
+- (id)initWithKeyPath:(CPString)keyPath
+{
+ [super initWithExpressionType:CPKeyPathExpressionType];
+ _keyPath = keyPath ;
+
+ return self;
+}
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ var keyPath = [coder decodeObjectForKey:@"CPExpressionKeyPath"];
+
+ return [self initWithKeyPath:keyPath];
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:_keyPath forKey:@"CPExpressionKeyPath"];
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if (self == object)
+ return YES;
+
+ if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object keyPath] isEqualToString:[self keyPath]])
+ return NO;
+
+ return YES;
+}
+
+- (CPString)keyPath
+{
+ return _keyPath;
+}
+
+- (id)expressionValueWithObject:object context:(CPDictionary)context
+{
+ return [object valueForKeyPath:_keyPath];
+}
+
+- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
+{
+ return self;
+}
+
+- (CPString)description
+{
+ return _keyPath;
+}
+
+@end
+
diff --git a/Foundation/CPPredicate/CPExpression_minusset.j b/Foundation/CPPredicate/CPExpression_minusset.j
new file mode 100644
index 000000000..528580a9b
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_minusset.j
@@ -0,0 +1,81 @@
+
+@import "CPExpression.j"
+
+@implementation CPExpression_minusset : CPExpression
+
+- (id)initWithLeft:(CPExpression)left right:(CPExpression)right
+{
+ [super initWithExpressionType:CPMinusSetExpressionType];
+ _left = left ;
+ _right = right;
+
+ return self;
+}
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ var left = [coder decodeObjectForKey:@"CPExpressionMinusSetLeftExpression"];
+ var right = [coder decodeObjectForKey:@"CPExpressionMinusSetRightExpression"];
+
+ return [self initWithLeft:left right:right];
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:_left forKey:@"CPExpressionMinusSetLeftExpression"];
+ [coder encodeObject:_right forKey:@"CPExpressionMinusSetRightExpression"];
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if (self == object)
+ return YES;
+
+ if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object leftExpression] isEqual:[self leftExpression]] || ![[object rightExpression] isEqual:[self rightExpression]])
+ return NO;
+
+ return YES;
+}
+
+- (id)expressionValueWithObject:object context:(CPDictionary)context
+{
+ var right = [_right expressionValueWithObject:object context:context];
+ if (![right respondsToSelector: @selector(objectEnumerator)])
+ [CPException raise:CPInvalidArgumentException reason:@"The right expression for a CPIntersectSetExpressionType expression must be either a CPArray, CPDictionary or CPSet"];
+
+ var left = [_left expressionValueWithObject:object context:context];
+ if (![left isKindOfClass:[CPSet set]])
+ [CPException raise:CPInvalidArgumentException reason:@"The left expression for a CPIntersectSetExpressionType expression must a CPSet"];
+
+ var set = [CPSet setWithSet:left],
+ e = [right objectEnumerator],
+ item;
+
+ while (item = [e nextObject])
+ [set removeObject:item];
+
+ return [CPExpression expressionForConstantValue:set];
+}
+
+- (CPExpression )_expressionWithSubstitutionVariables:(CPDictionary )variables
+{
+ return self;
+}
+
+- (CPExpression)leftExpression
+{
+ return _left;
+}
+
+- (CPExpression)rightExpression
+{
+ return _right;
+}
+
+- (CPString )description
+{
+ return [_left description] +" MINUS "+ [_right description];
+}
+
+@end
+
diff --git a/Foundation/CPPredicate/CPExpression_operator.j b/Foundation/CPPredicate/CPExpression_operator.j
new file mode 100644
index 000000000..3ac7d6450
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_operator.j
@@ -0,0 +1,117 @@
+
+@import "CPExpression.j"
+@import
+@import
+@import
+
+var CPExpressionOperatorNegate = "CPExpressionOperatorNegate";
+var CPExpressionOperatorAdd = "CPExpressionOperatorAdd";
+var CPExpressionOperatorSubtract = "CPExpressionOperatorSubtract";
+var CPExpressionOperatorMultiply = "CPExpressionOperatorMultiply";
+var CPExpressionOperatorDivide = "CPExpressionOperatorDivide";
+var CPExpressionOperatorExp = "CPExpressionOperatorExp";
+var CPExpressionOperatorAssign = "CPExpressionOperatorAssign";
+var CPExpressionOperatorKeypath = "CPExpressionOperatorKeypath";
+var CPExpressionOperatorIndex = "CPExpressionOperatorIndex";
+var CPExpressionOperatorIndexFirst = "CPExpressionOperatorIndexFirst";
+var CPExpressionOperatorIndexLast = "CPExpressionOperatorIndexLast";
+var CPExpressionOperatorIndexSize = "CPExpressionOperatorIndexSize";
+
+@implementation CPExpression_operator : CPExpression
+{
+ int _operator;
+ CPArray _arguments;
+}
+
+- (id)initWithOperator:(int)operator arguments:(CPArray)arguments
+{
+ _operator = operator;
+ _arguments = arguments;
+ return self;
+}
+
++ (CPExpression)expressionForOperator:(CPExpressionOperator)operator arguments:(CPArray)arguments
+{
+ return [self initWithOperator:operator arguments:arguments];
+}
+
+- (CPArray)arguments
+{
+ return _arguments;
+}
+
+- (CPString)description
+{
+ var result = [CPString string],
+ args = [CPArray array],
+ count = [_arguments count],
+ i;
+
+ for (i = 0; i < count; i++)
+ {
+ var check = [_arguments objectAtIndex:i],
+ precedence = [check description];
+
+ if ([check isKindOfClass:[CPExpression_operator class]])
+ precedence = [CPString stringWithFormat:@"(%@)", precedence];
+
+ [args addObject:precedence];
+ }
+
+ switch (_operator)
+ {
+ case CPExpressionOperatorNegate :
+ result = result + [CPString stringWithFormat:@"-%@", [args objectAtIndex:0]];
+ break;
+ case CPExpressionOperatorAdd :
+ result = result + [CPString stringWithFormat:@"%@ + %@", [args objectAtIndex:0], [args objectAtIndex:1]];
+ break;
+ case CPExpressionOperatorSubtract :
+ result = result + [CPString stringWithFormat:@"%@ - %@", [args objectAtIndex:0], [args objectAtIndex:1]];
+ break;
+ case CPExpressionOperatorMultiply :
+ result = result + [CPString stringWithFormat:@"%@ * %@", [args objectAtIndex:0], [args objectAtIndex:1]];
+ break;
+ case CPExpressionOperatorDivide :
+ result = result + [CPString stringWithFormat:@"%@ / %@", [args objectAtIndex:0], [args objectAtIndex:1]];
+ break;
+ case CPExpressionOperatorExp :
+ result = result + [CPString stringWithFormat:@"%@ ** %@", [args objectAtIndex:0], [args objectAtIndex:1]];
+ break;
+ case CPExpressionOperatorAssign :
+ result = result + [CPString stringWithFormat:@"%@ := %@", [args objectAtIndex:0], [args objectAtIndex:1]];
+ break;
+ case CPExpressionOperatorKeypath :
+ result = result + [CPString stringWithFormat:@"%@.%@", [args objectAtIndex:0], [args objectAtIndex:1]];
+ break;
+ case CPExpressionOperatorIndex :
+ result = result + [CPString stringWithFormat:@"%@[%@]", [args objectAtIndex:0], [args objectAtIndex:1]];
+ break;
+ case CPExpressionOperatorIndexFirst :
+ result = result + [CPString stringWithFormat:@"%@[FIRST]", [args objectAtIndex:0]];
+ break;
+ case CPExpressionOperatorIndexLast :
+ result = result + [CPString stringWithFormat:@"%@[LAST]", [args objectAtIndex:0]];
+ break;
+ case CPExpressionOperatorIndexSize :
+ result = result + [CPString stringWithFormat:@"%@[SIZE]", [args objectAtIndex:0]];
+ break;
+ }
+
+ return result;
+}
+
+- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
+{
+ var array = [CPArray array],
+ count = [_arguments count],
+ i;
+
+ for (i = 0; i < count; i++)
+ [array addObject:[[_arguments objectAtIndex:i] _expressionWithSubstitutionVariables:variables]];
+
+ return [CPExpression_operator expressionForOperator:_operator arguments:array];
+}
+
+@end
+
diff --git a/Foundation/CPPredicate/CPExpression_self.j b/Foundation/CPPredicate/CPExpression_self.j
new file mode 100644
index 000000000..bfad66fe5
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_self.j
@@ -0,0 +1,46 @@
+
+@import "CPExpression.j"
+@import
+@import
+@import
+
+@implementation CPExpression_self : CPExpression{}
+
+- (id)init
+{
+ [super initWithExpressionType:CPEvaluatedObjectExpressionType];
+
+ return self;
+}
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ return [self init];
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+}
+
+- (BOOL)isEqual:(id)object
+{
+ return (object == self);
+}
+
+- (id)expressionValueWithObject:object context:(CPDictionary)context
+{
+ return object;
+}
+
+- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
+{
+ return self;
+}
+
+- (CPString)description
+{
+ return @"SELF";
+}
+
+@end
+
diff --git a/Foundation/CPPredicate/CPExpression_unionset.j b/Foundation/CPPredicate/CPExpression_unionset.j
new file mode 100644
index 000000000..45c9d137e
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_unionset.j
@@ -0,0 +1,84 @@
+
+@import "CPExpression.j"
+
+@implementation CPExpression_unionset : CPExpression
+
+- (id)initWithLeft:(CPExpression)left right:(CPExpression)right
+{
+ [super initWithExpressionType:CPUnionSetExpressionType];
+ _left = left;
+ _right = right;
+
+ return self;
+}
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ var left = [coder decodeObjectForKey:@"CPExpressionUnionSetLeftExpression"],
+ right = [coder decodeObjectForKey:@"CPExpressionUnionSetRightExpression"];
+
+ return [self initWithLeft:left right:right];
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:_left forKey:@"CPExpressionUnionSetLeftExpression"];
+ [coder encodeObject:_right forKey:@"CPExpressionUnionSetRightExpression"];
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if (self == object)
+ return YES;
+
+ if (object.isa != self.isa
+ || [object expressionType] != [self expressionType]
+ || ![[object leftExpression] isEqual:[self leftExpression]]
+ || ![[object rightExpression] isEqual:[self rightExpression]])
+ return NO;
+
+ return YES;
+}
+
+- (id)expressionValueWithObject:object context:(CPDictionary )context
+{
+ var right = [_right expressionValueWithObject:object context:context];
+ if (![right respondsToSelector: @selector(objectEnumerator)])
+ [CPException raise:CPInvalidArgumentException reason:@"The right expression for a CPIntersectSetExpressionType expression must be either a CPArray, CPDictionary or CPSet"];
+
+ var left = [_left expressionValueWithObject:object context:context];
+ if (![left isKindOfClass:[CPSet set]])
+ [CPException raise:CPInvalidArgumentException reason:@"The left expression for a CPIntersectSetExpressionType expression must a CPSet"];
+
+ var unionset = [CPSet setWithSet:left],
+ e = [right objectEnumerator],
+ item;
+
+ while (item = [e nextObject])
+ [unionset addObject:item];
+
+ return [CPExpression expressionForConstantValue:unionset];
+}
+
+- (CPExpression )_expressionWithSubstitutionVariables:(CPDictionary )variables
+{
+ return self;
+}
+
+- (CPExpression)leftExpression
+{
+ return _left;
+}
+
+- (CPExpression)rightExpression
+{
+ return _right;
+}
+
+- (CPString )description
+{
+ return [_left description] +" UNION "+ [_right description];
+}
+
+@end
+
diff --git a/Foundation/CPPredicate/CPExpression_variable.j b/Foundation/CPPredicate/CPExpression_variable.j
new file mode 100644
index 000000000..1b7db6b28
--- /dev/null
+++ b/Foundation/CPPredicate/CPExpression_variable.j
@@ -0,0 +1,68 @@
+
+@import "CPExpression.j"
+@import
+@import
+
+@implementation CPExpression_variable : CPExpression
+{
+ CPString _variable;
+}
+
+- (id)initWithVariable:(CPString)variable
+{
+ [super initWithExpressionType:CPVariableExpressionType];
+ _variable = [variable copy];
+
+ return self;
+}
+
+- (id)initWithCoder:(CPCoder)coder
+{
+ var variable = [coder decodeObjectForKey:@"CPExpressionVariable"];
+ return [self initWithVariable:variable];
+}
+
+- (void)encodeWithCoder:(CPCoder)coder
+{
+ [coder encodeObject:_variable forKey:@"CPExpressionVariable"];
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if (self == object)
+ return YES;
+
+ if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object variable] isEqualToString:[self variable]])
+ return NO;
+
+ return YES;
+}
+
+- (CPString)variable
+{
+ return _variable;
+}
+
+- (id)expressionValueWithObject:object context:(CPDictionary)context
+{
+ return [context objectForKey:_variable];
+}
+
+- (CPString)description
+{
+ return [CPString stringWithFormat:@"$%s", _variable];
+}
+
+- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
+{
+ var aconstant = [variables objectForKey:_variable];
+
+ if (aconstant != nil)
+ return [CPExpression expressionForConstantValue:aconstant];
+
+ return self;
+}
+
+
+@end
+
diff --git a/Foundation/CPPredicate/CPPredicate.j b/Foundation/CPPredicate/CPPredicate.j
new file mode 100644
index 000000000..ff5f5f092
--- /dev/null
+++ b/Foundation/CPPredicate/CPPredicate.j
@@ -0,0 +1,908 @@
+
+@import
+@import
+@import
+@import
+@import
+
+/*!
+ @ingroup foundation
+ @class CPPredicate
+ @brief The CPPredicate class is used to define logical conditions used to constrain a search either for a fetch or for in-memory filtering.
+
+ You use predicates to represent logical conditions, used for describing objects in persistent stores and in-memory filtering of objects. Although it is common to create predicates directly from instances of CPComparisonPredicate, CPCompoundPredicate, and CPExpression, you often create predicates from a format string which is parsed by the class methods on CPPredicate. Examples of predicate format strings include:
+
+ Simple comparisons, such as grade == "7" or firstName like "Shaffiq"\n
+ Case/diacritic insensitive lookups, such as name contains[cd] "itroen"\n
+ Logical operations, such as (firstName like "Mark") OR (lastName like "Adderley")\n
+ “Between” predicates such as date between {$YESTERDAY, $TOMORROW}.\n
+
+ You can create predicates for relationships, such as:
+
+ group.name like "work*"\n
+ ALL children.age > 12\n
+ ANY children.age > 12\n
+ You can create predicates for operations, such as @sum.items.price < 1000.
+
+ You can also create predicates that include variables, so that the predicate can be pre-defined before substituting concrete values at runtime with evaluateWithObject:substitutionVariables: method.
+*/
+
+@implementation CPPredicate : CPObject
+{
+}
+
+/*!
+ Creates and returns a new predicate formed by creating a new string with a given format and parsing the result.
+ @param format The format string for the new predicate.
+ @param … A comma-separated list of arguments to substitute into format.
+ @return A new predicate formed by creating a new string with format and parsing the result.
+*/
++ (CPPredicate)predicateWithFormat:(CPString) format, ...
+{
+ if (!format)
+ [CPException raise:CPInvalidArgumentException reason:_cmd + " the format can't be 'nil'"];
+
+ var args = Array.prototype.slice.call(arguments, 3);
+ return [self predicateWithFormat:arguments[2] argumentArray:args];
+}
+
+/*!
+ Creates and returns a new predicate by substituting the values in a given array into a format string and parsing the result.
+ @param format The format string for the new predicate.
+ @param arguments The arguments to substitute into predicateFormat. Values are substituted into predicateFormat in the order they appear in the array.
+ @return A new predicate by substituting the values in arguments into predicateFormat, and parsing the result.
+*/
++ (CPPredicate)predicateWithFormat:(CPString)format argumentArray:(CPArray)args
+{
+ if (!format)
+ [CPException raise:CPInvalidArgumentException reason:_cmd + " the format can't be 'nil'"];
+
+ var s = [[CPPredicateScanner alloc] initWithString:format args:args],
+ p = [s parse];
+
+ return p;
+}
+
+/*!
+ Creates and returns a new predicate by substituting the values in an argument list into a format string and parsing the result.
+ @param format The format string for the new predicate.
+ @param argList The arguments to substitute into predicateFormat. Values are substituted into predicateFormat in the order they appear in the argument list.
+ @return A new predicate by substituting the values in argList into predicateFormat and parsing the result.
+*/
++ (CPPredicate)predicateWithFormat:(CPString)format arguments:(va_list)argList
+{
+ // UNIMPLEMENTED
+ return nil;
+}
+
+/*!
+ Returns a copy of the receiver with the receiver’s variables substituted by values specified in a given substitution variables dictionary.
+ @param variables The substitution variables dictionary. The dictionary must contain key-value pairs for all variables in the receiver.
+ @return A copy of the receiver with the receiver’s variables substituted by values specified in variables.
+*/
+- (CPPredicate)predicateWithSubstitutionVariables:(CPDictionary)variables
+{
+ // IMPLEMENTED BY SUBCLASSES
+}
+
+/*!
+ Creates and returns a predicate that always evaluates to a given value.
+ @param value The value to which the new predicate should evaluate.
+ @return A predicate that always evaluates to value.
+*/
++ (CPPredicate)predicateWithValue:(BOOL)value
+{
+ return [[CPPredicate_BOOL alloc] initWithBool:value];
+}
+
+// Evaluating a Predicate
+/*!
+ Returns a Boolean value that indicates whether a given object matches the conditions specified by the receiver.
+ @param object The object against which to evaluate the receiver.
+ @return YES if object matches the conditions specified by the receiver, otherwise NO.
+*/
+- (BOOL)evaluateWithObject:(id)object
+{
+ // IMPLEMENTED BY SUBCLASSES
+}
+
+/*!
+ Returns a Boolean value that indicates whether a given object matches the conditions specified by the receiver after substituting in the values in a given variables dictionary.
+ @param object The object against which to evaluate the receiver.
+ @param variables The substitution variables dictionary. The dictionary must contain key-value pairs for all variables in the receiver.
+ @return YES if object matches the conditions specified by the receiver after substituting in the values in variables for any replacement tokens, otherwise NO.
+*/
+- (BOOL)evaluateWithObject:(id)object substitutionVariables:(CPDictionary)variables
+{
+ // IMPLEMENTED BY SUBCLASSES
+}
+
+// Getting Format Information
+/*!
+ Returns the receiver’s format string.
+ @return The receiver’s format string.
+*/
+- (CPString)predicateFormat
+{
+ // IMPLEMENTED BY SUBCLASSES
+}
+
+- (CPString)description
+{
+ return [self predicateFormat];
+}
+
+@end
+
+@implementation CPPredicate_BOOL : CPPredicate
+{
+ BOOL _value;
+}
+
+- (id)initWithBool:(BOOL)value
+{
+ _value = value;
+ return self;
+}
+
+- (BOOL)evaluateObject:(id)object
+{
+ return _value;
+}
+
+- (CPString)predicateFormat
+{
+ return (_value) ? @"TRUEPREDICATE" : @"FALSEPREDICATE";
+}
+
+@end
+
+
+@implementation CPArray (CPPredicate)
+
+- (CPArray)filteredArrayUsingPredicate:(CPPredicate)predicate
+{
+ var count = [self count],
+ result = [CPArray array],
+ i;
+
+ for (i = 0; i < count; i++)
+ {
+ var object = self[i];
+ if ([predicate evaluateWithObject:object])
+ result.push(object);
+ }
+
+ return result;
+}
+
+- (void)filterUsingPredicate:(CPPredicate)predicate
+{
+ var count = [self count];
+
+ while (count--)
+ {
+ if (![predicate evaluateWithObject:self[count]])
+ splice(count, 1);
+ }
+}
+
+@end
+
+@implementation CPSet (CPPredicate)
+
+- (CPSet)filteredSetUsingPredicate:(CPPredicate)predicate
+{
+ var count = [self count],
+ result = [CPSet set],
+ i;
+
+ for (i = 0; i < count; i++)
+ {
+ var object = [self objectAtIndex:i];
+
+ if ([predicate evaluateWithObject:object])
+ [result addObject:object];
+ }
+
+ return result;
+}
+
+- (void)filterUsingPredicate:(CPPredicate)predicate
+{
+ var count = [self count];
+
+ while (--count >= 0)
+ {
+ var object = [self objectAtIndex:count];
+
+ if (![predicate evaluateWithObject:object])
+ [self removeObjectAtIndex:count];
+ }
+}
+
+@end
+
+#define REFERENCE(variable) \
+function(newValue)\
+{\
+ var oldValue = variable;\
+ if (typeof newValue != 'undefined')\
+ variable = newValue;\
+ return oldValue;\
+}
+
+@implementation CPPredicateScanner : CPScanner
+{
+ CPEnumerator _args;
+ unsigned _retrieved;
+}
+
+- (id) initWithString:(CPString)format args:(CPArray)args
+{
+ self = [super initWithString:format];
+ if (self != nil)
+ {
+ _args = [args objectEnumerator];
+ }
+ return self;
+}
+
+- (id) nextArg
+{
+ return [_args nextObject];
+}
+
+- (BOOL)scanPredicateKeyword:(CPString)key
+{
+ var loc = [self scanLocation];
+ var c;
+
+ [self setCaseSensitive:NO];
+ if (![self scanString:key intoString:NULL])
+ return NO;
+
+ if ([self isAtEnd])
+ return YES;
+
+ c = [[self string] characterAtIndex:[self scanLocation]];
+ if (![[CPCharacterSet alphanumericCharacterSet] characterIsMember:c])
+ return YES;
+
+ [self setScanLocation:loc];
+
+ return NO;
+}
+
+- (CPPredicate) parse
+{
+ var r = nil;
+
+ try
+ {
+ [self setCharactersToBeSkipped:[CPCharacterSet whitespaceCharacterSet]];
+ r = [self parsePredicate];
+ }
+ catch(error)
+ {
+ CPLogConsole(@"Parsing failed for "+[self string]+" with " + error);
+ }
+ finally
+ {
+ if (![self isAtEnd])
+ CPLogConsole(@"Format string contains extra characters: \""+[self string]+"\"");
+ }
+
+ return r;
+}
+
+- (CPPredicate) parsePredicate
+{
+ return [self parseAnd];
+}
+
+- (CPPredicate) parseAnd
+{
+ var l = [self parseOr];
+
+ while ([self scanPredicateKeyword:@"AND"] || [self scanPredicateKeyword:@"&&"])
+ {
+ var r = [self parseOr];
+
+ if ([r isKindOfClass:[CPCompoundPredicate class]] && [r compoundPredicateType] == CPAndPredicateType)
+ {
+ if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPAndPredicateType)
+ {
+ [[l subpredicates] addObjectsFromArray:[r subpredicates]];
+ }
+ else
+ {
+ [[r subpredicates] insertObject:l atIndex:0];
+ l = r;
+ }
+ }
+ else if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPAndPredicateType)
+ {
+ [[l subpredicates] addObject:r];
+ }
+ else
+ {
+ l = [CPCompoundPredicate andPredicateWithSubpredicates:[CPArray arrayWithObjects:l, r]];
+ }
+ }
+ return l;
+}
+
+- (CPPredicate) parseNot
+{
+ if ([self scanString:@"(" intoString:NULL])
+ {
+ var r = [self parsePredicate];
+
+ if (![self scanString:@")" intoString:NULL])
+ [CPException raise:CPInvalidArgumentException reason:@"Missing ) in compound predicate"];
+
+ return r;
+ }
+
+ if ([self scanPredicateKeyword:@"NOT"] || [self scanPredicateKeyword:@"!"])
+ {
+ return [CPCompoundPredicate notPredicateWithSubpredicate:[self parseNot]];
+ }
+ if ([self scanPredicateKeyword:@"TRUEPREDICATE"])
+ {
+ return [CPPredicate predicateWithValue:YES];
+ }
+ if ([self scanPredicateKeyword:@"FALSEPREDICATE"])
+ {
+ return [CPPredicate predicateWithValue:NO];
+ }
+
+ return [self parseComparison];
+}
+
+- (CPPredicate) parseOr
+{
+ var l = [self parseNot];
+ while ([self scanPredicateKeyword:@"OR"] || [self scanPredicateKeyword:@"||"])
+ {
+ var r = [self parseNot];
+
+ if ([r isKindOfClass:[CPCompoundPredicate class]] && [r compoundPredicateType] == CPOrPredicateType)
+ {
+ if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPOrPredicateType)
+ {
+ [[l subpredicates] addObjectsFromArray:[r subpredicates]];
+ }
+ else
+ {
+ [[r subpredicates] insertObject:l atIndex:0];
+ l = r;
+ }
+ }
+ else if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPOrPredicateType)
+ {
+ [[l subpredicates] addObject:r];
+ }
+ else
+ {
+ l = [CPCompoundPredicate orPredicateWithSubpredicates:[CPArray arrayWithObjects:l, r]];
+ }
+ }
+ return l;
+}
+
+- (CPPredicate) parseComparison
+{
+ var modifier = CPDirectPredicateModifier,
+ type = 0,
+ opts = 0,
+ left,
+ right,
+ p,
+ negate = NO,
+ swap = NO;
+
+ if ([self scanPredicateKeyword:@"ANY"])
+ {
+ modifier = CPAnyPredicateModifier;
+ }
+ else if ([self scanPredicateKeyword:@"ALL"])
+ {
+ modifier = CPAllPredicateModifier;
+ }
+ else if ([self scanPredicateKeyword:@"NONE"])
+ {
+ modifier = CPAnyPredicateModifier;
+ negate = YES;
+ }
+ else if ([self scanPredicateKeyword:@"SOME"])
+ {
+ modifier = CPAllPredicateModifier;
+ negate = YES;
+ }
+
+ left = [self parseExpression];
+ if ([self scanString:@"!=" intoString:NULL] || [self scanString:@"<>" intoString:NULL])
+ {
+ type = CPNotEqualToPredicateOperatorType;
+ }
+ else if ([self scanString:@"<=" intoString:NULL] || [self scanString:@"=<" intoString:NULL])
+ {
+ type = CPLessThanOrEqualToPredicateOperatorType;
+ }
+ else if ([self scanString:@">=" intoString:NULL] || [self scanString:@"=>" intoString:NULL])
+ {
+ type = CPGreaterThanOrEqualToPredicateOperatorType;
+ }
+ else if ([self scanString:@"<" intoString:NULL])
+ {
+ type = CPLessThanPredicateOperatorType;
+ }
+ else if ([self scanString:@">" intoString:NULL])
+ {
+ type = CPGreaterThanPredicateOperatorType;
+ }
+ else if ([self scanString:@"==" intoString:NULL] || [self scanString:@"=" intoString:NULL])
+ {
+ type = CPEqualToPredicateOperatorType;
+ }
+ else if ([self scanPredicateKeyword:@"MATCHES"])
+ {
+ type = CPMatchesPredicateOperatorType;
+ }
+ else if ([self scanPredicateKeyword:@"LIKE"])
+ {
+ type = CPLikePredicateOperatorType;
+ }
+ else if ([self scanPredicateKeyword:@"BEGINSWITH"])
+ {
+ type = CPBeginsWithPredicateOperatorType;
+ }
+ else if ([self scanPredicateKeyword:@"ENDSWITH"])
+ {
+ type = CPEndsWithPredicateOperatorType;
+ }
+ else if ([self scanPredicateKeyword:@"IN"])
+ {
+ type = CPInPredicateOperatorType;
+ }
+ else if ([self scanPredicateKeyword:@"CONTAINS"])
+ {
+ type = CPInPredicateOperatorType;
+ swap = YES;
+ }
+ else if ([self scanPredicateKeyword:@"BETWEEN"])
+ {
+ var exp = [self parseSimpleExpression],
+ a = [exp constantValue],
+ lower, upper,
+ lexp, uexp,
+ lp, up;
+
+ if (![a isKindOfClass:[CPArray class]])
+ [CPException raise:CPInvalidArgumentException reason:@"BETWEEN operator requires array argument"];
+
+ lower = [a objectAtIndex:0];
+ upper = [a objectAtIndex:1];
+ lexp = [CPExpression expressionForConstantValue:lower];
+ uexp = [CPExpression expressionForConstantValue:upper];
+ lp = [CPComparisonPredicate predicateWithLeftExpression:left
+ rightExpression:lexp
+ modifier:modifier
+ type:CPGreaterThanPredicateOperatorType
+ options:opts];
+ up = [CPComparisonPredicate predicateWithLeftExpression:left
+ rightExpression:uexp
+ modifier:modifier
+ type:CPLessThanPredicateOperatorType
+ options:opts];
+ return [CPCompoundPredicate andPredicateWithSubpredicates:
+ [CPArray arrayWithObjects:lp, up]];
+ }
+ else
+ [CPException raise:CPInvalidArgumentException reason:@"Invalid comparison predicate: "+ [[self string] substringFromIndex: [self scanLocation]]];
+
+ if ([self scanString:@"[cd]" intoString:NULL])
+ {
+ opts = CPCaseInsensitivePredicateOption | CPDiacriticInsensitivePredicateOption;
+ }
+ else if ([self scanString:@"[c]" intoString:NULL])
+ {
+ opts = CPCaseInsensitivePredicateOption;
+ }
+ else if ([self scanString:@"[d]" intoString:NULL])
+ {
+ opts = CPDiacriticInsensitivePredicateOption;
+ }
+
+ right = [self parseExpression];
+
+ if (swap == YES)
+ {
+ var tmp = left;
+
+ left = right;
+ right = tmp;
+ }
+
+ p = [CPComparisonPredicate predicateWithLeftExpression:left
+ rightExpression:right
+ modifier:modifier
+ type:type
+ options:opts];
+
+ return negate ? [CPCompoundPredicate notPredicateWithSubpredicate:p]:p;
+}
+
+- (CPExpression) parseExpression
+{
+ return [self parseBinaryExpression];
+}
+
+- (CPExpression) parseSimpleExpression
+{
+ var identifier,
+ location,
+ ident,
+ dbl;
+
+ if ([self scanDouble:REFERENCE(dbl)])
+ return [CPExpression expressionForConstantValue:[CPNumber numberWithDouble:dbl]];
+
+ // FIXME: handle integer, hex constants, 0x 0o 0b
+ if ([self scanString:@"-" intoString:NULL])
+ return [CPExpression expressionForFunction:@"chs" arguments:[CPArray arrayWithObject:[self parseExpression]]];
+
+ if ([self scanString:@"(" intoString:NULL])
+ {
+ var arg = [self parseExpression];
+
+ if (![self scanString:@")" intoString:NULL])
+ [CPException raise:CPInvalidArgumentException reason:@"Missing ) in expression"];
+
+ return arg;
+ }
+
+ if ([self scanString:@"{" intoString:NULL])
+ {
+ var a = [CPMutableArray arrayWithCapacity:10];
+
+ if ([self scanString:@"}" intoString:NULL])
+ return [CPExpression expressionForConstantValue:a];
+
+ [a addObject:[self parseExpression]];
+ while ([self scanString:@"," intoString:NULL])
+ [a addObject:[self parseExpression]];
+
+ if (![self scanString:@"}" intoString:NULL])
+ [CPException raise:CPInvalidArgumentException reason:@"Missing } in aggregate"];
+
+ return [CPExpression expressionForConstantValue:a];
+ }
+
+ if ([self scanPredicateKeyword:@"NULL"] || [self scanPredicateKeyword:@"NIL"])
+ {
+ return [CPExpression expressionForConstantValue:[CPNull null]];
+ }
+ if ([self scanPredicateKeyword:@"TRUE"] || [self scanPredicateKeyword:@"YES"])
+ {
+ return [CPExpression expressionForConstantValue:[CPNumber numberWithBool:YES]];
+ }
+ if ([self scanPredicateKeyword:@"FALSE"] || [self scanPredicateKeyword:@"NO"])
+ {
+ return [CPExpression expressionForConstantValue:[CPNumber numberWithBool:NO]];
+ }
+ if ([self scanPredicateKeyword:@"SELF"])
+ {
+ return [CPExpression expressionForEvaluatedObject];
+ }
+
+ if ([self scanString:@"$" intoString:NULL])
+ {
+ var variable = [self parseExpression];
+
+ if (![variable keyPath])
+ [CPException raise:CPInvalidArgumentException reason:@"Invalid variable identifier: " + variable];
+
+ return [CPExpression expressionForVariable:[variable keyPath]];
+ }
+
+ location = [self scanLocation];
+
+ if ([self scanString:@"%" intoString:NULL])
+ {
+ if ([self isAtEnd] == NO)
+ {
+ var c = [[self string] characterAtIndex:[self scanLocation]];
+
+ switch (c)
+ {
+ case '%':// '%%' is treated as '%'
+ location = [self scanLocation];
+ break;
+ case 'K':
+ [self setScanLocation:[self scanLocation] + 1];
+ return [CPExpression expressionForKeyPath:[self nextArg]];
+ case '@':
+ case 'c':
+ case 'C':
+ case 'd':
+ case 'D':
+ case 'i':
+ case 'o':
+ case 'O':
+ case 'u':
+ case 'U':
+ case 'x':
+ case 'X':
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ [self setScanLocation:[self scanLocation] + 1];
+ return [CPExpression expressionForConstantValue:[self nextArg]];
+ case 'h':
+ [self scanString:@"h" intoString:NULL];
+ if ([self isAtEnd] == NO)
+ {
+ c = [[self string] characterAtIndex:[self scanLocation]];
+ if (c == 'i' || c == 'u')
+ {
+ [self setScanLocation:[self scanLocation] + 1];
+ return [CPExpression expressionForConstantValue:[self nextArg]];
+ }
+ }
+ break;
+ case 'q':
+ [self scanString:@"q" intoString:NULL];
+ if ([self isAtEnd] == NO)
+ {
+ c = [[self string] characterAtIndex:[self scanLocation]];
+ if (c == 'i' || c == 'u' || c == 'x' || c == 'X')
+ {
+ [self setScanLocation:[self scanLocation] + 1];
+ return [CPExpression expressionForConstantValue:[self nextArg]];
+ }
+ }
+ break;
+ }
+ }
+
+ [self setScanLocation:location];
+ }
+
+ if ([self scanString:@"\"" intoString:NULL])
+ {
+ var skip = [self charactersToBeSkipped],
+ str;
+
+ [self setCharactersToBeSkipped:nil];
+ if ([self scanUpToString:@"\"" intoString:REFERENCE(str)] == NO)
+ {
+ [self setCharactersToBeSkipped:skip];
+ [CPException raise:CPInvalidArgumentException reason:@"Invalid double quoted literal at "+location];
+ }
+
+ [self scanString:@"\"" intoString:NULL];
+ [self setCharactersToBeSkipped:skip];
+
+ return [CPExpression expressionForConstantValue:str];
+ }
+ if ([self scanString:@"'" intoString:NULL])
+ {
+ var skip = [self charactersToBeSkipped],
+ str;
+
+ [self setCharactersToBeSkipped:nil];
+ if ([self scanUpToString:@"'" intoString:REFERENCE(str)] == NO)
+ {
+ [self setCharactersToBeSkipped:skip];
+ [CPException raise:CPInvalidArgumentException reason:@"Invalid single quoted literal at "+location];
+ }
+
+ [self scanString:@"'" intoString:NULL];
+ [self setCharactersToBeSkipped:skip];
+
+ return [CPExpression expressionForConstantValue:str];
+ }
+
+ if ([self scanString:@"@" intoString:NULL])
+ {
+ var e = [self parseExpression];
+
+ if (![e keyPath])
+ [CPException raise:CPInvalidArgumentException reason:@"Invalid keypath identifier: "+e];
+
+ return [CPExpression expressionForKeyPath:[e keyPath]+"@"];
+ }
+
+ [self scanString:@"#" intoString:NULL];
+ if (!identifier)
+ identifier = [CPCharacterSet characterSetWithCharactersInString:@"_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"];
+
+ if (![self scanCharactersFromSet:identifier intoString:REFERENCE(ident)])
+ [CPException raise:CPInvalidArgumentException reason:@"Missing identifier: "+[[self string] substringFromIndex:[self scanLocation]]];
+
+ return [CPExpression expressionForKeyPath:ident];
+}
+
+- (CPExpression)parseFunctionalExpression
+{
+ var left = [self parseSimpleExpression];
+
+ while (YES)
+ {
+ if ([self scanString:@"(" intoString:NULL])
+ {
+ // function - this parser allows for (max)(a, b, c) to be properly
+ // recognized and even (%K)(a, b, c) if %K evaluates to "max"
+ var args = [CPMutableArray arrayWithCapacity:5];
+
+ if (![left keyPath])
+ [CPException raise:CPInvalidArgumentException reason:@"Invalid function identifier: " + left];
+
+ if (![self scanString:@")" intoString:NULL])
+ {
+ // any arguments
+ // first argument
+ [args addObject:[self parseExpression]];
+ while ([self scanString:@"," intoString:NULL])
+ {
+ // more arguments
+ [args addObject:[self parseExpression]];
+ }
+
+ if (![self scanString:@")" intoString:NULL])
+ [CPException raise:CPInvalidArgumentException reason:@"Missing ) in function arguments"];
+ }
+ left = [CPExpression expressionForFunction:[left keyPath] arguments:args];
+ }
+ else if ([self scanString:@"[" intoString:NULL])
+ {
+ // index expression
+ if ([self scanPredicateKeyword:@"FIRST"])
+ {
+ left = [CPExpression expressionForFunction:@"first" arguments:[CPArray arrayWithObject:[self parseExpression]]];
+ }
+ else if ([self scanPredicateKeyword:@"LAST"])
+ {
+ left = [CPExpression expressionForFunction:@"last" arguments:[CPArray arrayWithObject:[self parseExpression]]];
+ }
+ else if ([self scanPredicateKeyword:@"SIZE"])
+ {
+ left = [CPExpression expressionForFunction:@"count" arguments:[CPArray arrayWithObject:[self parseExpression]]];
+ }
+ else
+ {
+ left = [CPExpression expressionForFunction:@"index" arguments:[CPArray arrayWithObjects:left, [self parseExpression]]];
+ }
+ if (![self scanString:@"]" intoString:NULL])
+ [CPException raise:CPInvalidArgumentException reason:@"Missing ] in index argument"];
+ }
+ else if ([self scanString:@"." intoString:NULL])
+ {
+ // keypath - this parser allows for (a).(b.c)
+ // to be properly recognized
+ // and even %K.((%K)) if the first %K evaluates to "a" and the
+ // second %K to "b.c"
+
+ if (![left keyPath])
+ [CPException raise:CPInvalidArgumentException reason:@"Invalid left keypath:" + left];
+
+ var right = [self parseExpression];
+ if (![right keyPath])
+ [CPException raise:CPInvalidArgumentException reason:@"Invalid right keypath:" + right];
+
+ // concatenate
+ left = [CPExpression expressionForKeyPath:[left keyPath]+ "." + [right keyPath]];
+ }
+ else
+ {
+ // done with suffixes
+ return left;
+ }
+ }
+}
+
+- (CPExpression)parsePowerExpression
+{
+ var left = [self parseFunctionalExpression];
+
+ while (YES)
+ {
+ var right;
+
+ if ([self scanString:@"**" intoString:NULL])
+ {
+ right = [self parseFunctionalExpression];
+ left = [CPExpression expressionForFunction:@"pow" arguments:[CPArray arrayWithObjects:left, right]];
+ }
+ else
+ {
+ return left;
+ }
+ }
+}
+
+- (CPExpression)parseMultiplicationExpression
+{
+ var left = [self parsePowerExpression];
+
+ while (YES)
+ {
+ var right;
+
+ if ([self scanString:@"*" intoString:NULL])
+ {
+ right = [self parsePowerExpression];
+ left = [CPExpression expressionForFunction:@"_mul" arguments:[CPArray arrayWithObjects:left, right]];
+ }
+ else if ([self scanString:@"/" intoString:NULL])
+ {
+ right = [self parsePowerExpression];
+ left = [CPExpression expressionForFunction:@"_div" arguments:[CPArray arrayWithObjects:left, right]];
+ }
+ else
+ {
+ return left;
+ }
+ }
+}
+
+- (CPExpression)parseAdditionExpression
+{
+ var left = [self parseMultiplicationExpression];
+
+ while (YES)
+ {
+ var right;
+
+ if ([self scanString:@"+" intoString:NULL])
+ {
+ right = [self parseMultiplicationExpression];
+ left = [CPExpression expressionForFunction:@"_add" arguments:[CPArray arrayWithObjects:left, right]];
+ }
+ else if ([self scanString:@"-" intoString:NULL])
+ {
+ right = [self parseMultiplicationExpression];
+ left = [CPExpression expressionForFunction:@"_sub" arguments:[CPArray arrayWithObjects:left, right]];
+ }
+ else
+ {
+ return left;
+ }
+ }
+}
+
+- (CPExpression)parseBinaryExpression
+{
+ var left = [self parseAdditionExpression];
+
+ while (YES)
+ {
+ var right;
+
+ if ([self scanString:@":=" intoString:NULL]) // assignment
+ {
+ // check left to be a variable?
+ right = [self parseAdditionExpression];
+ // FIXME
+ }
+ else
+ {
+ return left;
+ }
+ }
+}
+
+@end
+
+@import "CPCompoundPredicate.j"
+@import "CPComparisonPredicate.j"
+
+@import "CPExpression.j"
+@import "CPExpression_operator.j"
+@import "CPExpression_aggregate.j"
+@import "CPExpression_assignment.j"
diff --git a/Foundation/CPProxy.j b/Foundation/CPProxy.j
index 2b4054c83..cbb8f0f2f 100644
--- a/Foundation/CPProxy.j
+++ b/Foundation/CPProxy.j
@@ -67,7 +67,7 @@
// FIXME: This should be moved to the runtime?
- (void)forward:(SEL)aSelector :(marg_list)args
{
- [CPObject methodForSelector:_cmd](self, _cmd, aSelector, args);
+ return [CPObject methodForSelector:_cmd](self, _cmd, aSelector, args);
}
- (unsigned)hash
diff --git a/Foundation/CPScanner.j b/Foundation/CPScanner.j
new file mode 100644
index 000000000..b5373f0af
--- /dev/null
+++ b/Foundation/CPScanner.j
@@ -0,0 +1,392 @@
+// CPScanner.j
+// © Emanuele Vulcano, 2008.
+//
+// Licensed under the terms of Cappuccino's license
+// (the GNU Lesser General Public License, version 2.1).
+// Please see Cappuccino's LICENSE file for details.
+
+@import
+
+@implementation CPScanner : CPObject
+{
+ CPString _string;
+ CPDictionary _locale;
+ int _scanLocation;
+ BOOL _caseSensitive;
+ CPCharacterSet _charactersToBeSkipped;
+}
+
+// TODO Not all methods of NSScanner are available!
+
+/*
+- (BOOL)scanLongLong:(long long *)longLongValue
+{
+}
+- (BOOL)scanDecimal:(NSDecimal *)decimalValue
+{
+}
+- (BOOL)scanHexDouble:(double *)result
+{
+}
+- (BOOL)scanHexFloat:(float *)result
+{
+}
+- (BOOL)scanHexInt:(unsigned *)intValue
+{
+}
+- (BOOL)scanHexLongLong:(unsigned long long *)result
+{
+}
+- (BOOL)scanInteger:(NSInteger *)value
+{
+}
++ (id)localizedScannerWithString:(CPString)string
+{
+ var scanner = [self scannerWithString:string];
+
+ [scanner setLocale:[CPLocale currentLocale]];
+
+ return scanner;
+}
+
+*/
+
++ (id)scannerWithString:(CPString)aString
+{
+ return [[self alloc] initWithString:aString];
+}
+
+- (id)initWithString:(CPString)aString
+{
+ if (self = [super init])
+ {
+ _string = [aString copy];
+ _scanLocation = 0;
+ _charactersToBeSkipped = [CPCharacterSet whitespaceCharacterSet];
+ _caseSensitive = NO;
+ }
+
+ return self;
+}
+
+- (id)copy
+{
+ var copy = [[CPScanner alloc] initWithString:[self string]];
+
+ [copy setCharactersToBeSkipped:[self charactersToBeSkipped]];
+ [copy setCaseSensitive:[self caseSensitive]];
+ [copy setLocale:[self locale]];
+ [copy setScanLocation:[self scanLocation]];
+
+ return copy;
+}
+
+- (CPDictionary)locale
+{
+ return _locale;
+}
+
+- (void)setLocale:(CPDictionary)aLocale
+{
+ _locale = aLocale;
+}
+
+- (void)setCaseSensitive:(BOOL)flag
+{
+ _caseSensitive = flag;
+}
+
+- (BOOL)caseSensitive
+{
+ return _caseSensitive;
+}
+
+- (CPString)string
+{
+ return _string;
+}
+
+- (CPCharacterSet)charactersToBeSkipped
+{
+ return _charactersToBeSkipped;
+}
+
+- (void)setCharactersToBeSkipped:(CPCharacterSet)c
+{
+ _charactersToBeSkipped = c;
+}
+
+- (BOOL)isAtEnd
+{
+ return _scanLocation == _string.length;
+}
+
+- (int)scanLocation
+{
+ return _scanLocation;
+}
+
+- (void)setScanLocation:(int)aLocation
+{
+ if (aLocation > _string.length)
+ aLocation = _string.length; // clamp to just after the last character
+ else if (aLocation < 0)
+ aLocation = 0; // clamp to the first
+
+ _scanLocation = aLocation;
+}
+
+// Method body for all methods that return their value by reference.
+- (BOOL)_performScanWithSelector:(SEL)s withObject:(id)arg into:(id)ref
+{
+ var ret = [self performSelector:s withObject:arg];
+
+ if (ref != nil)
+ ref(ret);
+
+ return ret != NULL;
+}
+
+/* ================================ */
+/* = Scanning with CPCharacterSet = */
+/* ================================ */
+
+- (BOOL)scanCharactersFromSet:(CPCharacterSet)scanSet intoString:(id)ref
+{
+ return [self _performScanWithSelector:@selector(scanCharactersFromSet:) withObject:scanSet into:ref];
+}
+
+- (CPString)scanCharactersFromSet:(CPCharacterSet)scanSet
+{
+ return [self _scanWithSet:scanSet breakFlag:NO];
+}
+
+- (BOOL)scanUpToCharactersFromSet:(CPCharacterSet)scanSet intoString:(id)ref
+{
+ return [self _performScanWithSelector:@selector(scanUpToCharactersFromSet:) withObject:scanSet into:ref];
+}
+
+- (CPString)scanUpToCharactersFromSet:(CPCharacterSet)scanSet
+{
+ return [self _scanWithSet:scanSet breakFlag:YES];
+}
+
+// If stop == YES, it will stop when it sees a character from
+// the set (scanUpToCharactersFromSet:); if stop == NO, it will
+// stop when it sees a character NOT from the set
+// (scanCharactersFromSet:).
+- (CPString)_scanWithSet:(CPCharacterSet)scanSet breakFlag:(BOOL)stop
+{
+ if ([self isAtEnd])
+ return nil;
+
+ var current = [self scanLocation];
+ var str = nil;
+
+ while (current < _string.length)
+ {
+ var c = (_string.charAt(current));
+
+ if ([scanSet characterIsMember:c] == stop)
+ break;
+
+ if (![_charactersToBeSkipped characterIsMember:c])
+ {
+ if (!str)
+ str = '';
+ str += c;
+ }
+
+ current++;
+ }
+
+ if (str)
+ [self setScanLocation:current];
+
+ return str;
+}
+
+/* ==================== */
+/* = Scanning strings = */
+/* ==================== */
+
+- (void)_movePastCharactersToBeSkipped
+{
+ var current = [self scanLocation];
+ var string = [self string];
+ var toSkip = [self charactersToBeSkipped];
+
+ while (current < string.length)
+ {
+ if (![toSkip characterIsMember:string.charAt(current)])
+ break;
+
+ current++;
+ }
+
+ [self setScanLocation:current];
+}
+
+
+- (BOOL)scanString:(CPString)aString intoString:(id)ref
+{
+ return [self _performScanWithSelector:@selector(scanString:) withObject:aString into:ref];
+}
+
+- (CPString)scanString:(CPString)s
+{
+ [self _movePastCharactersToBeSkipped];
+ if ([self isAtEnd])
+ return nil;
+
+ var currentStr = [self string].substr([self scanLocation], s.length);
+ if ((_caseSensitive && currentStr != s) || (!_caseSensitive && (currentStr.toLowerCase() != s.toLowerCase())))
+ {
+ return nil;
+ }
+ else
+ {
+ [self setScanLocation:[self scanLocation] + s.length];
+ return s;
+ }
+}
+
+- (BOOL)scanUpToString:(CPString)aString intoString:(id)ref
+{
+ return [self _performScanWithSelector:@selector(scanUpToString:) withObject:aString into:ref];
+}
+
+- (CPString)scanUpToString:(CPString)s
+{
+ var current = [self scanLocation], str = [self string];
+ var captured = nil;
+ while (current < str.length)
+ {
+ var currentStr = str.substr(current, s.length);
+ if (currentStr == s || (!_caseSensitive && currentStr.toLowerCase() == s.toLowerCase()))
+ break;
+
+ if (!captured)
+ captured = '';
+ captured += str.charAt(current);
+ current++;
+ }
+
+ if (captured)
+ [self setScanLocation:current];
+
+ // evil private method use!
+ // this method is defined in the category on CPString
+ // in CPCharacterSet.j
+ if ([self charactersToBeSkipped])
+ captured = [captured _stringByTrimmingCharactersInSet:[self charactersToBeSkipped] options:_CPCharacterSetTrimAtBeginning];
+
+ return captured;
+}
+
+/* ==================== */
+/* = Scanning numbers = */
+/* ==================== */
+
+- (float)scanFloat
+{
+ [self _movePastCharactersToBeSkipped];
+ var str = [self string], current = [self scanLocation];
+
+ if ([self isAtEnd])
+ return 0;
+
+ var s = str.substring(current, str.length);
+ var f = parseFloat(s); // wont work with non . decimal separator !!
+ if (f)
+ {
+ var pos, foundDash = NO;
+/*
+ var decimalSeparatorString;
+ if(_locale != nil)
+ decimalSeparatorString = [_locale objectForKey:CPLocaleDecimalSeparator];
+ else
+ decimalSeparatorString = [[CPLocale systemLocale] objectForKey:CPLocaleDecimalSeparator];
+
+ var separatorCode = (decimalSeparatorString.length >0) decimalSeparatorString.charCodeAt(0) : 45;
+*/
+ var separatorCode = 45;
+
+ for(pos = current; pos < current + str.length; pos++)
+ {
+ var charCode = str.charCodeAt(pos);
+ if (charCode == separatorCode)
+ {
+ if (foundDash == YES)
+ break; // We already found a decimal separator so this one is an extra char
+ foundDash = YES;
+ }
+ else if (charCode < 48 || charCode > 57 || (charCode == 45 && pos != current)) // not a digit or a "-" but not prefix
+ break;
+ }
+
+ [self setScanLocation:pos];
+ return f;
+ }
+
+ return nil;
+}
+
+- (int)scanInt
+{
+ [self _movePastCharactersToBeSkipped];
+ var str = [self string], current = [self scanLocation];
+
+ if ([self isAtEnd])
+ return 0;
+ var s = str.substring(current, str.length);
+
+ var i = parseInt(s);
+ if (i)
+ {
+ var pos, foundDash = NO;
+ for (pos = current; pos < current + str.length; pos++)
+ {
+ var charCode = str.charCodeAt(pos);
+ if (charCode == 46)
+ {
+ if (foundDash == YES)
+ break;
+ foundDash = YES;
+ }
+ else if (charCode < 48 || charCode > 57 || (charCode == 45 && pos != current))
+ break;
+ }
+
+ [self setScanLocation:pos];
+ return i;
+ }
+
+ return nil;
+}
+
+- (BOOL)scanInt:(int)intoInt
+{
+ return [self _performScanWithSelector:@selector(scanInt) withObject:nil into:intoInt];
+}
+
+- (BOOL)scanFloat:(float)intoFloat
+{
+ return [self _performScanWithSelector:@selector(scanFloat) withObject:nil into:intoFloat];
+}
+
+- (BOOL)scanDouble:(float)intoDouble
+{
+ return [self scanFloat:intoDouble];
+}
+
+/* ========= */
+/* = Debug = */
+/* ========= */
+
+- (void) description
+{
+ return [super description] + " {" + CPStringFromClass([self class]) + ", state = '" + ([self string].substr(0, _scanLocation) + "{{ SCAN LOCATION ->}}" + [self string].substr(_scanLocation)) + "'; }";
+}
+
+@end
\ No newline at end of file
diff --git a/Foundation/CPString.j b/Foundation/CPString.j
index abbe70c2d..3b6858ab2 100644
--- a/Foundation/CPString.j
+++ b/Foundation/CPString.j
@@ -55,6 +55,12 @@ CPAnchoredSearch = 8;
@class CPString
*/
CPNumericSearch = 64;
+/*!
+ Search ignores diacritic marks.
+ @global
+ @class CPString
+*/
+CPDiacriticInsensitiveSearch = 128;
var CPStringUIDs = new CFMutableDictionary();
@@ -469,6 +475,12 @@ var CPStringRegexSpecialCharacters = [
rhs = rhs.toLowerCase();
}
+ if(aMask & CPDiacriticInsensitiveSearch)
+ {
+ lhs = lhs.stripDiacritics();
+ rhs = rhs.stripDiacritics();
+ }
+
if (lhs < rhs)
return CPOrderedAscending;
else if (lhs > rhs)
@@ -512,6 +524,18 @@ var CPStringRegexSpecialCharacters = [
return aString && aString != "" && length >= aString.length && lastIndexOf(aString) == (length - aString.length);
}
+- (BOOL)isEqual:(id)anObject
+{
+ if (self === anObject)
+ return YES;
+
+ if (!anObject || ![anObject isKindOfClass:[CPString class]])
+ return NO;
+
+ return [self isEqualToString:anObject];
+}
+
+
/*!
Returns \c YES if the specified string contains the same characters as the receiver.
*/
@@ -621,10 +645,9 @@ var CPStringRegexSpecialCharacters = [
a digit 1-9. Returns \c NO otherwise. This method skips the initial
whitespace characters, +,- followed by Zeroes.
*/
-
- (BOOL)boolValue
{
- var replaceRegExp = new RegExp("^\\s*[\\+,\\-]*0*");
+ var replaceRegExp = new RegExp("^\\s*[\\+,\\-]?0*");
return RegExp("^[Y,y,t,T,1-9]").test(self.replace(replaceRegExp, ''));
}
@@ -771,4 +794,31 @@ var CPStringRegexSpecialCharacters = [
@end
+var diacritics = [[192,198],[224,230],[231,231],[232,235],[236,239],[242,246],[249,252]]; // Basic Latin ; Latin-1 Supplement.
+var normalized = [65,97,99,101,105,111,117];
+
+String.prototype.stripDiacritics = function ()
+{
+ var output = "";
+ for (var indexSource = 0; indexSource < this.length; indexSource++)
+ {
+ var code = this.charCodeAt(indexSource);
+
+ for (var i = 0; i < diacritics.length; i++)
+ {
+ var drange = diacritics[i];
+
+ if (code >= drange[0] && code <= drange[drange.length-1])
+ {
+ code = normalized[i];
+ break;
+ }
+ }
+
+ output += String.fromCharCode(code);
+ }
+
+ return output;
+}
+
String.prototype.isa = CPString;
diff --git a/Foundation/CPTimer.j b/Foundation/CPTimer.j
index 6e14ba46a..5c25f1c59 100644
--- a/Foundation/CPTimer.j
+++ b/Foundation/CPTimer.j
@@ -62,7 +62,7 @@
*/
+ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
{
- var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds target:aTarget selector:aSelector userInfo:userInfo repeats:shouldRepeat]
+ var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds target:aTarget selector:aSelector userInfo:userInfo repeats:shouldRepeat];
//add to the runloop
[[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
diff --git a/Foundation/CPUndoManager.j b/Foundation/CPUndoManager.j
index f07247589..fb6865f7c 100644
--- a/Foundation/CPUndoManager.j
+++ b/Foundation/CPUndoManager.j
@@ -47,6 +47,7 @@ var _CPUndoGroupingPool = [],
{
_CPUndoGrouping _parent;
CPMutableArray _invocations;
+ CPString _actionName;
}
+ (void)_poolUndoGrouping:(_CPUndoGrouping)anUndoGrouping
@@ -82,6 +83,7 @@ var _CPUndoGroupingPool = [],
{
_parent = anUndoGrouping;
_invocations = [];
+ _actionName = @"";
}
return self;
@@ -124,10 +126,21 @@ var _CPUndoGroupingPool = [],
[_invocations[index] invoke];
}
+- (void)setActionName:(CPString)aName
+{
+ _actionName = aName;
+}
+
+- (CPString)actionName
+{
+ return _actionName;
+}
+
@end
var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
- _CPUndoGroupingInvocationsKey = @"_CPUndoGroupingInvocationsKey";
+ _CPUndoGroupingInvocationsKey = @"_CPUndoGroupingInvocationsKey",
+ _CPUndoGroupingActionNameKey = @"_CPUndoGroupingActionNameKey";
@implementation _CPUndoGrouping (CPCoder)
@@ -139,6 +152,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
{
_parent = [aCoder decodeObjectForKey:_CPUndoGroupingParentKey];
_invocations = [aCoder decodeObjectForKey:_CPUndoGroupingInvocationsKey];
+ _actionName = [aCoder decodeObjectForKey:_CPUndoGroupingActionNameKey];
}
return self;
@@ -148,6 +162,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
{
[aCoder encodeObject:_parent forKey:_CPUndoGroupingParentKey];
[aCoder encodeObject:_invocations forKey:_CPUndoGroupingInvocationsKey];
+ [aCoder encodeObject:_actionName forKey:_CPUndoGroupingActionNameKey];
}
@end
@@ -177,7 +192,6 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
int _levelsOfUndo;
id _currentGrouping;
int _state;
- CPString _actionName;
id _preparedTarget;
id _undoManagerProxy;
@@ -349,7 +363,8 @@ if (_currentGroup == nil)
[defaultCenter postNotificationName:CPUndoManagerWillUndoChangeNotification
object:self];
- var undoGrouping = _undoStack.pop();
+ var undoGrouping = _undoStack.pop(),
+ actionName = [undoGrouping actionName];
_state = CPUndoManagerUndoing;
@@ -361,6 +376,8 @@ if (_currentGroup == nil)
_state = CPUndoManagerNormal;
+ [[_redoStack lastObject] setActionName:actionName];
+
[defaultCenter postNotificationName:CPUndoManagerDidUndoChangeNotification
object:self];
}
@@ -388,7 +405,8 @@ if (_currentGroup == nil)
object:self];
var oldUndoGrouping = _currentGrouping,
- undoGrouping = _redoStack.pop();
+ undoGrouping = _redoStack.pop(),
+ actionName = [undoGrouping actionName];
_currentGrouping = nil;
_state = CPUndoManagerRedoing;
@@ -402,6 +420,7 @@ if (_currentGroup == nil)
_currentGrouping = oldUndoGrouping;
_state = CPUndoManagerNormal;
+ [[_undoStack lastObject] setActionName:actionName];
[defaultCenter postNotificationName:CPUndoManagerDidRedoChangeNotification object:self];
}
@@ -526,7 +545,7 @@ if (_currentGroup == nil)
- (unsigned)groupingLevel
{
var grouping = _currentGrouping,
- level = _currentGrouping != nil;
+ level = _currentGrouping ? 1 : 0;
while (grouping = [grouping parent])
++level;
@@ -620,7 +639,8 @@ if (_currentGroup == nil)
*/
- (void)setActionName:(CPString)anActionName
{
- _actionName = anActionName;
+ if (anActionName !== nil && _currentGrouping)
+ [_currentGrouping setActionName:anActionName];
}
/*!
@@ -631,7 +651,37 @@ if (_currentGroup == nil)
*/
- (CPString)redoActionName
{
- return [self canRedo] ? _actionName : nil;
+ if (![self canRedo])
+ return nil;
+
+ return [[_redoStack lastObject] actionName];
+}
+
+/*!
+ Returns the full localized title of the actions to be displayed
+ as a menu item. This method first invokes [-redoActionName] and
+ passes it to [-redoMenuTitleForUndoActionName:] and returns the result.
+*/
+- (CPString)redoMenuItemTitle
+{
+ return [self redoMenuTitleForUndoActionName:[self redoActionName]];
+}
+
+/*!
+ Returns the localized title of the actions to be displayed
+ as a menu item identified by actionName, by appending a
+ localized command string like @"Redo ".
+*/
+- (CPString)redoMenuTitleForUndoActionName:(CPString)anActionName
+{
+ // This handles the empty string ("") case as well.
+ if (anActionName || anActionName === 0)
+
+ // FIXME: The terms @"Redo" and @"Redo %@" should be localized.
+ // KEYWORDS: Localization
+ return @"Redo " + anActionName;
+
+ return @"Redo";
}
/*!
@@ -642,7 +692,37 @@ if (_currentGroup == nil)
*/
- (CPString)undoActionName
{
- return [self canUndo] ? _actionName : nil;
+ if (![self canUndo])
+ return nil;
+
+ return [[_undoStack lastObject] actionName];
+}
+
+/*!
+ Returns the full localized title of the actions to be displayed
+ as a menu item. This method first invokes [-undoActionName] and
+ passes it to [-undoMenuTitleForUndoActionName:] and returns the result.
+*/
+- (CPString)undoMenuItemTitle
+{
+ return [self undoMenuTitleForUndoActionName:[self undoActionName]];
+}
+
+/*!
+ Returns the localized title of the actions to be displayed
+ as a menu item identified by actionName, by appending a
+ localized command string like @"Undo ".
+*/
+- (CPString)undoMenuTitleForUndoActionName:(CPString)anActionName
+{
+ // This handles the empty string ("") case as well.
+ if (anActionName || anActionName === 0)
+
+ // FIXME: The terms @"Undo" and @"Undo %@" should be localized.
+ // KEYWORDS: Localization
+ return @"Undo " + anActionName;
+
+ return @"Undo";
}
// Working With Run Loops
@@ -726,6 +806,12 @@ if (_currentGroup == nil)
change:(CPDictionary)aChange
context:(id)aContext
{
+ // Don't add no-ops to the undo stack.
+ var before = [aChange valueForKey:CPKeyValueChangeOldKey],
+ after = [aChange valueForKey:CPKeyValueChangeNewKey];
+ if (before === after || (before !== nil && before.isa && (after === nil || after.isa) && [before isEqual:after]))
+ return;
+
[[self prepareWithInvocationTarget:anObject]
applyChange:[aChange inverseChangeDictionary]
toKeyPath:aKeyPath];
@@ -734,13 +820,13 @@ if (_currentGroup == nil)
@end
var CPUndoManagerRedoStackKey = @"CPUndoManagerRedoStackKey",
- CPUndoManagerUndoStackKey = @"CPUndoManagerUndoStackKey";
+ CPUndoManagerUndoStackKey = @"CPUndoManagerUndoStackKey",
- CPUndoManagerLevelsOfUndoKey = @"CPUndoManagerLevelsOfUndoKey";
- CPUndoManagerActionNameKey = @"CPUndoManagerActionNameKey";
- CPUndoManagerCurrentGroupingKey = @"CPUndoManagerCurrentGroupingKey";
+ CPUndoManagerLevelsOfUndoKey = @"CPUndoManagerLevelsOfUndoKey",
+ CPUndoManagerActionNameKey = @"CPUndoManagerActionNameKey",
+ CPUndoManagerCurrentGroupingKey = @"CPUndoManagerCurrentGroupingKey",
- CPUndoManagerRunLoopModesKey = @"CPUndoManagerRunLoopModesKey";
+ CPUndoManagerRunLoopModesKey = @"CPUndoManagerRunLoopModesKey",
CPUndoManagerGroupsByEventKey = @"CPUndoManagerGroupsByEventKey";
@implementation CPUndoManager (CPCoding)
diff --git a/Foundation/Foundation.j b/Foundation/Foundation.j
index a49654dd4..c09ee38e1 100755
--- a/Foundation/Foundation.j
+++ b/Foundation/Foundation.j
@@ -22,12 +22,17 @@
@import "CPArray.j"
@import "CPBundle.j"
+@import "CPCharacterSet.j"
@import "CPCoder.j"
+@import "CPComparisonPredicate.j"
+@import "CPCompoundPredicate.j"
@import "CPData.j"
@import "CPDate.j"
@import "CPDictionary.j"
@import "CPEnumerator.j"
@import "CPException.j"
+@import "CPFormatter.j"
+@import "CPExpression.j"
@import "CPIndexSet.j"
@import "CPInvocation.j"
@import "CPJSONPConnection.j"
@@ -43,9 +48,11 @@
@import "CPObjJRuntime.j"
@import "CPOperation.j"
@import "CPOperationQueue.j"
+@import "CPPredicate.j"
@import "CPPropertyListSerialization.j"
@import "CPRange.j"
@import "CPRunLoop.j"
+@import "CPScanner.j"
@import "CPSet.j"
@import "CPSortDescriptor.j"
@import "CPString.j"
diff --git a/Jakefile b/Jakefile
index d90e5989b..83ca1c030 100644
--- a/Jakefile
+++ b/Jakefile
@@ -4,6 +4,7 @@ require("./common.jake");
var FILE = require("file"),
SYSTEM = require("system"),
OS = require("os"),
+ UTIL = require("util"),
jake = require("jake"),
stream = require("term").stream;
@@ -89,20 +90,41 @@ task ("docs", ["documentation"]);
task ("documentation", function()
{
- if (executableExists("doxygen"))
+ // try to find a doxygen executable in the PATH;
+ var doxygen = executableExists("doxygen");
+
+ // If the Doxygen application is installed on Mac OS X, use that
+ if (!doxygen && executableExists("mdfind"))
{
- if (OS.system(["ruby", FILE.join("Tools", "Documentation", "make_headers")]))
+ var p = OS.popen(["mdfind", "kMDItemContentType == 'com.apple.application-bundle' && kMDItemCFBundleIdentifier == 'org.doxygen'"]);
+ if (p.wait() === 0)
+ {
+ var doxygenApps = p.stdout.read().split("\n");
+ if (doxygenApps[0])
+ doxygen = FILE.join(doxygenApps[0], "Contents/Resources/doxygen");
+ }
+ }
+
+ if (doxygen && FILE.exists(doxygen))
+ {
+ stream.print("\0green(Using " + doxygen + " for doxygen binary.\0)");
+
+ var documentationDir = FILE.join("Tools", "Documentation");
+
+ if (OS.system([FILE.join(documentationDir, "make_headers.sh")]))
OS.exit(1); //rake abort if ($? != 0)
- if (OS.system(["doxygen", FILE.join("Tools", "Documentation", "Cappuccino.doxygen")]))
- OS.exit(1); //rake abort if ($? != 0)
+ if (!OS.system([doxygen, FILE.join(documentationDir, "Cappuccino.doxygen")]))
+ {
+ rm_rf($DOCUMENTATION_BUILD);
+ mv("debug.txt", FILE.join("Documentation", "debug.txt"));
+ mv("Documentation", $DOCUMENTATION_BUILD);
+ }
- rm_rf($DOCUMENTATION_BUILD);
- mv("debug.txt", FILE.join("Documentation", "debug.txt"));
- mv("Documentation", $DOCUMENTATION_BUILD);
+ OS.system(["ruby", FILE.join(documentationDir, "cleanup_headers")]);
}
else
- print("doxygen not installed. skipping documentation generation.");
+ stream.print("\0yellow(Doxygen not installed, skipping documentation generation.\0)");
});
// Downloads
@@ -190,17 +212,17 @@ task ("demos", function()
return this._plist.valueForKey(key);
return this._plist;
}
-
+
Demo.prototype.name = function()
{
return this.plist("CPBundleName");
}
-
+
Demo.prototype.path = function()
{
return this._path;
}
-
+
Demo.prototype.excluded = function()
{
return !!this.plist("CPDemoExcluded");
@@ -210,7 +232,7 @@ task ("demos", function()
{
return this.name();
}
-
+
FILE.glob(FILE.join(demosDir, "demos", "**/Info.plist")).map(function(demoPath){
return new Demo(FILE.dirname(demoPath))
}).filter(function(demo){
@@ -224,7 +246,7 @@ task ("demos", function()
var outputPath = demo.name().replace(/\s/g, "-")+".zip";
OS.system("cd "+OS.enquote(FILE.dirname(demo.path()))+" && zip -ry -8 "+OS.enquote(outputPath)+" "+OS.enquote(FILE.basename(demo.path())));
- // remove the frameworks
+ // remove the frameworks
rm_rf(FILE.join(demo.path(), "Frameworks"));
});
});
@@ -308,7 +330,7 @@ function pushPackage(path, remote, branch)
cmd.push(["git", "tag", "rev-"+pkg["cappuccino-revision"].slice(0,6)]);
OS.system(buildCmd(cmd));
-
+
if (OS.system(buildCmd([
["cd", packagePath],
["git", "push", "--tags", "origin", "HEAD:"+branch]
diff --git a/Objective-J/CFBundle.js b/Objective-J/CFBundle.js
index 8c7044e73..a64ecf91d 100644
--- a/Objective-J/CFBundle.js
+++ b/Objective-J/CFBundle.js
@@ -235,6 +235,11 @@ CFBundle.prototype.isLoading = function()
return this._loadStatus & CFBundleLoading;
}
+CFBundle.prototype.isLoaded = function()
+{
+ return this._loadStatus & CFBundleLoaded;
+}
+
DISPLAY_NAME(CFBundle.prototype.isLoading);
CFBundle.prototype.load = function(/*BOOL*/ shouldExecute)
@@ -339,7 +344,7 @@ function loadExecutableAndResources(/*Bundle*/ aBundle, /*BOOL*/ shouldExecute)
if ((typeof CPApp === "undefined" || !CPApp || !CPApp._finishedLaunching) &&
typeof OBJJ_PROGRESS_CALLBACK === "function" && CPApplicationSizeInBytes)
{
- OBJJ_PROGRESS_CALLBACK(MAX(MIN(1.0, CFTotalBytesLoaded / CPApplicationSizeInBytes), 0.0), CPApplicationSizeInBytes, aBundle.path())
+ OBJJ_PROGRESS_CALLBACK(MAX(MIN(1.0, CFTotalBytesLoaded / CPApplicationSizeInBytes), 0.0), CPApplicationSizeInBytes, aBundle.bundlePath())
}
if (aBundle._loadStatus === CFBundleLoading)
diff --git a/Objective-J/CFData.js b/Objective-J/CFData.js
index 51743a5a9..7898756f7 100644
--- a/Objective-J/CFData.js
+++ b/Objective-J/CFData.js
@@ -227,6 +227,11 @@ CFData.decodeBase64ToString = function(input, strip)
return CFData.bytesToString(CFData.decodeBase64ToArray(input, strip));
}
+CFData.decodeBase64ToUtf16String = function(input, strip)
+{
+ return CFData.bytesToUtf16String(CFData.decodeBase64ToArray(input, strip));
+}
+
CFData.bytesToString = function(bytes)
{
// This is relatively efficient, I think:
@@ -242,3 +247,28 @@ CFData.encodeBase64String = function(input)
return CFData.encodeBase64Array(temp);
}
+
+CFData.bytesToUtf16String = function(bytes)
+{
+ // Strings are encoded with 16 bits per character.
+ var temp = [];
+ for (var i = 0; i < bytes.length; i+=2)
+ temp.push(bytes[i+1] << 8 | bytes[i]);
+ // This is relatively efficient, I think:
+ return String.fromCharCode.apply(NULL, temp);
+}
+
+
+CFData.encodeBase64Utf16String = function(input)
+{
+ // charCodeAt returns UTF-16.
+ var temp = [];
+ for (var i = 0; i < input.length; i++)
+ {
+ var c = input.charCodeAt(i);
+ temp.push(input.charCodeAt(i) & 0xFF);
+ temp.push((input.charCodeAt(i) & 0xFF00) >> 8);
+ }
+
+ return CFData.encodeBase64Array(temp);
+}
diff --git a/Objective-J/CFHTTPRequest.js b/Objective-J/CFHTTPRequest.js
index d4007f585..2ef8c721f 100644
--- a/Objective-J/CFHTTPRequest.js
+++ b/Objective-J/CFHTTPRequest.js
@@ -219,12 +219,6 @@ CFHTTPRequest.prototype.open = function(/*String*/ aMethod, /*String*/ aURL, /*B
CFHTTPRequest.prototype.send = function(/*Object*/ aBody)
{
- for (var i in this._requestHeaders)
- {
- if (this._requestHeaders.hasOwnProperty(i))
- this._nativeRequest.setRequestHeader(i, this._requestHeaders[i]);
- }
-
if (!this._isOpen)
{
delete this._nativeRequest.onreadystatechange;
@@ -232,6 +226,12 @@ CFHTTPRequest.prototype.send = function(/*Object*/ aBody)
this._nativeRequest.onreadystatechange = this._stateChangeHandler;
}
+ for (var i in this._requestHeaders)
+ {
+ if (this._requestHeaders.hasOwnProperty(i))
+ this._nativeRequest.setRequestHeader(i, this._requestHeaders[i]);
+ }
+
if (this._mimeType && "overrideMimeType" in this._nativeRequest)
this._nativeRequest.overrideMimeType(this._mimeType);
diff --git a/Objective-J/CFPropertyList.js b/Objective-J/CFPropertyList.js
index 39ba97e13..5faeb473b 100644
--- a/Objective-J/CFPropertyList.js
+++ b/Objective-J/CFPropertyList.js
@@ -306,6 +306,8 @@ var XML_XML = "xml",
#define PARENT_NODE(anXMLNode) (anXMLNode.parentNode)
#define DOCUMENT_ELEMENT(aDocument) (aDocument.documentElement)
+#define HAS_ATTRIBUTE_VALUE(anXMLNode, anAttributeName, aValue) (anXMLNode.getAttribute(anAttributeName) === aValue)
+
#define IS_OF_TYPE(anXMLNode, aType) (NODE_NAME(anXMLNode) === aType)
#define IS_PLIST(anXMLNode) IS_OF_TYPE(anXMLNode, PLIST_PLIST)
@@ -559,13 +561,17 @@ CFPropertyList.propertyListFromXML = function(/*String | XMLNode*/ aStringOrXMLN
case PLIST_DICTIONARY: object = new CFMutableDictionary();
containers.push(object);
break;
-
+
case PLIST_NUMBER_REAL: object = parseFloat(CHILD_VALUE(XMLNode));
break;
case PLIST_NUMBER_INTEGER: object = parseInt(CHILD_VALUE(XMLNode), 10);
break;
-
- case PLIST_STRING: object = decodeHTMLComponent(FIRST_CHILD(XMLNode) ? CHILD_VALUE(XMLNode) : "");
+
+ case PLIST_STRING: if (HAS_ATTRIBUTE_VALUE(XMLNode, "type", "base64"))
+ object = FIRST_CHILD(XMLNode) ? CFData.decodeBase64ToString(CHILD_VALUE(XMLNode)) : "";
+ else
+ object = decodeHTMLComponent(FIRST_CHILD(XMLNode) ? CHILD_VALUE(XMLNode) : "");
+
break;
case PLIST_BOOLEAN_TRUE: object = YES;
diff --git a/Objective-J/CPLog.js b/Objective-J/CPLog.js
index 473ccfd88..dec1e0f06 100644
--- a/Objective-J/CPLog.js
+++ b/Objective-J/CPLog.js
@@ -36,40 +36,43 @@ var _CPLogRegistrations = {};
// Register Functions:
// Register a logger for all levels, or up to an optional max level
-GLOBAL(CPLogRegister) = function(aProvider, aMaxLevel)
+GLOBAL(CPLogRegister) = function(aProvider, aMaxLevel, aFormatter)
{
- CPLogRegisterRange(aProvider, CPLogLevels[0], aMaxLevel || CPLogLevels[CPLogLevels.length-1]);
+ CPLogRegisterRange(aProvider, CPLogLevels[0], aMaxLevel || CPLogLevels[CPLogLevels.length-1], aFormatter);
}
// Register a logger for a range of levels
-GLOBAL(CPLogRegisterRange) = function(aProvider, aMinLevel, aMaxLevel)
+GLOBAL(CPLogRegisterRange) = function(aProvider, aMinLevel, aMaxLevel, aFormatter)
{
var min = _CPLogLevelsInverted[aMinLevel];
var max = _CPLogLevelsInverted[aMaxLevel];
- if (min !== undefined && max !== undefined)
- for (var i = 0; i <= max; i++)
- CPLogRegisterSingle(aProvider, CPLogLevels[i]);
+ if (min !== undefined && max !== undefined && min <= max)
+ for (var i = min; i <= max; i++)
+ CPLogRegisterSingle(aProvider, CPLogLevels[i], aFormatter);
}
// Register a logger for a single level
-GLOBAL(CPLogRegisterSingle) = function(aProvider, aLevel)
+GLOBAL(CPLogRegisterSingle) = function(aProvider, aLevel, aFormatter)
{
if (!_CPLogRegistrations[aLevel])
_CPLogRegistrations[aLevel] = [];
- // prevent duplicate registrations
+ // prevent duplicate registrations, but change formatter
for (var i = 0; i < _CPLogRegistrations[aLevel].length; i++)
- if (_CPLogRegistrations[aLevel][i] === aProvider)
+ if (_CPLogRegistrations[aLevel][i][0] === aProvider)
+ {
+ _CPLogRegistrations[aLevel][i][1] = aFormatter;
return;
+ }
- _CPLogRegistrations[aLevel].push(aProvider);
+ _CPLogRegistrations[aLevel].push([aProvider, aFormatter]);
}
GLOBAL(CPLogUnregister) = function(aProvider) {
for (var aLevel in _CPLogRegistrations)
for (var i = 0; i < _CPLogRegistrations[aLevel].length; i++)
- if (_CPLogRegistrations[aLevel][i] === aProvider)
+ if (_CPLogRegistrations[aLevel][i][0] === aProvider)
_CPLogRegistrations[aLevel].splice(i--, 1); // decrement since we're removing an element
}
@@ -80,13 +83,16 @@ function _CPLogDispatch(parameters, aLevel, aTitle)
aTitle = CPLogDefaultTitle;
if (aLevel == undefined)
aLevel = CPLogDefaultLevel;
-
+
// use sprintf if param 0 is a string and there is more than one param. otherwise just convert param 0 to a string
var message = (typeof parameters[0] == "string" && parameters.length > 1) ? exports.sprintf.apply(null, parameters) : String(parameters[0]);
-
+
if (_CPLogRegistrations[aLevel])
for (var i = 0; i < _CPLogRegistrations[aLevel].length; i++)
- _CPLogRegistrations[aLevel][i](message, aLevel, aTitle);
+ {
+ var logger = _CPLogRegistrations[aLevel][i];
+ logger[0](message, aLevel, aTitle, logger[1]);
+ }
}
// Setup CPLog() and CPLog.xxx() aliases
@@ -101,7 +107,7 @@ for (var i = 0; i < CPLogLevels.length; i++)
var _CPFormatLogMessage = function(aString, aLevel, aTitle)
{
var now = new Date();
- aLevel = ( aLevel == null ? '' : ' [' + aLevel + ']' );
+ aLevel = ( aLevel == null ? '' : ' [' + CPLogColorize(aLevel, aLevel) + ']' );
if (typeof exports.sprintf == "function")
return exports.sprintf("%4d-%02d-%02d %02d:%02d:%02d.%03d %s%s: %s",
@@ -115,12 +121,12 @@ var _CPFormatLogMessage = function(aString, aLevel, aTitle)
// Loggers:
// CPLogConsole uses the built in "console" object
-GLOBAL(CPLogConsole) = function(aString, aLevel, aTitle)
+GLOBAL(CPLogConsole) = function(aString, aLevel, aTitle, aFormatter)
{
if (typeof console != "undefined")
{
- var message = _CPFormatLogMessage(aString, aLevel, aTitle);
-
+ var message = (aFormatter || _CPFormatLogMessage)(aString, aLevel, aTitle);
+
var logger = {
"fatal": "error",
"error": "error",
@@ -129,7 +135,7 @@ GLOBAL(CPLogConsole) = function(aString, aLevel, aTitle)
"debug": "debug",
"trace": "debug"
}[aLevel];
-
+
if (logger && console[logger])
console[logger](message);
else if (console.log)
@@ -158,7 +164,21 @@ try {
var stream;
-GLOBAL(CPLogPrint) = function(aString, aLevel, aTitle)
+GLOBAL(CPLogColorize) = function(aString, aLevel)
+{
+ if (stream)
+ {
+ // Try to determine if a colorizing stanza is already open, they can't be nested
+ if (/^.*\x00\w+\([^\x00]*$/.test(aString))
+ return aString;
+ else
+ return "\0" + (levelColorMap[aLevel] || "info") + "(" + aString + "\0)";
+ }
+ else
+ return aString;
+}
+
+GLOBAL(CPLogPrint) = function(aString, aLevel, aTitle, aFormatter)
{
if (stream === undefined) {
try {
@@ -168,56 +188,64 @@ GLOBAL(CPLogPrint) = function(aString, aLevel, aTitle)
}
}
+ var formatter = aFormatter || _CPFormatLogMessage;
+
if (stream) {
if (aLevel == "fatal" || aLevel == "error" || aLevel == "warn")
- stream.print("\0"+levelColorMap[aLevel]+"(" + _CPFormatLogMessage(aString, aLevel, aTitle) + "\0)");
+ stream.print(CPLogColorize(formatter(aString, aLevel, aTitle), aLevel));
else
- stream.print(_CPFormatLogMessage(aString, "\0"+levelColorMap[aLevel]+"(" + aLevel + "\0)", aTitle));
+ stream.print(formatter(aString, aLevel, aTitle));
} else if (typeof print != "undefined") {
- print(_CPFormatLogMessage(aString, aLevel, aTitle))
+ print(formatter(aString, aLevel, aTitle))
}
}
#else
+// A stub to allow the same formatter to be used for both stream and browser output
+GLOBAL(CPLogColorize) = function(aString, aLevel)
+{
+ return aString;
+}
+
// CPLogAlert uses basic browser alert() functions
-GLOBAL(CPLogAlert) = function(aString, aLevel, aTitle)
+GLOBAL(CPLogAlert) = function(aString, aLevel, aTitle, aFormatter)
{
if (typeof alert != "undefined" && !CPLogDisable)
{
- var message = _CPFormatLogMessage(aString, aLevel, aTitle);
+ var message = (aFormatter || _CPFormatLogMessage)(aString, aLevel, aTitle);
CPLogDisable = !confirm(message + "\n\n(Click cancel to stop log alerts)");
}
}
// CPLogPopup uses a slick popup window in the browser:
var CPLogWindow = null;
-GLOBAL(CPLogPopup) = function(aString, aLevel, aTitle)
+GLOBAL(CPLogPopup) = function(aString, aLevel, aTitle, aFormatter)
{
try {
if (CPLogDisable || window.open == undefined)
return;
-
+
if (!CPLogWindow || !CPLogWindow.document)
{
CPLogWindow = window.open("", "_blank", "width=600,height=400,status=no,resizable=yes,scrollbars=yes");
-
+
if (!CPLogWindow) {
CPLogDisable = !confirm(aString + "\n\n(Disable pop-up blocking for CPLog window; Click cancel to stop log alerts)");
return;
}
-
+
_CPLogInitPopup(CPLogWindow);
}
-
+
var logDiv = CPLogWindow.document.createElement("div");
logDiv.setAttribute("class", aLevel || "fatal");
- var message = _CPFormatLogMessage(aString, null, aTitle);
-
+ var message = (aFormatter || _CPFormatLogMessage)(aString, aFormatter ? aLevel : null, aTitle);
+
logDiv.appendChild(CPLogWindow.document.createTextNode(message));
CPLogWindow.log.appendChild(logDiv);
-
+
if (CPLogWindow.focusEnabled.checked)
CPLogWindow.focus();
if (CPLogWindow.blockEnabled.checked)
@@ -251,27 +279,27 @@ ul#options li{margin:0 0 0 0;padding:0 0 0 0;display:inline;} \
function _CPLogInitPopup(logWindow)
{
var doc = logWindow.document;
-
+
// HACK so that head is available below:
doc.writeln(""+CPLogPopupStyle+"");
-
+
doc.title = CPLogDefaultTitle + " Run Log";
-
+
var head = doc.getElementsByTagName("head")[0];
var body = doc.getElementsByTagName("body")[0];
-
+
var base = window.location.protocol + "//" + window.location.host + window.location.pathname;
base = base.substring(0,base.lastIndexOf("/")+1);
-
+
var div = doc.createElement("div");
div.setAttribute("id", "header");
body.appendChild(div);
-
+
// Enablers
var ul = doc.createElement("ul");
ul.setAttribute("id", "enablers");
div.appendChild(ul);
-
+
for (var i = 0; i < CPLogLevels.length; i++) {
var li = doc.createElement("li");
li.setAttribute("id", "en"+CPLogLevels[i]);
@@ -281,35 +309,35 @@ function _CPLogInitPopup(logWindow)
li.appendChild(doc.createTextNode(CPLogLevels[i]));
ul.appendChild(li);
}
-
+
// Options
var ul = doc.createElement("ul");
ul.setAttribute("id", "options");
div.appendChild(ul);
-
+
var options = {"focus":["Focus",false], "block":["Block",false], "wrap":["Wrap",false], "scroll":["Scroll",true], "close":["Close",true]};
for (o in options) {
var li = doc.createElement("li");
ul.appendChild(li);
-
+
logWindow[o+"Enabled"] = doc.createElement("input");
logWindow[o+"Enabled"].setAttribute("id", o);
logWindow[o+"Enabled"].setAttribute("type", "checkbox");
- if (options[o][1])
+ if (options[o][1])
logWindow[o+"Enabled"].setAttribute("checked", "checked");
li.appendChild(logWindow[o+"Enabled"]);
-
+
var label = doc.createElement("label");
label.setAttribute("for", o);
label.appendChild(doc.createTextNode(options[o][0]));
li.appendChild(label);
}
-
+
// Log
logWindow.log = doc.createElement("div");
logWindow.log.setAttribute("class", "enerror endebug enwarn eninfo enfatal entrace");
body.appendChild(logWindow.log);
-
+
logWindow.toggle = function(elem) {
var enabled = (elem.getAttribute("enabled") == "yes") ? "no" : "yes";
elem.setAttribute("enabled", enabled);
@@ -319,17 +347,17 @@ function _CPLogInitPopup(logWindow)
else
logWindow.log.className = logWindow.log.className.replace(new RegExp("[\\s]*"+elem.id, "g"), "");
}
-
+
// Scroll
logWindow.scrollToBottom = function() {
logWindow.scrollTo(0, body.offsetHeight);
}
-
+
// Wrap
logWindow.wrapEnabled.addEventListener("click", function() {
logWindow.log.setAttribute("wrap", logWindow.wrapEnabled.checked ? "yes" : "no");
}, false);
-
+
// Clear
logWindow.addEventListener("keydown", function(e) {
var e = e || logWindow.event;
@@ -340,7 +368,7 @@ function _CPLogInitPopup(logWindow)
e.preventDefault();
}
}, "false");
-
+
// Parent closing
window.addEventListener("unload", function() {
if (logWindow && logWindow.closeEnabled && logWindow.closeEnabled.checked) {
@@ -348,7 +376,7 @@ function _CPLogInitPopup(logWindow)
logWindow.close();
}
}, false);
-
+
// Log popup closing
logWindow.addEventListener("unload", function() {
if (!CPLogDisable) {
diff --git a/Objective-J/Preprocessor.js b/Objective-J/Preprocessor.js
index b27950a60..799d2ed26 100644
--- a/Objective-J/Preprocessor.js
+++ b/Objective-J/Preprocessor.js
@@ -61,7 +61,7 @@ var TOKEN_ACCESSORS = "accessors",
TOKEN_WHITESPACE = /^(?:(?:\s+$)|(?:\/(?:\/|\*)))/,
TOKEN_NUMBER = /^[+-]?\d+(([.]\d+)*([eE][+-]?\d+))?$/,
TOKEN_IDENTIFIER = /^[a-zA-Z_$](\w|$)*$/;
-
+
#define IS_WORD(token) /^\w+$/.test(token)
function Lexer(/*String*/ aString)
@@ -70,7 +70,7 @@ function Lexer(/*String*/ aString)
// FIXME: Used fixed regex
this._tokens = (aString + '\n').match(/\/\/.*(\r|\n)?|\/\*(?:.|\n|\r)*?\*\/|\w+\b|[+-]?\d+(([.]\d+)*([eE][+-]?\d+))?|"[^"\\]*(\\[\s\S][^"\\]*)*"|'[^'\\]*(\\[\s\S][^'\\]*)*'|\s+|./g);
this._context = [];
-
+
return this;
}
@@ -84,17 +84,17 @@ Lexer.prototype.pop = function()
this._index = this._context.pop();
}
-Lexer.prototype.peak = function(shouldSkipWhitespace)
+Lexer.prototype.peek = function(shouldSkipWhitespace)
{
if (shouldSkipWhitespace)
{
this.push();
var token = this.skip_whitespace();
this.pop();
-
+
return token;
}
-
+
return this._tokens[this._index + 1];
}
@@ -112,18 +112,18 @@ Lexer.prototype.last = function()
{
if (this._index < 0)
return NULL;
-
+
return this._tokens[this._index - 1];
}
-Lexer.prototype.skip_whitespace= function(shouldMoveBackwards)
-{
+Lexer.prototype.skip_whitespace = function(shouldMoveBackwards)
+{
var token;
-
+
if (shouldMoveBackwards)
- while((token = this.previous()) && TOKEN_WHITESPACE.test(token)) ;
+ while ((token = this.previous()) && TOKEN_WHITESPACE.test(token)) ;
else
- while((token = this.next()) && TOKEN_WHITESPACE.test(token)) ;
+ while ((token = this.next()) && TOKEN_WHITESPACE.test(token)) ;
return token;
}
@@ -187,7 +187,7 @@ var Preprocessor = function(/*String*/ aString, /*CFURL|String*/ aURL, /*unsigne
Preprocessor.prototype.setClassInfo = function(className, superClassName, ivars)
{
- this._classLookupTable[className] = {superClassName:superClassName, ivars:ivars};
+ this._classLookupTable[className] = { superClassName:superClassName, ivars:ivars };
}
Preprocessor.prototype.getClassInfo = function(className)
@@ -234,7 +234,7 @@ Preprocessor.prototype.accessors = function(tokens)
if (token != TOKEN_OPEN_PARENTHESIS)
{
tokens.previous();
-
+
return attributes;
}
@@ -249,7 +249,7 @@ Preprocessor.prototype.accessors = function(tokens)
if ((token = tokens.skip_whitespace()) == TOKEN_EQUAL)
{
value = tokens.skip_whitespace();
-
+
if (!IS_WORD(value))
throw new SyntaxError(this.error_message("*** @accessors attribute value not valid."));
@@ -257,7 +257,7 @@ Preprocessor.prototype.accessors = function(tokens)
{
if ((token = tokens.next()) != TOKEN_COLON)
throw new SyntaxError(this.error_message("*** @accessors setter attribute requires argument with \":\" at end of selector name."));
-
+
value += ":";
}
@@ -268,30 +268,30 @@ Preprocessor.prototype.accessors = function(tokens)
if (token == TOKEN_CLOSE_PARENTHESIS)
break;
-
+
if (token != TOKEN_COMMA)
throw new SyntaxError(this.error_message("*** Expected ',' or ')' in @accessors attribute list."));
}
-
+
return attributes;
}
Preprocessor.prototype.brackets = function(/*Lexer*/ tokens, /*StringBuffer*/ aStringBuffer)
{
var tuples = [];
-
+
while (this.preprocess(tokens, NULL, NULL, NULL, tuples[tuples.length] = [])) ;
if (tuples[0].length === 1)
{
CONCAT(aStringBuffer, '[');
-
+
// When we have an empty array literal ([]), tuples[0][0] will be an empty StringBuffer
CONCAT(aStringBuffer, tuples[0][0]);
-
+
CONCAT(aStringBuffer, ']');
}
-
+
else
{
var selector = new StringBuffer();
@@ -313,15 +313,15 @@ Preprocessor.prototype.brackets = function(/*Lexer*/ tokens, /*StringBuffer*/ aS
var index = 1,
count = tuples.length,
marg_list = new StringBuffer();
-
+
for(; index < count; ++index)
{
var pair = tuples[index];
-
- CONCAT(selector, pair[1])
+
+ CONCAT(selector, pair[1]);
CONCAT(marg_list, ", " + pair[0]);
}
-
+
CONCAT(aStringBuffer, ", \"");
CONCAT(aStringBuffer, selector); // FIXME: sel_getUid(selector + "") ?
CONCAT(aStringBuffer, '\"');
@@ -335,21 +335,21 @@ Preprocessor.prototype.directive = function(tokens, aStringBuffer, allowedDirect
// Grab the next token, preprocessor directives follow '@' immediately.
var buffer = aStringBuffer ? aStringBuffer : new StringBuffer(),
token = tokens.next();
-
- // To provide compatibility with Objective-C files, we convert NSString literals into
+
+ // To provide compatibility with Objective-C files, we convert NSString literals into
// toll-freed JavaScript/CPString strings.
if (token.charAt(0) == TOKEN_DOUBLE_QUOTE)
CONCAT(buffer, token);
-
- // Currently we simply swallow forward declarations and only provide them to allow
+
+ // Currently we simply swallow forward declarations and only provide them to allow
// compatibility with Objective-C files.
else if (token === TOKEN_CLASS)
{
tokens.skip_whitespace();
-
+
return;
}
-
+
// @implementation Class implementations
else if (token === TOKEN_IMPLEMENTATION)
this.implementation(tokens, buffer);
@@ -364,7 +364,7 @@ Preprocessor.prototype.directive = function(tokens, aStringBuffer, allowedDirect
// @selector
else if (token === TOKEN_SELECTOR)
this.selector(tokens, buffer);
-
+
if (!aStringBuffer)
return buffer;
}
@@ -472,7 +472,7 @@ Preprocessor.prototype.hash = function(tokens, aStringBuffer)
if (token === TOKEN_PRAGMA)
{
token = tokens.skip_whitespace();
-
+
// '#pragma mark' directive is used in Xcode editor for creating labels,
// which is irrelevant to Cappuccino - just swallow this line
if (token === TOKEN_MARK)
@@ -495,7 +495,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
instance_methods = new StringBuffer(),
class_methods = new StringBuffer();
-
+
if (!(/^\w/).test(class_name))
throw new Error(this.error_message("*** Expected class name, found \"" + class_name + "\"."));
@@ -506,16 +506,16 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
this._currentSelector = "";
// If we reach an open parenthesis, we are declaring a category.
- if((token = tokens.skip_whitespace()) == TOKEN_OPEN_PARENTHESIS)
+ if ((token = tokens.skip_whitespace()) == TOKEN_OPEN_PARENTHESIS)
{
token = tokens.skip_whitespace();
-
+
if (token == TOKEN_CLOSE_PARENTHESIS)
throw new SyntaxError(this.error_message("*** Can't Have Empty Category Name for class \"" + class_name + "\"."));
-
+
if (tokens.skip_whitespace() != TOKEN_CLOSE_PARENTHESIS)
throw new SyntaxError(this.error_message("*** Improper Category Definition for class \"" + class_name + "\"."));
-
+
CONCAT(buffer, "{\nvar the_class = objj_getClass(\"" + class_name + "\")\n");
CONCAT(buffer, "if(!the_class) throw new SyntaxError(\"*** Could not find definition for class \\\"" + class_name + "\\\"\");\n");
CONCAT(buffer, "var meta_class = the_class.isa;");
@@ -526,17 +526,17 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
if(token == TOKEN_COLON)
{
token = tokens.skip_whitespace();
-
+
if (!TOKEN_IDENTIFIER.test(token))
throw new SyntaxError(this.error_message("*** Expected class name, found \"" + token + "\"."));
-
+
superclass_name = token;
token = tokens.skip_whitespace();
}
-
+
CONCAT(buffer, "{var the_class = objj_allocateClassPair(" + superclass_name + ", \"" + class_name + "\"),\nmeta_class = the_class.isa;");
-
+
// If we are at an opening curly brace ('{'), then we have an ivar declaration.
if (token == TOKEN_OPEN_BRACE)
{
@@ -545,7 +545,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
declaration = [],
attributes,
accessors = {};
-
+
while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_BRACE)
{
if (token === TOKEN_PREPROCESSOR)
@@ -566,10 +566,10 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
var name = declaration[declaration.length - 1];
CONCAT(buffer, "new objj_ivar(\"" + name + "\")");
-
+
ivar_names[name] = 1;
declaration = [];
-
+
if (attributes)
{
accessors[name] = attributes;
@@ -586,7 +586,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
if (ivar_count)
CONCAT(buffer, "]);\n");
-
+
if (!token)
throw new SyntaxError(this.error_message("*** Expected '}'"));
@@ -599,44 +599,44 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
{
var accessor = accessors[ivar_name],
property = accessor["property"] || ivar_name;
-
+
// getter
var getterName = accessor["getter"] || property,
getterCode = "(id)" + getterName + "\n{\nreturn " + ivar_name + ";\n}";
if (IS_NOT_EMPTY(instance_methods))
CONCAT(instance_methods, ",\n");
-
+
CONCAT(instance_methods, this.method(new Lexer(getterCode), ivar_names));
-
+
// setter
if (accessor["readonly"])
continue;
-
+
var setterName = accessor["setter"];
-
+
if (!setterName)
{
var start = property.charAt(0) == '_' ? 1 : 0;
setterName = (start ? "_" : "") + "set" + property.substr(start, 1).toUpperCase() + property.substring(start + 1) + ":";
}
-
+
var setterCode = "(void)" + setterName + "(id)newValue\n{\n";
-
+
if (accessor["copy"])
setterCode += "if (" + ivar_name + " !== newValue)\n" + ivar_name + " = [newValue copy];\n}";
else
setterCode += ivar_name + " = newValue;\n}";
-
+
if (IS_NOT_EMPTY(instance_methods))
CONCAT(instance_methods, ",\n");
-
+
CONCAT(instance_methods, this.method(new Lexer(setterCode), ivar_names));
}
}
else
tokens.previous();
-
+
// We must make a new class object for our class definition.
CONCAT(buffer, "objj_registerClassPair(the_class);\n");
}
@@ -661,7 +661,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
if (IS_NOT_EMPTY(instance_methods))
CONCAT(instance_methods, ", ");
-
+
CONCAT(instance_methods, this.method(tokens, ivar_names));
}
// If we reach a # symbol, we may be at a C preprocessor directive.
@@ -681,21 +681,21 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
//else
// throw new SyntaxError(this.error_message("*** Expected a method declaration, or \"@end\", found \"" + token + "\"."));
}
-
+
if (IS_NOT_EMPTY(instance_methods))
{
CONCAT(buffer, "class_addMethods(the_class, [");
CONCAT(buffer, instance_methods);
CONCAT(buffer, "]);\n");
}
-
+
if (IS_NOT_EMPTY(class_methods))
{
CONCAT(buffer, "class_addMethods(meta_class, [");
CONCAT(buffer, class_methods);
CONCAT(buffer, "]);\n");
}
-
+
CONCAT(buffer, '}');
this._currentClass = "";
@@ -709,16 +709,16 @@ Preprocessor.prototype._import = function(tokens)
if (token === TOKEN_LESS_THAN)
{
- while((token = tokens.next()) && token !== TOKEN_GREATER_THAN)
+ while ((token = tokens.next()) && token !== TOKEN_GREATER_THAN)
URLString += token;
-
- if(!token)
+
+ if (!token)
throw new SyntaxError(this.error_message("*** Unterminated import statement."));
}
-
+
else if (token.charAt(0) === TOKEN_DOUBLE_QUOTE)
URLString = token.substr(1, token.length - 2);
-
+
else
throw new SyntaxError(this.error_message("*** Expecting '<' or '\"', found \"" + token + "\"."));
@@ -739,7 +739,7 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens, ivar_names)
ivar_names = ivar_names || {};
- while((token = tokens.skip_whitespace()) && token !== TOKEN_OPEN_BRACE && token !== TOKEN_SEMICOLON)
+ while ((token = tokens.skip_whitespace()) && token !== TOKEN_OPEN_BRACE && token !== TOKEN_SEMICOLON)
{
if (token == TOKEN_COLON)
{
@@ -747,33 +747,33 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens, ivar_names)
// Colons are part of the selector name
selector += token;
-
+
token = tokens.skip_whitespace();
-
+
if (token == TOKEN_OPEN_PARENTHESIS)
{
// Swallow parameter/return type. Perhaps later we can use this for debugging?
- while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS)
+ while ((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS)
type += token;
-
+
token = tokens.skip_whitespace();
}
-
+
// Add the type. If it's empty, add null instead.
- types[parameters.length+1] = type || null;
+ types[parameters.length + 1] = type || null;
// Since this follows a colon, this must be the parameter name.
parameters[parameters.length] = token;
if (token in ivar_names)
- throw new SyntaxError(this.error_message("*** Method ( "+selector+" ) uses a parameter name that is already in use ( "+token+" )"));
+ throw new SyntaxError(this.error_message("*** Method ( "+selector+" ) uses a parameter name that is already in use ( "+token+" )"));
}
else if (token == TOKEN_OPEN_PARENTHESIS)
{
var type = "";
// Since :( is handled above, this must be the return type, just swallow it.
- while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS)
+ while ((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS)
type += token;
// types[0] is the return argument
@@ -805,7 +805,7 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens, ivar_names)
var index = 0,
count = parameters.length;
-
+
CONCAT(buffer, "new objj_method(sel_getUid(\"");
CONCAT(buffer, selector);
CONCAT(buffer, "\"), function");
@@ -814,10 +814,10 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens, ivar_names)
if (this._flags & Preprocessor.Flags.IncludeDebugSymbols)
CONCAT(buffer, " $" + this._currentClass + "__" + selector.replace(/:/g, "_"));
-
+
CONCAT(buffer, "(self, _cmd");
-
- for(; index < count; ++index)
+
+ for (; index < count; ++index)
{
CONCAT(buffer, ", ");
CONCAT(buffer, parameters[index]);
@@ -845,53 +845,53 @@ Preprocessor.prototype.preprocess = function(tokens, /*StringBuffer*/ aStringBuf
if (tuple)
{
tuple[0] = buffer;
-
+
var bracket = false,
closures = [0, 0, 0];
}
-
+
while ((token = tokens.next()) && ((token !== terminator) || count))
{
if (tuple)
{
- // Ignore :'s the belong to tertiary operators (?:)
+ // Ignore :'s the belong to ternary operators (?:)
if (token === TOKEN_QUESTION_MARK)
++closures[2];
-
- // Ingore anything between { } and ()
+
+ // Ingore anything between { } and ()
else if (token === TOKEN_OPEN_BRACE)
++closures[0];
-
+
else if (token === TOKEN_CLOSE_BRACE)
--closures[0];
-
+
else if (token === TOKEN_OPEN_PARENTHESIS)
++closures[1];
-
+
else if (token === TOKEN_CLOSE_PARENTHESIS)
--closures[1];
-
+
// If not in {} and not in () and this is a colon and we don't belong to a tertiary operator OR this is a closing bracket...
- else if ((token === TOKEN_COLON && closures[2]-- === 0 ||
+ else if ((token === TOKEN_COLON && closures[2]-- === 0 ||
(bracket = (token === TOKEN_CLOSE_BRACKET))) &&
closures[0] === 0 && closures[1] === 0)
{
tokens.push(); // 1
-
- // If a bracket made us enter, go backwards skipping whitespace ([a b ] allowed),
+
+ // If a bracket made us enter, go backwards skipping whitespace ([a b ] allowed),
// if not grab token immediately behind us ([a b : c] not allowed
var label = bracket ? tokens.skip_whitespace(true) : tokens.previous(),
isEmptyLabel = TOKEN_WHITESPACE.test(label);
-
+
// The label must be an identifier, and preceded by whitespace, or whitespace itself (the "empty label")
if (isEmptyLabel || TOKEN_IDENTIFIER.test(label) && TOKEN_WHITESPACE.test(tokens.previous()))
{
tokens.push(); // 2
-
+
var last = tokens.skip_whitespace(true),
operatorCheck = true,
isDoubleOperator = false;
-
+
// unary or binary, still disables.
// + - + x is bad because it could be (+ - + x) or (a + - + x)
// the only good is unbroken chain
@@ -903,28 +903,28 @@ Preprocessor.prototype.preprocess = function(tokens, /*StringBuffer*/ aStringBuf
last = tokens.skip_whitespace(true);
isDoubleOperator = true;
}}
-
+
tokens.pop(); // 2
-
+
tokens.pop(); // 1
-
+
//alert(operatorCheck + "operatorCheck for " + label + " and " + last);
if (operatorCheck && (
-
+
// <)>