Merge branch 'refs/heads/master' into nib2cib-cpbrowser-compat
@@ -1,8 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
before_install:
|
||||
- jdk_switcher use openjdk6
|
||||
install: ./bootstrap.sh --quiet --noprompt --directory ./narwhal
|
||||
script: jake test
|
||||
env: PATH=$HOME/build/$TRAVIS_REPO_SLUG/narwhal/bin:$PATH CAPP_BUILD=$HOME/build/$TRAVIS_REPO_SLUG/Build NARWHAL_ENGINE=rhino
|
||||
|
||||
@@ -717,50 +717,40 @@ var bottomHeight = 71;
|
||||
|
||||
+ (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],
|
||||
[CPNull null],
|
||||
[CPNull null],
|
||||
[CPNull null],
|
||||
0.0,
|
||||
0.0,
|
||||
3.0,
|
||||
[CPColor blackColor],
|
||||
[CPFont systemFontOfSize:12.0],
|
||||
[CPNull null],
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
]
|
||||
forKeys:[@"size", @"content-inset", @"informative-offset", @"button-offset",
|
||||
@"message-text-alignment", @"message-text-color", @"message-text-font", @"message-text-shadow-color", @"message-text-shadow-offset",
|
||||
@"informative-text-alignment", @"informative-text-color", @"informative-text-font", @"informative-text-shadow-color", @"informative-text-shadow-offset",
|
||||
@"image-offset",
|
||||
@"information-image",
|
||||
@"warning-image",
|
||||
@"error-image",
|
||||
@"help-image",
|
||||
@"help-image-left-offset",
|
||||
@"help-image-pressed",
|
||||
@"suppression-button-y-offset",
|
||||
@"suppression-button-x-offset",
|
||||
@"default-elements-margin",
|
||||
@"suppression-button-text-color",
|
||||
@"suppression-button-text-font",
|
||||
@"suppression-button-text-shadow-color",
|
||||
@"suppression-button-text-shadow-offset",
|
||||
@"modal-window-button-margin-y",
|
||||
@"modal-window-button-margin-x",
|
||||
@"standard-window-button-margin-y",
|
||||
@"standard-window-button-margin-x"
|
||||
]];
|
||||
return [CPDictionary dictionaryWithObjectsAndKeys:
|
||||
CGSizeMake(400.0, 110.0), @"size",
|
||||
CGInsetMake(15, 15, 15, 50), @"content-inset",
|
||||
6, @"informative-offset",
|
||||
10, @"button-offset",
|
||||
CPJustifiedTextAlignment, @"message-text-alignment",
|
||||
[CPColor blackColor], @"message-text-color",
|
||||
[CPFont boldSystemFontOfSize:13.0], @"message-text-font",
|
||||
[CPNull null], @"message-text-shadow-color",
|
||||
CGSizeMakeZero(), @"message-text-shadow-offset",
|
||||
CPJustifiedTextAlignment, @"informative-text-alignment",
|
||||
[CPColor blackColor], @"informative-text-color",
|
||||
[CPFont systemFontOfSize:12.0], @"informative-text-font",
|
||||
[CPNull null], @"informative-text-shadow-color",
|
||||
CGSizeMakeZero(), @"informative-text-shadow-offset",
|
||||
CGPointMake(15, 12), @"image-offset",
|
||||
[CPNull null], @"information-image",
|
||||
[CPNull null], @"warning-image",
|
||||
[CPNull null], @"error-image",
|
||||
[CPNull null], @"help-image",
|
||||
15, @"help-image-left-offset",
|
||||
[CPNull null], @"help-image-pressed",
|
||||
0.0, @"suppression-button-y-offset",
|
||||
0.0, @"suppression-button-x-offset",
|
||||
3.0, @"default-elements-margin",
|
||||
[CPColor blackColor], @"suppression-button-text-color",
|
||||
[CPFont systemFontOfSize:12.0], @"suppression-button-text-font",
|
||||
[CPNull null], @"suppression-button-text-shadow-color",
|
||||
0.0, @"suppression-button-text-shadow-offset",
|
||||
0.0, @"modal-window-button-margin-y",
|
||||
0.0, @"modal-window-button-margin-x",
|
||||
0.0, @"standard-window-button-margin-y",
|
||||
0.0, @"standard-window-button-margin-x"
|
||||
];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -897,7 +897,9 @@
|
||||
if (![self canAdd])
|
||||
return;
|
||||
|
||||
[self insert:sender];
|
||||
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject];
|
||||
|
||||
[self addObject:newObject];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -909,9 +911,14 @@
|
||||
if (![self canInsert])
|
||||
return;
|
||||
|
||||
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject];
|
||||
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject],
|
||||
lastSelectedIndex = [_selectionIndexes lastIndex];
|
||||
|
||||
if (lastSelectedIndex)
|
||||
[self insertObject:newObject atArrangedObjectIndex:lastSelectedIndex];
|
||||
else
|
||||
[self addObject:newObject];
|
||||
|
||||
[self addObject:newObject];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1073,12 +1080,12 @@
|
||||
|
||||
@implementation CPArrayController (CPBinder)
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (theBinding == @"contentArray")
|
||||
if (aBinding == @"contentArray")
|
||||
return [_CPArrayControllerContentBinder class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1114,6 +1121,7 @@
|
||||
}
|
||||
|
||||
var isPlaceholder = CPIsControllerMarker(newValue);
|
||||
|
||||
if (isPlaceholder)
|
||||
{
|
||||
if (newValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
||||
|
||||
@@ -63,19 +63,12 @@ CPBelowBottom = 6;
|
||||
CPTextField _titleView;
|
||||
}
|
||||
|
||||
+ (id)boxEnclosingView:(CPView)aView
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
var box = [[self alloc] initWithFrame:CGRectMakeZero()],
|
||||
enclosingView = [aView superview];
|
||||
if ([aBinding hasPrefix:CPDisplayPatternTitleBinding])
|
||||
return [CPTitleWithPatternBinding class];
|
||||
|
||||
[box setAutoresizingMask:[aView autoresizingMask]];
|
||||
[box setFrameFromContentFrame:[aView frame]];
|
||||
|
||||
[enclosingView replaceSubview:aView with:box];
|
||||
|
||||
[box setContentView:aView];
|
||||
|
||||
return box;
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
@@ -96,6 +89,21 @@ CPBelowBottom = 6;
|
||||
@"content-margin"]];
|
||||
}
|
||||
|
||||
+ (id)boxEnclosingView:(CPView)aView
|
||||
{
|
||||
var box = [[self alloc] initWithFrame:CGRectMakeZero()],
|
||||
enclosingView = [aView superview];
|
||||
|
||||
[box setAutoresizingMask:[aView autoresizingMask]];
|
||||
[box setFrameFromContentFrame:[aView frame]];
|
||||
|
||||
[enclosingView replaceSubview:aView with:box];
|
||||
|
||||
[box setContentView:aView];
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frameRect
|
||||
{
|
||||
self = [super initWithFrame:frameRect];
|
||||
@@ -411,6 +419,14 @@ CPBelowBottom = 6;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forKey:(CPString)aKey
|
||||
{
|
||||
if (aKey === CPDisplayPatternTitleBinding)
|
||||
[self setTitle:aValue || @""];
|
||||
else
|
||||
[super setValue:aValue forKey:aKey];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
var bounds = CGRectMakeCopy([self bounds]);
|
||||
@@ -421,9 +437,9 @@ CPBelowBottom = 6;
|
||||
// NSBox does not include a horizontal flag for the separator type. We have to determine
|
||||
// the type of separator to draw by the width and height of the frame.
|
||||
if (CGRectGetWidth(bounds) === 5.0)
|
||||
return [self _drawVerticalSeperatorInRect:bounds];
|
||||
return [self _drawVerticalSeparatorInRect:bounds];
|
||||
else if (CGRectGetHeight(bounds) === 5.0)
|
||||
return [self _drawHorizontalSeperatorInRect:bounds];
|
||||
return [self _drawHorizontalSeparatorInRect:bounds];
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -438,6 +454,13 @@ CPBelowBottom = 6;
|
||||
bounds.size.height -= [_titleView frameSize].height;
|
||||
}
|
||||
|
||||
// Primary or secondary type boxes always draw the same way, unless they are CPNoBorder.
|
||||
if ((_boxType === CPBoxPrimary || _boxType === CPBoxSecondary) && _borderType !== CPNoBorder)
|
||||
{
|
||||
[self _drawPrimaryBorderInRect:bounds];
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_borderType)
|
||||
{
|
||||
case CPBezelBorder:
|
||||
@@ -455,7 +478,7 @@ CPBelowBottom = 6;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_drawHorizontalSeperatorInRect:(CGRect)aRect
|
||||
- (void)_drawHorizontalSeparatorInRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
@@ -467,7 +490,7 @@ CPBelowBottom = 6;
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
- (void)_drawVerticalSeperatorInRect:(CGRect)aRect
|
||||
- (void)_drawVerticalSeparatorInRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
@@ -504,22 +527,51 @@ CPBelowBottom = 6;
|
||||
shadowSize = [self valueForThemeAttribute:@"inner-shadow-size"],
|
||||
shadowColor = [self valueForThemeAttribute:@"inner-shadow-color"];
|
||||
|
||||
var baseRect = aRect;
|
||||
aRect = CGRectInset(aRect, borderWidth / 2.0, borderWidth / 2.0);
|
||||
|
||||
// clip the canvas to the actual content view in order to only display inner shadow
|
||||
CGContextBeginPath(context);
|
||||
CGContextAddPath(context, CGPathWithRoundedRectangleInRect(aRect, cornerRadius, cornerRadius, YES, YES, YES, YES));
|
||||
CGContextClip(context);
|
||||
CGContextSaveGState(context);
|
||||
|
||||
CGContextSetFillColor(context, [self fillColor]);
|
||||
CGContextSetStrokeColor(context, [self borderColor]);
|
||||
|
||||
CGContextSetShadowWithColor(context, shadowOffset, shadowSize, shadowColor);
|
||||
CGContextSetLineWidth(context, borderWidth);
|
||||
CGContextSetFillColor(context, [self fillColor]);
|
||||
CGContextFillRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
|
||||
CGContextStrokeRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
|
||||
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
|
||||
- (void)_drawPrimaryBorderInRect:(CGRect)aRect
|
||||
{
|
||||
// Draw the "primary" style CPBox.
|
||||
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
cornerRadius = [self cornerRadius],
|
||||
borderWidth = [self borderWidth],
|
||||
shadowOffset = [self valueForThemeAttribute:@"inner-shadow-offset"],
|
||||
shadowSize = [self valueForThemeAttribute:@"inner-shadow-size"],
|
||||
shadowColor = [self valueForThemeAttribute:@"inner-shadow-color"],
|
||||
baseRect = aRect;
|
||||
|
||||
aRect = CGRectInset(aRect, borderWidth / 2.0, borderWidth / 2.0);
|
||||
|
||||
CGContextSaveGState(context);
|
||||
|
||||
CGContextSetStrokeColor(context, [self borderColor]);
|
||||
CGContextSetLineWidth(context, borderWidth);
|
||||
CGContextSetFillColor(context, [self fillColor]);
|
||||
CGContextFillRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
|
||||
|
||||
CGContextBeginPath(context);
|
||||
// Note we can't use the 0.5 inset rectangle when setting up clipping. The clipping has to be
|
||||
// on integer coordinates for this to look right in Chrome.
|
||||
CGContextAddPath(context, CGPathWithRoundedRectangleInRect(baseRect, cornerRadius, cornerRadius, YES, YES, YES, YES));
|
||||
CGContextClip(context);
|
||||
CGContextSetShadowWithColor(context, shadowOffset, shadowSize, shadowColor);
|
||||
CGContextStrokeRoundedRectangleInRect(context, aRect, cornerRadius, YES, YES, YES, YES);
|
||||
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
|
||||
- (void)_drawNoBorderInRect:(CGRect)aRect
|
||||
{
|
||||
@@ -529,9 +581,6 @@ CPBelowBottom = 6;
|
||||
CGContextFillRect(context, aRect);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
|
||||
@@ -426,11 +426,26 @@
|
||||
|
||||
- (void)keyDown:(CPEvent)anEvent
|
||||
{
|
||||
var column = [self selectedColumn];
|
||||
if (column === -1)
|
||||
var key = [anEvent charactersIgnoringModifiers],
|
||||
column = [self selectedColumn];
|
||||
|
||||
if (column === CPNotFound)
|
||||
return;
|
||||
|
||||
[_tableViews[column] keyDown:anEvent];
|
||||
if (key === CPLeftArrowFunctionKey || key === CPRightArrowFunctionKey)
|
||||
{
|
||||
if (key === CPLeftArrowFunctionKey)
|
||||
{
|
||||
var previousColumn = column - 1,
|
||||
selectedRow = [self selectedRowInColumn:previousColumn];
|
||||
|
||||
[self selectRow:selectedRow inColumn:previousColumn];
|
||||
}
|
||||
else
|
||||
[self selectRow:0 inColumn:column + 1];
|
||||
}
|
||||
else
|
||||
[_tableViews[column] keyDown:anEvent];
|
||||
}
|
||||
|
||||
// SIZING
|
||||
@@ -829,30 +844,6 @@
|
||||
[super dragViewForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragViewOffset];
|
||||
}
|
||||
|
||||
- (void)moveUp:(id)sender
|
||||
{
|
||||
[super moveUp:sender];
|
||||
[_browser selectRow:[self selectedRow] inColumn:[_browser selectedColumn]];
|
||||
}
|
||||
|
||||
- (void)moveDown:(id)sender
|
||||
{
|
||||
[super moveDown:sender];
|
||||
[_browser selectRow:[self selectedRow] inColumn:[_browser selectedColumn]];
|
||||
}
|
||||
|
||||
- (void)moveLeft:(id)sender
|
||||
{
|
||||
var previousColumn = [_browser selectedColumn] - 1,
|
||||
selectedRow = [_browser selectedRowInColumn:previousColumn];
|
||||
|
||||
[_browser selectRow:selectedRow inColumn:previousColumn];
|
||||
}
|
||||
|
||||
- (void)moveRight:(id)sender
|
||||
{
|
||||
[_browser selectRow:0 inColumn:[_browser selectedColumn] + 1];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -60,11 +60,11 @@ CPMomentaryPushInButton = 7;
|
||||
CPMomentaryPushButton = 0;
|
||||
CPMomentaryLight = 7;
|
||||
|
||||
CPNoButtonMask = 0;
|
||||
CPContentsButtonMask = 1;
|
||||
CPPushInButtonMask = 2;
|
||||
CPGrayButtonMask = 4;
|
||||
CPBackgroundButtonMask = 8;
|
||||
CPNoButtonMask = 0;
|
||||
CPContentsButtonMask = 1;
|
||||
CPPushInButtonMask = 2;
|
||||
CPGrayButtonMask = 4;
|
||||
CPBackgroundButtonMask = 8;
|
||||
|
||||
CPNoCellMask = CPNoButtonMask;
|
||||
CPContentsCellMask = CPContentsButtonMask;
|
||||
@@ -72,16 +72,18 @@ CPPushInCellMask = CPPushInButtonMask;
|
||||
CPChangeGrayCellMask = CPGrayButtonMask;
|
||||
CPChangeBackgroundCellMask = CPBackgroundButtonMask;
|
||||
|
||||
CPButtonStateMixed = CPThemeState("mixed");
|
||||
CPButtonStateBezelStyleRounded = CPThemeState("rounded");
|
||||
CPButtonStateMixed = CPThemeState("mixed");
|
||||
CPButtonStateBezelStyleRounded = CPThemeState("rounded");
|
||||
|
||||
// add all future correspondance between bezel styles and theme state here.
|
||||
var CPButtonBezelStyleStateMap = [CPDictionary dictionaryWithObjects:[CPButtonStateBezelStyleRounded, nil]
|
||||
forKeys:[CPRoundedBezelStyle, CPRoundRectBezelStyle]];
|
||||
|
||||
var CPButtonBezelStyleStateMap = [CPDictionary dictionaryWithObjectsAndKeys:
|
||||
CPButtonStateBezelStyleRounded, CPRoundedBezelStyle,
|
||||
[CPNull null], CPRoundRectBezelStyle];
|
||||
|
||||
/// @cond IGNORE
|
||||
CPButtonDefaultHeight = 25.0;
|
||||
CPButtonImageOffset = 3.0;
|
||||
/// @endcond
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -117,6 +119,14 @@ CPButtonImageOffset = 3.0;
|
||||
BOOL _isTracking;
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding === CPTargetBinding || [aBinding hasPrefix:CPArgumentBinding])
|
||||
return [CPArgumentTargetBinding class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
+ (id)buttonWithTitle:(CPString)aTitle
|
||||
{
|
||||
return [self buttonWithTitle:aTitle theme:[CPTheme defaultTheme]];
|
||||
@@ -991,6 +1001,3 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//@import "CPCheckBox.j"
|
||||
//@import "CPRadio.j"
|
||||
|
||||
@@ -44,12 +44,12 @@ CPCheckBoxImageOffset = 4.0;
|
||||
return @"check-box";
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (theBinding === CPValueBinding)
|
||||
if (aBinding === CPValueBinding)
|
||||
return [_CPCheckBoxValueBinder class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#import "../Foundation/CPRange.h"
|
||||
#import "../Foundation/Ref.h"
|
||||
|
||||
@import <Foundation/CPArray.j>
|
||||
@import <Foundation/CPData.j>
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
@@ -28,6 +31,7 @@
|
||||
|
||||
@import "CPCollectionViewItem.j"
|
||||
@import "CPCompatibility.j"
|
||||
@import "CPDragServer_Constants.j"
|
||||
@import "CPPasteboard.j"
|
||||
@import "CPView.j"
|
||||
|
||||
@@ -67,6 +71,8 @@
|
||||
@return an array of drag types (CPString)
|
||||
*/
|
||||
|
||||
var HORIZONTAL_MARGIN = 2;
|
||||
|
||||
@implementation CPCollectionView : CPView
|
||||
{
|
||||
CPArray _content;
|
||||
@@ -103,6 +109,17 @@
|
||||
id _delegate;
|
||||
|
||||
CPEvent _mouseDownEvent;
|
||||
|
||||
BOOL _needsMinMaxItemSizeUpdate;
|
||||
CGSize _storedFrameSize;
|
||||
|
||||
BOOL _uniformSubviewsResizing @accessors(property=uniformSubviewsResizing);
|
||||
BOOL _lockResizing;
|
||||
|
||||
CPInteger _currentDropIndex;
|
||||
CPDragOperation _currentDragOperation;
|
||||
|
||||
_CPCollectionViewDropIndicator _dropView;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -111,28 +128,52 @@
|
||||
|
||||
if (self)
|
||||
{
|
||||
_items = [];
|
||||
_content = [];
|
||||
_maxNumberOfRows = 0;
|
||||
_maxNumberOfColumns = 0;
|
||||
|
||||
_cachedItems = [];
|
||||
|
||||
_itemSize = CGSizeMakeZero();
|
||||
_minItemSize = CGSizeMakeZero();
|
||||
_maxItemSize = CGSizeMakeZero();
|
||||
_minItemSize = _CGSizeMakeZero();
|
||||
_maxItemSize = _CGSizeMakeZero();
|
||||
|
||||
[self setBackgroundColors:nil];
|
||||
|
||||
_verticalMargin = 5.0;
|
||||
_tileWidth = -1.0;
|
||||
|
||||
_selectionIndexes = [CPIndexSet indexSet];
|
||||
_allowsEmptySelection = YES;
|
||||
_isSelectable = YES;
|
||||
_allowsEmptySelection = YES;
|
||||
|
||||
[self _init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_content = [];
|
||||
|
||||
_items = [];
|
||||
_cachedItems = [];
|
||||
|
||||
_numberOfColumns = CPNotFound;
|
||||
_numberOfRows = CPNotFound;
|
||||
|
||||
_itemSize = _CGSizeMakeZero();
|
||||
|
||||
_selectionIndexes = [CPIndexSet indexSet];
|
||||
|
||||
_storedFrameSize = _CGSizeMakeZero();
|
||||
|
||||
_needsMinMaxItemSizeUpdate = YES;
|
||||
_uniformSubviewsResizing = NO;
|
||||
_lockResizing = NO;
|
||||
|
||||
_currentDropIndex = -1;
|
||||
_currentDragOperation = CPDragOperationNone;
|
||||
_dropView = nil;
|
||||
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[self setAutoresizingMask:0];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the item prototype to \c anItem
|
||||
|
||||
@@ -187,7 +228,7 @@
|
||||
_itemForDragging = nil;
|
||||
_itemPrototype = anItem;
|
||||
|
||||
[self reloadContent];
|
||||
[self _reloadContentCachingRemovedItems:NO];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -210,13 +251,7 @@
|
||||
item = _cachedItems.pop();
|
||||
|
||||
else
|
||||
{
|
||||
if (!_itemData)
|
||||
if (_itemPrototype)
|
||||
_itemData = [CPKeyedArchiver archivedDataWithRootObject:_itemPrototype];
|
||||
|
||||
item = [CPKeyedUnarchiver unarchiveObjectWithData:_itemData];
|
||||
}
|
||||
item = [_itemPrototype copy];
|
||||
|
||||
[item setRepresentedObject:anObject];
|
||||
[[item view] setFrameSize:_itemSize];
|
||||
@@ -382,8 +417,13 @@
|
||||
return [_selectionIndexes copy];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)reloadContent
|
||||
{
|
||||
[self _reloadContentCachingRemovedItems:YES];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_reloadContentCachingRemovedItems:(BOOL)shouldCache
|
||||
{
|
||||
// Remove current views
|
||||
var count = _items.length;
|
||||
@@ -393,7 +433,8 @@
|
||||
[[_items[count] view] removeFromSuperview];
|
||||
[_items[count] setSelected:NO];
|
||||
|
||||
_cachedItems.push(_items[count]);
|
||||
if (shouldCache)
|
||||
_cachedItems.push(_items[count]);
|
||||
}
|
||||
|
||||
_items = [];
|
||||
@@ -417,91 +458,163 @@
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound && index < count)
|
||||
[_items[index] setSelected:YES];
|
||||
|
||||
[self tile];
|
||||
[self tileIfNeeded:NO];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)tile
|
||||
- (void)resizeSubviewsWithOldSize:(CPSize)oldBoundsSize
|
||||
{
|
||||
var width = CGRectGetWidth([self bounds]);
|
||||
// Desactivate subviews autoresizing
|
||||
}
|
||||
|
||||
if (width == _tileWidth)
|
||||
- (void)resizeWithOldSuperviewSize:(CPSize)oldBoundsSize
|
||||
{
|
||||
if (_lockResizing)
|
||||
return;
|
||||
|
||||
// We try to fit as many views per row as possible. Any remaining space is then
|
||||
// either proportioned out to the views (if their minSize != maxSize) or used as
|
||||
// margin
|
||||
var itemSize = CGSizeMakeCopy(_minItemSize);
|
||||
_lockResizing = YES;
|
||||
|
||||
_numberOfColumns = MAX(1.0, FLOOR(width / itemSize.width));
|
||||
[self tile];
|
||||
|
||||
if (_maxNumberOfColumns > 0)
|
||||
_numberOfColumns = MIN(_maxNumberOfColumns, _numberOfColumns);
|
||||
|
||||
var remaining = width - _numberOfColumns * itemSize.width,
|
||||
itemsNeedSizeUpdate = NO;
|
||||
|
||||
if (remaining > 0 && itemSize.width < _maxItemSize.width)
|
||||
itemSize.width = MIN(_maxItemSize.width, itemSize.width + FLOOR(remaining / _numberOfColumns));
|
||||
|
||||
// When we ONE column and a non-integral width, the FLOORing above can cause the item width to be smaller than the total width.
|
||||
if (_maxNumberOfColumns == 1 && itemSize.width < _maxItemSize.width && itemSize.width < width)
|
||||
itemSize.width = MIN(_maxItemSize.width, width);
|
||||
|
||||
if (!CGSizeEqualToSize(_itemSize, itemSize))
|
||||
{
|
||||
_itemSize = itemSize;
|
||||
itemsNeedSizeUpdate = YES;
|
||||
}
|
||||
|
||||
var index = 0,
|
||||
count = _items.length;
|
||||
|
||||
if (_maxNumberOfColumns > 0 && _maxNumberOfRows > 0)
|
||||
count = MIN(count, _maxNumberOfColumns * _maxNumberOfRows);
|
||||
|
||||
_numberOfRows = CEIL(count / _numberOfColumns);
|
||||
|
||||
_horizontalMargin = FLOOR((width - _numberOfColumns * itemSize.width) / (_numberOfColumns + 1));
|
||||
|
||||
var x = _horizontalMargin,
|
||||
y = -itemSize.height;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
if (index % _numberOfColumns == 0)
|
||||
{
|
||||
x = _horizontalMargin;
|
||||
y += _verticalMargin + itemSize.height;
|
||||
}
|
||||
|
||||
var view = [_items[index] view];
|
||||
|
||||
[view setFrameOrigin:CGPointMake(x, y)];
|
||||
|
||||
if (itemsNeedSizeUpdate)
|
||||
[view setFrameSize:_itemSize];
|
||||
|
||||
x += itemSize.width + _horizontalMargin;
|
||||
}
|
||||
|
||||
var superview = [self superview],
|
||||
proposedHeight = y + itemSize.height + _verticalMargin;
|
||||
|
||||
if ([superview isKindOfClass:[CPClipView class]])
|
||||
{
|
||||
var superviewSize = [superview bounds].size;
|
||||
proposedHeight = MAX(superviewSize.height, proposedHeight);
|
||||
}
|
||||
|
||||
_tileWidth = width;
|
||||
[self setFrameSize:CGSizeMake(width, proposedHeight)];
|
||||
_tileWidth = -1.0;
|
||||
_lockResizing = NO;
|
||||
}
|
||||
|
||||
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
|
||||
- (void)tile
|
||||
{
|
||||
[self tile];
|
||||
[self tileIfNeeded:!_uniformSubviewsResizing];
|
||||
}
|
||||
|
||||
- (void)tileIfNeeded:(BOOL)lazyFlag
|
||||
{
|
||||
var frameSize = [[self superview] frameSize],
|
||||
count = _items.length,
|
||||
oldNumberOfColumns = _numberOfColumns,
|
||||
oldNumberOfRows = _numberOfRows,
|
||||
oldItemSize = _itemSize,
|
||||
storedFrameSize = _storedFrameSize;
|
||||
|
||||
// No need to tile if we are not yet placed in the view hierarchy.
|
||||
if (!frameSize)
|
||||
return;
|
||||
|
||||
[self _updateMinMaxItemSizeIfNeeded];
|
||||
|
||||
[self _computeGridWithSize:frameSize count:AT_REF(count)];
|
||||
|
||||
//CPLog.debug("frameSize="+CPStringFromSize(frameSize) + "itemSize="+CPStringFromSize(itemSize) + " ncols=" + colsRowsCount[0] +" nrows="+ colsRowsCount[1]+" displayCount="+ colsRowsCount[2]);
|
||||
|
||||
[self setFrameSize:_storedFrameSize];
|
||||
|
||||
//CPLog.debug("OLD " + oldNumberOfColumns + " NEW " + _numberOfColumns);
|
||||
if (!lazyFlag ||
|
||||
_numberOfColumns !== oldNumberOfColumns ||
|
||||
_numberOfRows !== oldNumberOfRows ||
|
||||
!_CGSizeEqualToSize(_itemSize, oldItemSize))
|
||||
|
||||
[self displayItems:_items frameSize:_storedFrameSize itemSize:_itemSize columns:_numberOfColumns rows:_numberOfRows count:count];
|
||||
}
|
||||
|
||||
- (void)_computeGridWithSize:(CGSize)aSuperviewSize count:(Function)countRef
|
||||
{
|
||||
var width = aSuperviewSize.width,
|
||||
height = aSuperviewSize.height,
|
||||
itemSize = _CGSizeMakeCopy(_minItemSize),
|
||||
maxItemSizeWidth = _maxItemSize.width,
|
||||
maxItemSizeHeight = _maxItemSize.height,
|
||||
itemsCount = [_items count],
|
||||
numberOfRows,
|
||||
numberOfColumns;
|
||||
|
||||
numberOfColumns = FLOOR(width / itemSize.width);
|
||||
|
||||
if (maxItemSizeWidth == 0)
|
||||
numberOfColumns = MIN(numberOfColumns, _maxNumberOfColumns);
|
||||
|
||||
if (_maxNumberOfColumns > 0)
|
||||
numberOfColumns = MIN(MIN(_maxNumberOfColumns, itemsCount), numberOfColumns);
|
||||
|
||||
numberOfColumns = MAX(1.0, numberOfColumns);
|
||||
|
||||
itemSize.width = FLOOR(width / numberOfColumns);
|
||||
|
||||
if (maxItemSizeWidth > 0)
|
||||
{
|
||||
itemSize.width = MIN(maxItemSizeWidth, itemSize.width);
|
||||
|
||||
if (numberOfColumns == 1)
|
||||
itemSize.width = MIN(maxItemSizeWidth, width);
|
||||
}
|
||||
|
||||
numberOfRows = MAX(1.0 , MIN(CEIL(itemsCount / numberOfColumns), _maxNumberOfRows));
|
||||
|
||||
height = MAX(height, numberOfRows * (_minItemSize.height + _verticalMargin));
|
||||
|
||||
var itemSizeHeight = FLOOR(height / numberOfRows);
|
||||
|
||||
if (maxItemSizeHeight > 0)
|
||||
itemSizeHeight = MIN(itemSizeHeight, maxItemSizeHeight);
|
||||
|
||||
_itemSize = _CGSizeMake(MAX(_minItemSize.width, itemSize.width), MAX(_minItemSize.height, itemSizeHeight));
|
||||
_storedFrameSize = _CGSizeMake(MAX(width, _minItemSize.width), height);
|
||||
_numberOfColumns = numberOfColumns;
|
||||
_numberOfRows = numberOfRows;
|
||||
countRef(MIN(itemsCount, numberOfColumns * numberOfRows));
|
||||
}
|
||||
|
||||
- (void)displayItems:(CPArray)displayItems frameSize:(CGSize)aFrameSize itemSize:(CGSize)anItemSize columns:(CPInteger)numberOfColumns rows:(CPInteger)numberOfRows count:(CPInteger)displayCount
|
||||
{
|
||||
// CPLog.debug("DISPLAY ITEMS " + numberOfColumns + " " + numberOfRows);
|
||||
|
||||
_horizontalMargin = _uniformSubviewsResizing ? FLOOR((aFrameSize.width - numberOfColumns * anItemSize.width) / (numberOfColumns + 1)) : HORIZONTAL_MARGIN;
|
||||
|
||||
var x = _horizontalMargin,
|
||||
y = -anItemSize.height;
|
||||
|
||||
[displayItems enumerateObjectsUsingBlock:function(item, idx, stop)
|
||||
{
|
||||
var view = [item view];
|
||||
|
||||
if (idx >= displayCount)
|
||||
{
|
||||
[view setFrameOrigin:_CGPointMake(-anItemSize.width, -anItemSize.height)];
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx % numberOfColumns == 0)
|
||||
{
|
||||
x = _horizontalMargin;
|
||||
y += _verticalMargin + anItemSize.height;
|
||||
}
|
||||
|
||||
[view setFrameOrigin:_CGPointMake(x, y)];
|
||||
[view setFrameSize:anItemSize];
|
||||
|
||||
x += anItemSize.width + _horizontalMargin;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_updateMinMaxItemSizeIfNeeded
|
||||
{
|
||||
if (!_needsMinMaxItemSizeUpdate)
|
||||
return;
|
||||
|
||||
var prototypeView;
|
||||
|
||||
if (_itemPrototype && (prototypeView = [_itemPrototype view]))
|
||||
{
|
||||
if (_minItemSize.width == 0)
|
||||
_minItemSize.width = [prototypeView frameSize].width;
|
||||
|
||||
if (_minItemSize.height == 0)
|
||||
_minItemSize.height = [prototypeView frameSize].height;
|
||||
|
||||
if (_maxItemSize.height == 0 && !([prototypeView autoresizingMask] & CPViewHeightSizable))
|
||||
_maxItemSize.height = [prototypeView frameSize].height;
|
||||
|
||||
if (_maxItemSize.width == 0 && !([prototypeView autoresizingMask] & CPViewWidthSizable))
|
||||
_maxItemSize.width = [prototypeView frameSize].width;
|
||||
|
||||
_needsMinMaxItemSizeUpdate = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Laying Out the Collection View
|
||||
@@ -574,10 +687,14 @@
|
||||
{
|
||||
if (aSize === nil || aSize === undefined)
|
||||
[CPException raise:CPInvalidArgumentException reason:"Invalid value provided for minimum size"];
|
||||
if (CGSizeEqualToSize(_minItemSize, aSize))
|
||||
|
||||
if (_CGSizeEqualToSize(_minItemSize, aSize))
|
||||
return;
|
||||
|
||||
_minItemSize = CGSizeMakeCopy(aSize);
|
||||
_minItemSize = _CGSizeMakeCopy(aSize);
|
||||
|
||||
if (_CGSizeEqualToSize(_minItemSize, _CGSizeMakeZero()))
|
||||
_needsMinMaxItemSizeUpdate = YES;
|
||||
|
||||
[self tile];
|
||||
}
|
||||
@@ -596,10 +713,13 @@
|
||||
*/
|
||||
- (void)setMaxItemSize:(CGSize)aSize
|
||||
{
|
||||
if (CGSizeEqualToSize(_maxItemSize, aSize))
|
||||
if (_CGSizeEqualToSize(_maxItemSize, aSize))
|
||||
return;
|
||||
|
||||
_maxItemSize = CGSizeMakeCopy(aSize);
|
||||
_maxItemSize = _CGSizeMakeCopy(aSize);
|
||||
|
||||
// if (_maxItemSize.width == 0 || _maxItemSize.height == 0)
|
||||
// _needsMinMaxItemSizeUpdate = YES;
|
||||
|
||||
[self tile];
|
||||
}
|
||||
@@ -620,7 +740,7 @@
|
||||
_backgroundColors = backgroundColors;
|
||||
|
||||
if (!_backgroundColors)
|
||||
_backgroundColors = [CPColor whiteColor];
|
||||
_backgroundColors = [[CPColor whiteColor]];
|
||||
|
||||
if ([_backgroundColors count] === 1)
|
||||
[self setBackgroundColor:_backgroundColors[0]];
|
||||
@@ -668,9 +788,9 @@
|
||||
newSelectedRange = nil;
|
||||
|
||||
if (index < firstSelectedIndex)
|
||||
newSelectedRange = CPMakeRange(index, (firstSelectedIndex - index) + 1);
|
||||
newSelectedRange = _CPMakeRange(index, (firstSelectedIndex - index) + 1);
|
||||
else
|
||||
newSelectedRange = CPMakeRange(firstSelectedIndex, (index - firstSelectedIndex) + 1);
|
||||
newSelectedRange = _CPMakeRange(firstSelectedIndex, (index - firstSelectedIndex) + 1);
|
||||
|
||||
indexes = [[self selectionIndexes] copy];
|
||||
[indexes addIndexesInRange:newSelectedRange];
|
||||
@@ -692,65 +812,6 @@
|
||||
[self setSelectionIndexes:[CPIndexSet indexSet]];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
// Don't crash if we never registered the intial click.
|
||||
if (!_mouseDownEvent)
|
||||
return;
|
||||
|
||||
var locationInWindow = [anEvent locationInWindow],
|
||||
mouseDownLocationInWindow = [_mouseDownEvent locationInWindow];
|
||||
|
||||
// FIXME: This is because Safari's drag hysteresis is 3px x 3px
|
||||
if ((ABS(locationInWindow.x - mouseDownLocationInWindow.x) < 3) &&
|
||||
(ABS(locationInWindow.y - mouseDownLocationInWindow.y) < 3))
|
||||
return;
|
||||
|
||||
if (![_delegate respondsToSelector:@selector(collectionView:dragTypesForItemsAtIndexes:)])
|
||||
return;
|
||||
|
||||
// If we don't have any selected items, we've clicked away, and thus the drag is meaningless.
|
||||
if (![_selectionIndexes count])
|
||||
return;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(collectionView:canDragItemsAtIndexes:withEvent:)] &&
|
||||
![_delegate collectionView:self canDragItemsAtIndexes:_selectionIndexes withEvent:_mouseDownEvent])
|
||||
return;
|
||||
|
||||
// Set up the pasteboard
|
||||
var dragTypes = [_delegate collectionView:self dragTypesForItemsAtIndexes:_selectionIndexes];
|
||||
|
||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:dragTypes owner:self];
|
||||
|
||||
if (!_itemForDragging)
|
||||
_itemForDragging = [self newItemForRepresentedObject:_content[[_selectionIndexes firstIndex]]];
|
||||
else
|
||||
[_itemForDragging setRepresentedObject:_content[[_selectionIndexes firstIndex]]];
|
||||
|
||||
var view = [_itemForDragging view];
|
||||
|
||||
[view setFrameSize:_itemSize];
|
||||
[view setAlphaValue:0.7];
|
||||
|
||||
[self dragView:view
|
||||
at:[[_items[[_selectionIndexes firstIndex]] view] frame].origin
|
||||
offset:CGSizeMakeZero()
|
||||
event:_mouseDownEvent
|
||||
pasteboard:nil
|
||||
source:self
|
||||
slideBack:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
Places the selected items on the specified pasteboard. The items are requested from the collection's delegate.
|
||||
@param aPasteboard the pasteboard to put the items on
|
||||
@param aType the format the pasteboard data
|
||||
*/
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
[aPasteboard setData:[_delegate collectionView:self dataForItemsAtIndexes:_selectionIndexes forType:aType] forType:aType];
|
||||
}
|
||||
|
||||
// Cappuccino Additions
|
||||
|
||||
/*!
|
||||
@@ -768,6 +829,13 @@
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)setUniformSubviewsResizing:(float)flag
|
||||
{
|
||||
_uniformSubviewsResizing = flag;
|
||||
[self tileIfNeeded:NO];
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Gets the collection view's current vertical spacing between elements.
|
||||
*/
|
||||
@@ -810,10 +878,17 @@
|
||||
|
||||
- (int)_indexAtPoint:(CGPoint)thePoint
|
||||
{
|
||||
var row = FLOOR(thePoint.y / (_itemSize.height + _verticalMargin)),
|
||||
column = FLOOR(thePoint.x / (_itemSize.width + _horizontalMargin));
|
||||
var column = FLOOR(thePoint.x / (_itemSize.width + _horizontalMargin));
|
||||
|
||||
return row * _numberOfColumns + column;
|
||||
if (column < _numberOfColumns)
|
||||
{
|
||||
var row = FLOOR(thePoint.y / (_itemSize.height + _verticalMargin));
|
||||
|
||||
if (row < _numberOfRows)
|
||||
return (row * _numberOfColumns + column);
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (CPCollectionViewItem)itemAtIndex:(unsigned)anIndex
|
||||
@@ -844,6 +919,293 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPCollectionView (DragAndDrop)
|
||||
/*
|
||||
TODO: dropOperation is not supported yet. The visible drop operation is like CPCollectionViewDropBefore.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Places the selected items on the specified pasteboard. The items are requested from the collection's delegate.
|
||||
@param aPasteboard the pasteboard to put the items on
|
||||
@param aType the format the pasteboard data
|
||||
*/
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
[aPasteboard setData:[_delegate collectionView:self dataForItemsAtIndexes:_selectionIndexes forType:aType] forType:aType];
|
||||
}
|
||||
|
||||
- (void)_createDropIndicatorIfNeeded
|
||||
{
|
||||
// Create and position the drop indicator view.
|
||||
|
||||
if (!_dropView)
|
||||
_dropView = [[_CPCollectionViewDropIndicator alloc] initWithFrame:_CGRectMake(-8, -8, 0, 0)];
|
||||
|
||||
[_dropView setFrameSize:_CGSizeMake(10, _itemSize.height + _verticalMargin)];
|
||||
[self addSubview:_dropView];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
// Don't crash if we never registered the intial click.
|
||||
if (!_mouseDownEvent)
|
||||
return;
|
||||
|
||||
[self _createDropIndicatorIfNeeded];
|
||||
|
||||
var locationInWindow = [anEvent locationInWindow],
|
||||
mouseDownLocationInWindow = [_mouseDownEvent locationInWindow];
|
||||
|
||||
// FIXME: This is because Safari's drag hysteresis is 3px x 3px
|
||||
if ((ABS(locationInWindow.x - mouseDownLocationInWindow.x) < 3) &&
|
||||
(ABS(locationInWindow.y - mouseDownLocationInWindow.y) < 3))
|
||||
return;
|
||||
|
||||
if (![_delegate respondsToSelector:@selector(collectionView:dragTypesForItemsAtIndexes:)])
|
||||
return;
|
||||
|
||||
// If we don't have any selected items, we've clicked away, and thus the drag is meaningless.
|
||||
if (![_selectionIndexes count])
|
||||
return;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(collectionView:canDragItemsAtIndexes:withEvent:)] &&
|
||||
![_delegate collectionView:self canDragItemsAtIndexes:_selectionIndexes withEvent:_mouseDownEvent])
|
||||
return;
|
||||
|
||||
// Set up the pasteboard
|
||||
var dragTypes = [_delegate collectionView:self dragTypesForItemsAtIndexes:_selectionIndexes];
|
||||
|
||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:dragTypes owner:self];
|
||||
|
||||
var dragImageOffset = _CGSizeMakeZero(),
|
||||
view = [self _draggingViewForItemsAtIndexes:_selectionIndexes withEvent:_mouseDownEvent offset:dragImageOffset];
|
||||
|
||||
[view setFrameSize:_itemSize];
|
||||
[view setAlphaValue:0.7];
|
||||
|
||||
var dragLocation = [self convertPoint:locationInWindow fromView:nil],
|
||||
dragPoint = _CGPointMake(dragLocation.x - _itemSize.width / 2 , dragLocation.y - _itemSize.height / 2);
|
||||
|
||||
[self dragView:view
|
||||
at:dragPoint
|
||||
offset:dragImageOffset
|
||||
event:_mouseDownEvent
|
||||
pasteboard:nil
|
||||
source:self
|
||||
slideBack:YES];
|
||||
}
|
||||
|
||||
- (CPView)_draggingViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)anEvent offset:(CGPoint)offset
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(collectionView:draggingViewForItemsAtIndexes:withEvent:offset:)])
|
||||
return [_delegate collectionView:self draggingViewForItemsAtIndexes:indexes withEvent:anEvent offset:offset];
|
||||
|
||||
return [self draggingViewForItemsAtIndexes:indexes withEvent:anEvent offset:offset];
|
||||
}
|
||||
|
||||
- (CPView)draggingViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset
|
||||
{
|
||||
var idx = _content[[indexes firstIndex]];
|
||||
|
||||
if (!_itemForDragging)
|
||||
_itemForDragging = [self newItemForRepresentedObject:idx];
|
||||
else
|
||||
[_itemForDragging setRepresentedObject:idx];
|
||||
|
||||
return [_itemForDragging view];
|
||||
}
|
||||
|
||||
- (BOOL)_canDragItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)anEvent
|
||||
{
|
||||
if ([self respondsToSelector:@selector(collectionView:canDragItemsAtIndexes:withEvent:)])
|
||||
return [_delegate collectionView:self canDragItemsAtIndexes:indexes withEvent:anEvent];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CPDragOperation)draggingEntered:(id)draggingInfo
|
||||
{
|
||||
var dropIndex = -1,
|
||||
dropIndexRef = AT_REF(dropIndex),
|
||||
dragOp = [self _validateDragWithInfo:draggingInfo dropIndex:dropIndexRef dropOperation:1];
|
||||
|
||||
dropIndex = dropIndexRef();
|
||||
|
||||
[self _createDropIndicatorIfNeeded];
|
||||
|
||||
[self _updateDragAndDropStateWithDraggingInfo:draggingInfo newDragOperation:dragOp newDropIndex:dropIndex newDropOperation:1];
|
||||
|
||||
return _currentDragOperation;
|
||||
}
|
||||
|
||||
- (CPDragOperation)draggingUpdated:(id)draggingInfo
|
||||
{
|
||||
if (![self _dropIndexDidChange:draggingInfo])
|
||||
return _currentDragOperation;
|
||||
|
||||
var dropIndex,
|
||||
dropIndexRef = AT_REF(dropIndex);
|
||||
|
||||
var dragOperation = [self _validateDragWithInfo:draggingInfo dropIndex:dropIndexRef dropOperation:1];
|
||||
|
||||
dropIndex = dropIndexRef();
|
||||
|
||||
[self _updateDragAndDropStateWithDraggingInfo:draggingInfo newDragOperation:dragOperation newDropIndex:dropIndex newDropOperation:1];
|
||||
|
||||
return dragOperation;
|
||||
}
|
||||
|
||||
- (CPDragOperation)_validateDragWithInfo:(id)draggingInfo dropIndex:(Function)dropIndexRef dropOperation:(int)dropOperation
|
||||
{
|
||||
var result = CPDragOperationMove,
|
||||
dropIndex = [self _dropIndexForDraggingInfo:draggingInfo proposedDropOperation:dropOperation];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(collectionView:validateDrop:proposedIndex:dropOperation:)])
|
||||
{
|
||||
var dropIndexRef2 = AT_REF(dropIndex);
|
||||
|
||||
result = [_delegate collectionView:self validateDrop:draggingInfo proposedIndex:dropIndexRef2 dropOperation:dropOperation];
|
||||
|
||||
if (result !== CPDragOperationNone)
|
||||
{
|
||||
dropIndex = dropIndexRef2();
|
||||
}
|
||||
}
|
||||
|
||||
dropIndexRef(dropIndex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)draggingExited:(id)draggingInfo
|
||||
{
|
||||
[self _updateDragAndDropStateWithDraggingInfo:draggingInfo newDragOperation:0 newDropIndex:-1 newDropOperation:1];
|
||||
}
|
||||
|
||||
- (void)draggingEnded:(id)draggingInfo
|
||||
{
|
||||
[self _updateDragAndDropStateWithDraggingInfo:draggingInfo newDragOperation:0 newDropIndex:-1 newDropOperation:1];
|
||||
}
|
||||
|
||||
/*
|
||||
Not supported. Use -collectionView:dataForItemsAtIndexes:fortype:
|
||||
- (BOOL)_writeItemsAtIndexes:(CPIndexSet)indexes toPasteboard:(CPPasteboard)pboard
|
||||
{
|
||||
if ([self respondsToSelector:@selector(collectionView:writeItemsAtIndexes:toPasteboard:)])
|
||||
return [_delegate collectionView:self writeItemsAtIndexes:indexes toPasteboard:pboard];
|
||||
|
||||
return NO;
|
||||
}
|
||||
*/
|
||||
|
||||
- (BOOL)performDragOperation:(id)draggingInfo
|
||||
{
|
||||
var result = NO;
|
||||
|
||||
if (_currentDragOperation && _currentDropIndex !== -1)
|
||||
result = [_delegate collectionView:self acceptDrop:draggingInfo index:_currentDropIndex dropOperation:1];
|
||||
|
||||
[self draggingEnded:draggingInfo]; // Is this correct ?
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)_updateDragAndDropStateWithDraggingInfo:(id)draggingInfo newDragOperation:(CPDragOperation)dragOperation newDropIndex:(CPInteger)dropIndex newDropOperation:(CPInteger)dropOperation
|
||||
{
|
||||
_currentDropIndex = dropIndex;
|
||||
_currentDragOperation = dragOperation;
|
||||
|
||||
var frameOrigin,
|
||||
dropviewFrameWidth = _CGRectGetWidth([_dropView frame]);
|
||||
|
||||
if (_currentDropIndex == -1 || _currentDragOperation == CPDragOperationNone)
|
||||
frameOrigin = _CGPointMake(-dropviewFrameWidth, 0);
|
||||
else if (_currentDropIndex == 0)
|
||||
frameOrigin = _CGPointMake(0, 0);
|
||||
else
|
||||
{
|
||||
var offset;
|
||||
|
||||
if ((_currentDropIndex % _numberOfColumns) !== 0 || _currentDropIndex == [_items count])
|
||||
{
|
||||
dropIndex = _currentDropIndex - 1;
|
||||
offset = (_horizontalMargin - dropviewFrameWidth) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = - _itemSize.width - dropviewFrameWidth - (_horizontalMargin - dropviewFrameWidth) / 2;
|
||||
}
|
||||
|
||||
var rect = [self frameForItemAtIndex:dropIndex];
|
||||
|
||||
frameOrigin = _CGPointMake(_CGRectGetMaxX(rect) + offset, rect.origin.y - _verticalMargin);
|
||||
}
|
||||
|
||||
[_dropView setFrameOrigin:frameOrigin];
|
||||
}
|
||||
|
||||
- (BOOL)_dropIndexDidChange:(id)draggingInfo
|
||||
{
|
||||
var dropIndex = [self _dropIndexForDraggingInfo:draggingInfo proposedDropOperation:1];
|
||||
|
||||
if (dropIndex == CPNotFound)
|
||||
dropIndex = [[self content] count];
|
||||
|
||||
return (_currentDropIndex !== dropIndex)
|
||||
}
|
||||
|
||||
- (CPInteger)_dropIndexForDraggingInfo:(id)draggingInfo proposedDropOperation:(int)dropOperation
|
||||
{
|
||||
var location = [self convertPoint:[draggingInfo draggingLocation] fromView:nil],
|
||||
locationX = location.x + _itemSize.width / 2;
|
||||
|
||||
var column = MIN(FLOOR(locationX / (_itemSize.width + _horizontalMargin)), _numberOfColumns),
|
||||
row = FLOOR(location.y / (_itemSize.height + _verticalMargin));
|
||||
|
||||
if (row >= _numberOfRows - 1)
|
||||
{
|
||||
if (row >= _numberOfRows)
|
||||
{
|
||||
row = _numberOfRows - 1;
|
||||
column = _numberOfColumns;
|
||||
}
|
||||
|
||||
return MIN((row * _numberOfColumns + column), [_items count]);
|
||||
}
|
||||
|
||||
return (row * _numberOfColumns + column);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCollectionViewDropIndicator : CPView
|
||||
{
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
width = _CGRectGetWidth(aRect),
|
||||
circleRect = _CGRectMake(1, 1, width - 2, width - 2);
|
||||
|
||||
CGContextSetStrokeColor(context, [CPColor colorWithHexString:@"4886ca"]);
|
||||
CGContextSetFillColor(context, [CPColor whiteColor]);
|
||||
CGContextSetLineWidth(context, 3);
|
||||
|
||||
//draw white under the circle thing
|
||||
CGContextFillRect(context, circleRect);
|
||||
//draw the circle thing
|
||||
CGContextStrokeEllipseInRect(context, circleRect);
|
||||
//then draw the line
|
||||
CGContextBeginPath(context);
|
||||
CGContextMoveToPoint(context, FLOOR(width / 2), _CGRectGetMinY(aRect) + width);
|
||||
CGContextAddLineToPoint(context, FLOOR(width / 2), _CGRectGetHeight(aRect));
|
||||
CGContextClosePath(context);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPCollectionView (KeyboardInteraction)
|
||||
|
||||
- (void)_modifySelectionWithNewIndex:(int)anIndex direction:(int)aDirection expand:(BOOL)shouldExpand
|
||||
@@ -858,9 +1220,9 @@
|
||||
|
||||
// if the direction is backward (-1) check with the bottom anchor
|
||||
if (aDirection === -1)
|
||||
[indexes addIndexesInRange:CPMakeRange(anIndex, bottomAnchor - anIndex + 1)];
|
||||
[indexes addIndexesInRange:_CPMakeRange(anIndex, bottomAnchor - anIndex + 1)];
|
||||
else
|
||||
[indexes addIndexesInRange:CPMakeRange(topAnchor, anIndex - topAnchor + 1)];
|
||||
[indexes addIndexesInRange:_CPMakeRange(topAnchor, anIndex - topAnchor + 1)];
|
||||
}
|
||||
else
|
||||
indexes = [CPIndexSet indexSetWithIndex:anIndex];
|
||||
@@ -873,7 +1235,7 @@
|
||||
{
|
||||
var frame = [self frameForItemsAtIndexes:[self selectionIndexes]];
|
||||
|
||||
if (!CGRectIsNull(frame))
|
||||
if (!_CGRectIsNull(frame))
|
||||
[self scrollRectToVisible:frame];
|
||||
}
|
||||
|
||||
@@ -953,6 +1315,11 @@
|
||||
[self interpretKeyEvents:[anEvent]];
|
||||
}
|
||||
|
||||
- (void)setAutoresizingMask:(int)aMask
|
||||
{
|
||||
[super setAutoresizingMask:0];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPCollectionView (Deprecated)
|
||||
@@ -987,35 +1354,12 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeK
|
||||
|
||||
@implementation CPCollectionView (CPCoding)
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
[super awakeFromCib];
|
||||
|
||||
var prototypeView = [_itemPrototype view];
|
||||
if (prototypeView && (CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()) || CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero())))
|
||||
{
|
||||
var item = _itemPrototype;
|
||||
|
||||
if (CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
|
||||
_minItemSize = [prototypeView frameSize];
|
||||
else if (CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
|
||||
_maxItemSize = [prototypeView frameSize];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_items = [];
|
||||
_content = [];
|
||||
|
||||
_cachedItems = [];
|
||||
|
||||
_itemSize = CGSizeMakeZero();
|
||||
|
||||
_minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey];
|
||||
_maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey];
|
||||
|
||||
@@ -1029,11 +1373,7 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeK
|
||||
|
||||
[self setBackgroundColors:[aCoder decodeObjectForKey:CPCollectionViewBackgroundColorsKey]];
|
||||
|
||||
_tileWidth = -1.0;
|
||||
|
||||
_selectionIndexes = [CPIndexSet indexSet];
|
||||
|
||||
_allowsEmptySelection = YES;
|
||||
[self _init];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -1043,10 +1383,10 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeK
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
if (!CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
|
||||
if (!_CGSizeEqualToSize(_minItemSize, _CGSizeMakeZero()))
|
||||
[aCoder encodeSize:_minItemSize forKey:CPCollectionViewMinItemSizeKey];
|
||||
|
||||
if (!CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
|
||||
if (!_CGSizeEqualToSize(_maxItemSize, _CGSizeMakeZero()))
|
||||
[aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
|
||||
|
||||
[aCoder encodeInt:_maxNumberOfRows forKey:CPCollectionViewMaxNumberOfRowsKey];
|
||||
|
||||
@@ -22,13 +22,38 @@
|
||||
|
||||
@import "CPViewController.j"
|
||||
|
||||
|
||||
/*!
|
||||
Represents an object inside a CPCollectionView.
|
||||
*/
|
||||
@implementation CPCollectionViewItem : CPViewController
|
||||
{
|
||||
BOOL _isSelected;
|
||||
CPData _cachedArchive;
|
||||
}
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
var cibName = [self cibName],
|
||||
copy;
|
||||
|
||||
if (cibName)
|
||||
{
|
||||
copy = [[[self class] alloc] initWithCibName:cibName bundle:[self cibBundle]];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_cachedArchive)
|
||||
_cachedArchive = [CPKeyedArchiver archivedDataWithRootObject:self];
|
||||
|
||||
copy = [CPKeyedUnarchiver unarchiveObjectWithData:_cachedArchive];
|
||||
|
||||
// copy connections
|
||||
}
|
||||
|
||||
[copy setRepresentedObject:[self representedObject]];
|
||||
[copy setSelected:_isSelected];
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Setting the Represented Object
|
||||
|
||||
@@ -1302,7 +1302,7 @@ var cloneObject = function(object)
|
||||
if (object.hasOwnProperty(key))
|
||||
if (object[key].constructor === Array)
|
||||
clone[key] = object[key].slice(0);
|
||||
else if (typeof(object) === "object")
|
||||
else if (typeof(object[key]) === "object")
|
||||
clone[key] = cloneObject(object[key]);
|
||||
else
|
||||
clone[key] = object[key];
|
||||
|
||||
@@ -45,12 +45,12 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
CPColor _color;
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (theBinding == CPValueBinding)
|
||||
if (aBinding == CPValueBinding)
|
||||
return [CPColorWellValueBinder class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
@@ -315,7 +315,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
|
||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
||||
{
|
||||
var placeholderColor = [CPColor blueColor];
|
||||
var placeholderColor = [CPColor blackColor];
|
||||
|
||||
[self _setPlaceholder:placeholderColor forMarker:CPMultipleValuesMarker isDefault:YES];
|
||||
[self _setPlaceholder:placeholderColor forMarker:CPNoSelectionMarker isDefault:YES];
|
||||
@@ -323,30 +323,19 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
[self _setPlaceholder:placeholderColor forMarker:CPNullMarker isDefault:YES];
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)theBinding
|
||||
- (id)valueForBinding:(CPString)aBinding
|
||||
{
|
||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
||||
options = [_info objectForKey:CPOptionsKey],
|
||||
newValue = [destination valueForKeyPath:keyPath],
|
||||
isPlaceholder = CPIsControllerMarker(newValue);
|
||||
return [_source color];
|
||||
}
|
||||
|
||||
if (isPlaceholder)
|
||||
{
|
||||
if (newValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
||||
{
|
||||
[CPException raise:CPGenericException
|
||||
reason:@"can't transform non applicable key on: " + _source + " value: " + newValue];
|
||||
}
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)theBinding
|
||||
{
|
||||
[_source setColor:aValue];
|
||||
}
|
||||
|
||||
newValue = [self _placeholderForMarker:newValue];
|
||||
}
|
||||
else
|
||||
{
|
||||
newValue = [self transformValue:newValue withOptions:options];
|
||||
}
|
||||
|
||||
[_source setColor:newValue];
|
||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
||||
{
|
||||
[_source setColor:aValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -63,12 +63,12 @@ var CPComboBoxTextSubview = @"text",
|
||||
return [CPDictionary dictionaryWithObjectsAndKeys:_CGSizeMake(21.0, 29.0), @"popup-button-size", _CGInsetMake(3.0, 3.0, 3.0, 3.0), @"border-inset"];
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (theBinding === CPContentBinding || theBinding === CPContentValuesBinding)
|
||||
if (aBinding === CPContentBinding || aBinding === CPContentValuesBinding)
|
||||
return [_CPComboBoxContentBinder class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -1111,7 +1111,7 @@ var CPComboBoxCompletionTest = function(object, index, context)
|
||||
*/
|
||||
@implementation _CPComboBoxContentBinder : CPBinder
|
||||
|
||||
- (void)setValueFor:(CPString)theBinding
|
||||
- (void)setValueFor:(CPString)aBinding
|
||||
{
|
||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
||||
@@ -1153,13 +1153,15 @@ var CPComboBoxCompletionTest = function(object, index, context)
|
||||
else
|
||||
newValue = [self transformValue:newValue withOptions:options];
|
||||
|
||||
switch (theBinding)
|
||||
switch (aBinding)
|
||||
{
|
||||
case CPContentBinding: [_source setContent:newValue];
|
||||
break;
|
||||
case CPContentBinding:
|
||||
[_source setContent:newValue];
|
||||
break;
|
||||
|
||||
case CPContentValuesBinding: [_source setContentValues:newValue];
|
||||
break;
|
||||
case CPContentValuesBinding:
|
||||
[_source setContentValues:newValue];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,58 +38,58 @@ CPWindowsOperatingSystem = 1;
|
||||
CPOtherOperatingSystem = 2;
|
||||
|
||||
// Features
|
||||
CPCSSRGBAFeature = 1 << 5;
|
||||
CPCSSRGBAFeature = 5;
|
||||
|
||||
CPHTMLCanvasFeature = 1 << 6;
|
||||
CPHTMLContentEditableFeature = 1 << 7;
|
||||
CPHTMLDragAndDropFeature = 1 << 8;
|
||||
CPHTMLCanvasFeature = 6;
|
||||
CPHTMLContentEditableFeature = 7;
|
||||
CPHTMLDragAndDropFeature = 8;
|
||||
|
||||
CPJavaScriptInnerTextFeature = 1 << 9;
|
||||
CPJavaScriptTextContentFeature = 1 << 10;
|
||||
CPJavaScriptClipboardEventsFeature = 1 << 11;
|
||||
CPJavaScriptClipboardAccessFeature = 1 << 12;
|
||||
CPJavaScriptCanvasDrawFeature = 1 << 13;
|
||||
CPJavaScriptCanvasTransformFeature = 1 << 14;
|
||||
CPJavaScriptInnerTextFeature = 9;
|
||||
CPJavaScriptTextContentFeature = 10;
|
||||
CPJavaScriptClipboardEventsFeature = 11;
|
||||
CPJavaScriptClipboardAccessFeature = 12;
|
||||
CPJavaScriptCanvasDrawFeature = 13;
|
||||
CPJavaScriptCanvasTransformFeature = 14;
|
||||
|
||||
CPVMLFeature = 1 << 15;
|
||||
CPVMLFeature = 15;
|
||||
|
||||
CPJavaScriptRemedialKeySupport = 1 << 16;
|
||||
CPJavaScriptShadowFeature = 1 << 20;
|
||||
CPJavaScriptRemedialKeySupport = 16;
|
||||
CPJavaScriptShadowFeature = 20;
|
||||
|
||||
CPJavaScriptNegativeMouseWheelValues = 1 << 22;
|
||||
CPJavaScriptMouseWheelValues_8_15 = 1 << 23;
|
||||
CPJavaScriptNegativeMouseWheelValues = 22;
|
||||
CPJavaScriptMouseWheelValues_8_15 = 23;
|
||||
|
||||
CPOpacityRequiresFilterFeature = 1 << 24;
|
||||
CPOpacityRequiresFilterFeature = 24;
|
||||
|
||||
//Internet explorer does not allow dynamically changing the type of an input element
|
||||
CPInputTypeCanBeChangedFeature = 1 << 25;
|
||||
CPHTML5DragAndDropSourceYOffBy1 = 1 << 26;
|
||||
// Internet explorer does not allow dynamically changing the type of an input element
|
||||
CPInputTypeCanBeChangedFeature = 25;
|
||||
CPHTML5DragAndDropSourceYOffBy1 = 26;
|
||||
|
||||
CPSOPDisabledFromFileURLs = 1 << 27;
|
||||
CPSOPDisabledFromFileURLs = 27;
|
||||
|
||||
// element.style.font can be set for an element not in the DOM.
|
||||
CPInputSetFontOutsideOfDOM = 1 << 28;
|
||||
CPInputSetFontOutsideOfDOM = 28;
|
||||
|
||||
// Input elements have 1 px of extra padding on the left regardless of padding setting.
|
||||
CPInput1PxLeftPadding = 1 << 29;
|
||||
CPInputOnInputEventFeature = 1 << 30;
|
||||
CPInput1PxLeftPadding = 29;
|
||||
CPInputOnInputEventFeature = 30;
|
||||
|
||||
// When an absolutely positioned div (CPView) with an absolutely positioned canvas in it (CPView with drawRect:) moves things on top of the canvas
|
||||
// (subviews) don't redraw correctly. E.g. if you have a bunch of text fields in a CPBox in a sheet which animates in, some of the text fields might
|
||||
// not be visible because the CPBox has a canvas at the bottom and the box moved form offscreen to onscreen.
|
||||
// This bug is probably very related: https://bugs.webkit.org/show_bug.cgi?id=67203
|
||||
CPFileAPIFeature = 31;
|
||||
|
||||
/*
|
||||
When an absolutely positioned div (CPView) with an absolutely positioned canvas in it (CPView with drawRect:) moves things on top of the canvas (subviews) don't redraw correctly. E.g. if you have a bunch of text fields in a CPBox in a sheet which animates in, some of the text fields might not be visible because the CPBox has a canvas at the bottom and the box moved form offscreen to onscreen. This bug is probably very related: https://bugs.webkit.org/show_bug.cgi?id=67203
|
||||
*/
|
||||
CPCanvasParentDrawErrorsOnMovementBug = 1 << 0;
|
||||
|
||||
var USER_AGENT = "",
|
||||
PLATFORM_ENGINE = CPUnknownBrowserEngine,
|
||||
PLATFORM_FEATURES = 0,
|
||||
PLATFORM_FEATURES = [],
|
||||
PLATFORM_BUGS = 0,
|
||||
PLATFORM_STYLE_JS_PROPERTIES = {};
|
||||
|
||||
// default these features to true
|
||||
|
||||
PLATFORM_FEATURES |= CPInputTypeCanBeChangedFeature;
|
||||
PLATFORM_FEATURES |= CPInputSetFontOutsideOfDOM;
|
||||
PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = YES;
|
||||
PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = YES;
|
||||
|
||||
if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
|
||||
USER_AGENT = window.navigator.userAgent;
|
||||
@@ -99,7 +99,7 @@ if (typeof window !== "undefined" && window.opera)
|
||||
{
|
||||
PLATFORM_ENGINE = CPOperaBrowserEngine;
|
||||
|
||||
PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
|
||||
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
|
||||
}
|
||||
|
||||
// Internet Explorer
|
||||
@@ -108,16 +108,16 @@ else if (typeof window !== "undefined" && window.attachEvent) // Must follow Ope
|
||||
PLATFORM_ENGINE = CPInternetExplorerBrowserEngine;
|
||||
|
||||
// Features we can only be sure of with IE (no known independent tests)
|
||||
PLATFORM_FEATURES |= CPVMLFeature;
|
||||
PLATFORM_FEATURES |= CPJavaScriptRemedialKeySupport;
|
||||
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||
PLATFORM_FEATURES[CPVMLFeature] = YES;
|
||||
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
|
||||
PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
|
||||
|
||||
PLATFORM_FEATURES |= CPOpacityRequiresFilterFeature;
|
||||
PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = YES;
|
||||
|
||||
PLATFORM_FEATURES &= ~CPInputTypeCanBeChangedFeature;
|
||||
PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = NO;
|
||||
|
||||
// Tested in Internet Explore 8 and 9.
|
||||
PLATFORM_FEATURES &= ~CPInputSetFontOutsideOfDOM;
|
||||
PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = NO;
|
||||
}
|
||||
|
||||
// WebKit
|
||||
@@ -126,15 +126,15 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
PLATFORM_ENGINE = CPWebKitBrowserEngine;
|
||||
|
||||
// Features we can only be sure of with WebKit (no known independent tests)
|
||||
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||
PLATFORM_FEATURES |= CPHTMLContentEditableFeature;
|
||||
PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
|
||||
PLATFORM_FEATURES[CPHTMLContentEditableFeature] = YES;
|
||||
|
||||
if (USER_AGENT.indexOf("Chrome") === -1)
|
||||
PLATFORM_FEATURES |= CPHTMLDragAndDropFeature;
|
||||
PLATFORM_FEATURES[CPHTMLDragAndDropFeature] = YES;
|
||||
|
||||
PLATFORM_FEATURES |= CPJavaScriptClipboardEventsFeature;
|
||||
PLATFORM_FEATURES |= CPJavaScriptClipboardAccessFeature;
|
||||
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||
PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES;
|
||||
PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = YES;
|
||||
PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
|
||||
|
||||
var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
|
||||
versionEnd = USER_AGENT.indexOf(" ", versionStart),
|
||||
@@ -144,22 +144,22 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
minorVersion = parseInt(versionString.substr(versionDivision + 1));
|
||||
|
||||
if ((USER_AGENT.indexOf("Safari") !== CPNotFound && (majorVersion > 525 || (majorVersion === 525 && minorVersion > 14))) || USER_AGENT.indexOf("Chrome") !== CPNotFound)
|
||||
PLATFORM_FEATURES |= CPJavaScriptRemedialKeySupport;
|
||||
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
|
||||
|
||||
// FIXME this is a terrible hack to get around this bug:
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=21548
|
||||
if (![CPPlatform isBrowser])
|
||||
PLATFORM_FEATURES |= CPJavaScriptRemedialKeySupport;
|
||||
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
|
||||
|
||||
if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
|
||||
PLATFORM_FEATURES |= CPHTML5DragAndDropSourceYOffBy1;
|
||||
PLATFORM_FEATURES[CPHTML5DragAndDropSourceYOffBy1] = YES;
|
||||
|
||||
// This is supposedly fixed in webkit r123603. Seems to work in Chrome 21 but not Safari 6.0.
|
||||
if (majorVersion < 537)
|
||||
PLATFORM_FEATURES |= CPInput1PxLeftPadding;
|
||||
PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
|
||||
|
||||
if (USER_AGENT.indexOf("Chrome") === CPNotFound)
|
||||
PLATFORM_FEATURES |= CPSOPDisabledFromFileURLs;
|
||||
PLATFORM_FEATURES[CPSOPDisabledFromFileURLs] = YES;
|
||||
|
||||
// Assume this bug was introduced around Safari 5.1/Chrome 16. This could probably be tighter.
|
||||
if (majorVersion > 533)
|
||||
@@ -177,59 +177,71 @@ else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
|
||||
{
|
||||
PLATFORM_ENGINE = CPGeckoBrowserEngine;
|
||||
|
||||
PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
|
||||
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
|
||||
|
||||
var index = USER_AGENT.indexOf("Firefox"),
|
||||
version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1));
|
||||
|
||||
if (version >= 3.0)
|
||||
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||
PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
|
||||
|
||||
if (version < 3.0)
|
||||
PLATFORM_FEATURES |= CPJavaScriptMouseWheelValues_8_15;
|
||||
PLATFORM_FEATURES[CPJavaScriptMouseWheelValues_8_15] = YES;
|
||||
|
||||
// Some day this might be fixed and should be version prefixed. No known fixed version yet.
|
||||
PLATFORM_FEATURES |= CPInput1PxLeftPadding;
|
||||
PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
|
||||
}
|
||||
|
||||
// Feature Specific Checks
|
||||
// Feature-specific checks
|
||||
if (typeof document != "undefined")
|
||||
{
|
||||
var canvasElement = document.createElement("canvas");
|
||||
// Detect Canvas Support
|
||||
|
||||
// Detect canvas support
|
||||
if (canvasElement && canvasElement.getContext)
|
||||
{
|
||||
PLATFORM_FEATURES |= CPHTMLCanvasFeature;
|
||||
PLATFORM_FEATURES[CPHTMLCanvasFeature] = YES;
|
||||
|
||||
// Detect Canvas setTransform/transform support
|
||||
// Detect canvas setTransform/transform support
|
||||
var context = document.createElement("canvas").getContext("2d");
|
||||
|
||||
if (context && context.setTransform && context.transform)
|
||||
PLATFORM_FEATURES |= CPJavaScriptCanvasTransformFeature;
|
||||
PLATFORM_FEATURES[CPJavaScriptCanvasTransformFeature] = YES;
|
||||
}
|
||||
|
||||
var DOMElement = document.createElement("div");
|
||||
|
||||
// Detect whether we have innerText or textContent (or neither)
|
||||
if (DOMElement.innerText != undefined)
|
||||
PLATFORM_FEATURES |= CPJavaScriptInnerTextFeature;
|
||||
PLATFORM_FEATURES[CPJavaScriptInnerTextFeature] = YES;
|
||||
else if (DOMElement.textContent != undefined)
|
||||
PLATFORM_FEATURES |= CPJavaScriptTextContentFeature;
|
||||
PLATFORM_FEATURES[CPJavaScriptTextContentFeature] = YES;
|
||||
|
||||
var DOMInputElement = document.createElement("input");
|
||||
|
||||
if ("oninput" in DOMInputElement)
|
||||
PLATFORM_FEATURES |= CPInputOnInputEventFeature;
|
||||
PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
|
||||
else if (typeof DOMInputElement.setAttribute === "function")
|
||||
{
|
||||
DOMInputElement.setAttribute("oninput", "return;");
|
||||
|
||||
if (typeof DOMInputElement.oninput === "function")
|
||||
PLATFORM_FEATURES |= CPInputOnInputEventFeature;
|
||||
PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
|
||||
}
|
||||
|
||||
// Detect FileAPI support
|
||||
if (typeof DOMInputElement.setAttribute === "function")
|
||||
{
|
||||
DOMInputElement.setAttribute("type", "file");
|
||||
PLATFORM_FEATURES[CPFileAPIFeature] = !!DOMInputElement["files"];
|
||||
}
|
||||
else
|
||||
PLATFORM_FEATURES[CPFileAPIFeature] = NO;
|
||||
}
|
||||
|
||||
function CPFeatureIsCompatible(aFeature)
|
||||
{
|
||||
return PLATFORM_FEATURES & aFeature;
|
||||
return !!PLATFORM_FEATURES[aFeature];
|
||||
}
|
||||
|
||||
function CPPlatformHasBug(aBug)
|
||||
|
||||
@@ -152,12 +152,14 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
[self exposeBinding:@"enabled"];
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (theBinding === CPValueBinding)
|
||||
if (aBinding === CPValueBinding)
|
||||
return [_CPValueBinder class];
|
||||
else if ([aBinding hasPrefix:CPEnabledBinding])
|
||||
return [CPMultipleValueAndBinding class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -239,6 +241,9 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
{
|
||||
[self _reverseSetBinding];
|
||||
|
||||
var binding = [CPBinder getBinding:CPTargetBinding forObject:self];
|
||||
[binding invokeAction];
|
||||
|
||||
return [CPApp sendAction:anAction to:anObject from:self];
|
||||
}
|
||||
|
||||
|
||||
@@ -162,8 +162,10 @@ var currentCursor = nil,
|
||||
|
||||
if (doesHaveImage)
|
||||
{
|
||||
var themeResourcePath = [[[CPApp themeBlend] bundle] resourcePath];
|
||||
cssString = [CPString stringWithFormat:@"url(%@cursors/%@.cur), %@", themeResourcePath, cursorName, aString];
|
||||
var themeResourcePath = [[[CPApp themeBlend] bundle] resourcePath],
|
||||
extension = CPBrowserIsEngine(CPInternetExplorerBrowserEngine) ? @"cur" : @"png";
|
||||
|
||||
cssString = [CPString stringWithFormat:@"url(%@cursors/%@.%@), %@", themeResourcePath, cursorName, extension, aString];
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
@import "CGColor.j"
|
||||
@import "CGColorSpace.j"
|
||||
@import "CGGradient.j"
|
||||
@import "CPBezierPath.j"
|
||||
@import "CPGraphicsContext.j"
|
||||
|
||||
CPGradientDrawsBeforeStartingLocation = kCGGradientDrawsBeforeStartLocation;
|
||||
@@ -40,6 +41,11 @@ CPGradientDrawsAfterEndingLocation = kCGGradientDrawsAfterEndLocation;
|
||||
CGGradient _gradient;
|
||||
}
|
||||
|
||||
- (id)initWithStartingColor:(CPColor)startingColor endingColor:(CPColor)endingColor
|
||||
{
|
||||
return [self initWithColors:[startingColor, endingColor]];
|
||||
}
|
||||
|
||||
- (id)initWithColors:(CPArray)someColors
|
||||
{
|
||||
var count = [someColors count];
|
||||
@@ -83,7 +89,19 @@ CPGradientDrawsAfterEndingLocation = kCGGradientDrawsAfterEndLocation;
|
||||
CGContextClipToRect(ctx, rect);
|
||||
CGContextAddRect(ctx, rect);
|
||||
|
||||
var startPoint,
|
||||
[self _drawInRect:rect atAngle:angle];
|
||||
|
||||
CGContextRestoreGState(ctx);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore like draw in rect but apply no clipping.
|
||||
*/
|
||||
- (void)_drawInRect:(CGRect)rect atAngle:(float)angle
|
||||
{
|
||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
|
||||
startPoint,
|
||||
endPoint;
|
||||
|
||||
// Modulo of negative values doesn't work as expected in JS.
|
||||
@@ -117,7 +135,25 @@ CPGradientDrawsAfterEndingLocation = kCGGradientDrawsAfterEndLocation;
|
||||
startPoint.y + length * SIN(radians));
|
||||
|
||||
[self drawFromPoint:startPoint toPoint:endPoint options:CPGradientDrawsBeforeStartingLocation | CPGradientDrawsAfterEndingLocation];
|
||||
CGContextRestoreGState(ctx);
|
||||
}
|
||||
|
||||
- (void)drawInBezierPath:(CPBezierPath)aPath angle:(float)anAngle
|
||||
{
|
||||
[CPGraphicsContext saveGraphicsState];
|
||||
|
||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
// Nail down the path which CGContextDrawLinearGradient will cause to be filled.
|
||||
// Note we don't do this by clipping to the path and then calling drawInRect:atAngle:
|
||||
// as this would cut off any antialias of the drawing, plus any active context shadow.
|
||||
CGContextBeginPath(ctx);
|
||||
CGContextAddPath(ctx, aPath._path);
|
||||
CGContextSetLineWidth(ctx, [aPath lineWidth]);
|
||||
CGContextClosePath(ctx);
|
||||
|
||||
[self _drawInRect:[aPath bounds] atAngle:anAngle];
|
||||
|
||||
[CPGraphicsContext restoreGraphicsState];
|
||||
}
|
||||
|
||||
- (void)drawFromPoint:(NSPoint)startingPoint toPoint:(NSPoint)endingPoint options:(NSGradientDrawingOptions)options
|
||||
|
||||
@@ -70,12 +70,14 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
||||
CPImageViewEmptyPlaceholderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"empty.png"]];
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (theBinding === CPValueBinding || theBinding === CPValueURLBinding || theBinding === CPValuePathBinding || theBinding === CPDataBinding)
|
||||
if (aBinding === CPValueBinding || aBinding === CPValueURLBinding || aBinding === CPValuePathBinding || aBinding === CPDataBinding)
|
||||
return [CPImageViewValueBinder class];
|
||||
else if ([aBinding hasPrefix:CPEditableBinding])
|
||||
return [CPMultipleValueAndBinding class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPInvocation.j>
|
||||
@import <Foundation/CPValueTransformer.j>
|
||||
@import <Foundation/CPKeyValueObserving.j>
|
||||
|
||||
@class CPButton
|
||||
|
||||
var exposedBindingsMap = [CPDictionary new],
|
||||
bindingsMap = [CPDictionary new];
|
||||
@@ -39,7 +41,7 @@ var CPBindingOperationAnd = 0,
|
||||
@implementation CPBinder : CPObject
|
||||
{
|
||||
CPDictionary _info;
|
||||
id _source;
|
||||
id _source @accessors(getter=source);
|
||||
|
||||
JSObject _suppressedNotifications;
|
||||
JSObject _placeholderForMarker;
|
||||
@@ -95,9 +97,9 @@ var CPBindingOperationAnd = 0,
|
||||
if (!theBinding)
|
||||
return;
|
||||
|
||||
var infoDictionary = theBinding._info,
|
||||
observedObject = [infoDictionary objectForKey:CPObservedObjectKey],
|
||||
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey];
|
||||
var info = theBinding._info,
|
||||
observedObject = [info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [info objectForKey:CPObservedKeyPathKey];
|
||||
|
||||
[observedObject removeObserver:theBinding forKeyPath:keyPath];
|
||||
[bindings removeObjectForKey:aBinding];
|
||||
@@ -106,6 +108,7 @@ var CPBindingOperationAnd = 0,
|
||||
+ (void)unbindAllForObject:(id)anObject
|
||||
{
|
||||
var bindings = [bindingsMap objectForKey:[anObject UID]];
|
||||
|
||||
if (!bindings)
|
||||
return;
|
||||
|
||||
@@ -120,12 +123,14 @@ var CPBindingOperationAnd = 0,
|
||||
|
||||
- (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
|
||||
{
|
||||
self = [super init];
|
||||
// We use [self init] here because subclasses override init. We can't override this method
|
||||
// because their initialization has to occur before this method in this class is executed.
|
||||
self = [self init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_source = aSource;
|
||||
_info = [CPDictionary dictionaryWithObjects:[aDestination, aKeyPath] forKeys:[CPObservedObjectKey, CPObservedKeyPathKey]];
|
||||
_info = [CPDictionary dictionaryWithObjects:[aDestination, aKeyPath] forKeys:[CPObservedObjectKey, CPObservedKeyPathKey]];
|
||||
_suppressedNotifications = {};
|
||||
_placeholderForMarker = {};
|
||||
|
||||
@@ -137,6 +142,7 @@ var CPBindingOperationAnd = 0,
|
||||
[aDestination addObserver:self forKeyPath:aKeyPath options:CPKeyValueObservingOptionNew context:aBinding];
|
||||
|
||||
var bindings = [bindingsMap objectForKey:[_source UID]];
|
||||
|
||||
if (!bindings)
|
||||
{
|
||||
bindings = [CPDictionary new];
|
||||
@@ -150,21 +156,25 @@ var CPBindingOperationAnd = 0,
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)raiseIfNotApplicable:(id)aValue forKeyPath:(CPString)keyPath options:(CPDictionary)options
|
||||
{
|
||||
if (aValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
||||
{
|
||||
[CPException raise:CPGenericException
|
||||
reason:@"Cannot transform non-applicable key on: " + _source + " key path: " + keyPath + " value: " + aValue];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)theBinding
|
||||
{
|
||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
||||
options = [_info objectForKey:CPOptionsKey],
|
||||
newValue = [destination valueForKeyPath:keyPath],
|
||||
isPlaceholder = CPIsControllerMarker(newValue);
|
||||
newValue = [destination valueForKeyPath:keyPath];
|
||||
|
||||
if (isPlaceholder)
|
||||
if (CPIsControllerMarker(newValue))
|
||||
{
|
||||
if (newValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
||||
{
|
||||
[CPException raise:CPGenericException
|
||||
reason:@"Cannot transform non-applicable key on: " + _source + " key path: " + keyPath + " value: " + newValue];
|
||||
}
|
||||
[self raiseIfNotApplicable:newValue forKeyPath:keyPath options:options];
|
||||
|
||||
var value = [self _placeholderForMarker:newValue];
|
||||
[self setPlaceholderValue:value withMarker:newValue forBinding:theBinding];
|
||||
@@ -211,6 +221,7 @@ var CPBindingOperationAnd = 0,
|
||||
return;
|
||||
|
||||
var objectSuppressions = _suppressedNotifications[[anObject UID]];
|
||||
|
||||
if (objectSuppressions && objectSuppressions[aKeyPath])
|
||||
return;
|
||||
|
||||
@@ -229,6 +240,7 @@ var CPBindingOperationAnd = 0,
|
||||
if (!valueTransformer)
|
||||
{
|
||||
var valueTransformerClass = CPClassFromString(valueTransformerName);
|
||||
|
||||
if (valueTransformerClass)
|
||||
{
|
||||
valueTransformer = [[valueTransformerClass alloc] init];
|
||||
@@ -286,6 +298,7 @@ var CPBindingOperationAnd = 0,
|
||||
|
||||
var uid = [anObject UID],
|
||||
objectSuppressions = _suppressedNotifications[uid];
|
||||
|
||||
if (!objectSuppressions)
|
||||
_suppressedNotifications[uid] = objectSuppressions = {};
|
||||
|
||||
@@ -302,6 +315,7 @@ var CPBindingOperationAnd = 0,
|
||||
|
||||
var uid = [anObject UID],
|
||||
objectSuppressions = _suppressedNotifications[uid];
|
||||
|
||||
if (!objectSuppressions)
|
||||
return;
|
||||
|
||||
@@ -318,6 +332,7 @@ var CPBindingOperationAnd = 0,
|
||||
optionName = CPBinderPlaceholderOptions[count],
|
||||
isExplicit = [options containsKey:optionName],
|
||||
placeholder = isExplicit ? [options objectForKey:optionName] : nil;
|
||||
|
||||
[self _setPlaceholder:placeholder forMarker:marker isDefault:!isExplicit];
|
||||
}
|
||||
}
|
||||
@@ -330,8 +345,10 @@ var CPBindingOperationAnd = 0,
|
||||
- (void)_placeholderForMarker:aMarker
|
||||
{
|
||||
var placeholder = _placeholderForMarker[aMarker];
|
||||
|
||||
if (placeholder)
|
||||
return placeholder['value'];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -358,7 +375,7 @@ var CPBindingOperationAnd = 0,
|
||||
[CPBinder exposeBinding:aBinding forClass:[self class]];
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
return [CPBinder class];
|
||||
}
|
||||
@@ -411,7 +428,7 @@ var CPBindingOperationAnd = 0,
|
||||
[binderClass unbind:aBinding forObject:self];
|
||||
}
|
||||
|
||||
- (id)_replacementKeyPathForBinding:(CPString)binding
|
||||
- (CPString)_replacementKeyPathForBinding:(CPString)binding
|
||||
{
|
||||
return binding;
|
||||
}
|
||||
@@ -420,13 +437,13 @@ var CPBindingOperationAnd = 0,
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
Provides stub implementations that simply call super for the "objectValue" binding
|
||||
This class should not be necessary but assures backwards compliance with our old way of doing bindings
|
||||
Every class with a value binding should implement a subclass to handle it's specific value binding logic
|
||||
Provides stub implementations that simply calls super for the "objectValue" binding.
|
||||
This class should not be necessary but assures backwards compliance with our old way of doing bindings.
|
||||
|
||||
IMPORTANT!
|
||||
Every class with a value binding should implement a subclass to handle its specific value binding logic.
|
||||
*/
|
||||
@implementation _CPValueBinder : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)theBinding
|
||||
{
|
||||
@@ -440,8 +457,9 @@ var CPBindingOperationAnd = 0,
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPKeyValueOrBinding : CPBinder
|
||||
@implementation _CPMultipleValueBooleanBinding : CPBinder
|
||||
{
|
||||
CPBindingOperationKind _operation;
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)aBinding
|
||||
@@ -451,7 +469,14 @@ var CPBindingOperationAnd = 0,
|
||||
if (!bindings)
|
||||
return;
|
||||
|
||||
[_source setValue:resolveMultipleValues(aBinding, bindings, CPBindingOperationOr) forKey:aBinding];
|
||||
var baseBinding = aBinding.replace(/\d$/, "");
|
||||
|
||||
[_source setValue:[self resolveMultipleValuesForBinding:baseBinding bindings:bindings booleanOperation:_operation] forKey:baseBinding];
|
||||
}
|
||||
|
||||
- (void)reverseSetValueFor:(CPString)theBinding
|
||||
{
|
||||
// read-only
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
||||
@@ -459,61 +484,146 @@ var CPBindingOperationAnd = 0,
|
||||
[self setValueFor:context];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPKeyValueAndBinding : CPBinder
|
||||
- (BOOL)resolveMultipleValuesForBinding:(CPString)aBinding bindings:(CPDictionary)bindings booleanOperation:(CPBindingOperationKind)operation
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)aBinding
|
||||
{
|
||||
var bindings = [bindingsMap objectForKey:[_source UID]];
|
||||
|
||||
if (!bindings)
|
||||
return;
|
||||
|
||||
[_source setValue:resolveMultipleValues(aBinding, bindings, CPBindingOperationAnd) forKey:aBinding];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObejct change:(CPDictionary)changes context:(id)context
|
||||
{
|
||||
[self setValueFor:context];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var resolveMultipleValues = function(/*CPString*/key, /*CPDictionary*/bindings, /*GSBindingOperationKind*/operation)
|
||||
{
|
||||
var bindingName = key,
|
||||
var bindingName = aBinding,
|
||||
theBinding,
|
||||
count = 1;
|
||||
count = 2;
|
||||
|
||||
while (theBinding = [bindings objectForKey:bindingName])
|
||||
{
|
||||
var infoDictionary = theBinding._info,
|
||||
object = [infoDictionary objectForKey:CPObservedObjectKey],
|
||||
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey],
|
||||
options = [infoDictionary objectForKey:CPOptionsKey];
|
||||
var info = theBinding._info,
|
||||
object = [info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [info objectForKey:CPObservedKeyPathKey],
|
||||
options = [info objectForKey:CPOptionsKey],
|
||||
value = [object valueForKeyPath:keyPath];
|
||||
|
||||
var value = [theBinding transformValue:[object valueForKeyPath:keyPath] withOptions:options];
|
||||
if (CPIsControllerMarker(value))
|
||||
{
|
||||
[self raiseIfNotApplicable:value forKeyPath:keyPath options:options];
|
||||
value = [theBinding _placeholderForMarker:value];
|
||||
}
|
||||
else
|
||||
value = [theBinding transformValue:value withOptions:options];
|
||||
|
||||
if (value == operation)
|
||||
return operation;
|
||||
if (operation === CPBindingOperationOr)
|
||||
{
|
||||
// Any true condition means true for OR
|
||||
if (value)
|
||||
return YES;
|
||||
}
|
||||
|
||||
bindingName = [CPString stringWithFormat:@"%@%i", key, ++count];
|
||||
// Any false condition means false for AND
|
||||
else if (!value)
|
||||
return NO;
|
||||
|
||||
bindingName = aBinding + (count++);
|
||||
}
|
||||
|
||||
return !operation;
|
||||
};
|
||||
// If we get here, all OR conditions were false or all AND conditions were true
|
||||
return operation === CPBindingOperationOr ? NO : YES;
|
||||
}
|
||||
|
||||
var invokeAction = function(/*CPString*/targetKey, /*CPString*/argumentKey, /*CPDictionary*/bindings)
|
||||
@end
|
||||
|
||||
@implementation CPMultipleValueAndBinding : _CPMultipleValueBooleanBinding
|
||||
|
||||
- (id)init
|
||||
{
|
||||
var theBinding = [bindings objectForKey:targetKey],
|
||||
infoDictionary = theBinding._info,
|
||||
if (self = [super init])
|
||||
_operation = CPBindingOperationAnd;
|
||||
|
||||
object = [infoDictionary objectForKey:CPObservedObjectKey],
|
||||
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey],
|
||||
options = [infoDictionary objectForKey:CPOptionsKey],
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPMultipleValueOrBinding : _CPMultipleValueBooleanBinding
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
_operation = CPBindingOperationOr;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMultipleValueArgumentBinding : CPBinder
|
||||
{
|
||||
CPString _argumentBinding;
|
||||
CPString _targetBinding;
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)theBinding
|
||||
{
|
||||
// Called when the binding is first created
|
||||
[self checkForNullBinding:theBinding initializing:YES];
|
||||
}
|
||||
|
||||
- (void)reverseSetValueFor:(CPString)theBinding
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
||||
{
|
||||
// context is the binding name
|
||||
[self checkForNullBinding:context initializing:NO];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
|
||||
When the value of a multiple-value argument binding changes,
|
||||
if the binding is marked not to allow null arguments, we have to check
|
||||
if the binding's value is nil and disable the button if it is.
|
||||
Otherwise the button is enabled.
|
||||
*/
|
||||
- (void)checkForNullBinding:(CPString)theBinding initializing:(BOOL)isInitializing
|
||||
{
|
||||
// Only done for buttons
|
||||
if (![_source isKindOfClass:CPButton])
|
||||
return;
|
||||
|
||||
// We start with the button enabled for the first argument during init,
|
||||
// and subsequent checks can disable it.
|
||||
if (isInitializing && theBinding === CPArgumentBinding)
|
||||
[_source setEnabled:YES];
|
||||
|
||||
var bindings = [bindingsMap valueForKey:[_source UID]],
|
||||
binding = [bindings objectForKey:theBinding],
|
||||
info = binding._info,
|
||||
options = [info objectForKey:CPOptionsKey];
|
||||
|
||||
if (![options valueForKey:CPAllowsNullArgumentBindingOption])
|
||||
{
|
||||
var object = [info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [info objectForKey:CPObservedKeyPathKey],
|
||||
value = [object valueForKeyPath:keyPath];
|
||||
|
||||
if (value === nil || value === undefined)
|
||||
{
|
||||
[_source setEnabled:NO];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If a binding value changed and did not fail the null test, enable the button
|
||||
if (!isInitializing)
|
||||
[_source setEnabled:YES];
|
||||
}
|
||||
|
||||
- (void)invokeAction
|
||||
{
|
||||
var bindings = [bindingsMap valueForKey:[_source UID]],
|
||||
theBinding = [bindings objectForKey:CPTargetBinding],
|
||||
|
||||
info = theBinding._info,
|
||||
object = [info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [info objectForKey:CPObservedKeyPathKey],
|
||||
options = [info objectForKey:CPOptionsKey],
|
||||
|
||||
target = [object valueForKeyPath:keyPath],
|
||||
selector = [options objectForKey:CPSelectorNameBindingOption];
|
||||
@@ -521,27 +631,174 @@ var invokeAction = function(/*CPString*/targetKey, /*CPString*/argumentKey, /*CP
|
||||
if (!target || !selector)
|
||||
return;
|
||||
|
||||
var invocation = [CPInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]];
|
||||
[invocation setSelector:selector];
|
||||
|
||||
var bindingName = argumentKey,
|
||||
var invocation = [CPInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]],
|
||||
bindingName = CPArgumentBinding,
|
||||
count = 1;
|
||||
|
||||
while (theBinding = [bindings objectForKey:bindingName])
|
||||
{
|
||||
infoDictionary = theBinding._info;
|
||||
info = theBinding._info;
|
||||
object = [info objectForKey:CPObservedObjectKey];
|
||||
keyPath = [info objectForKey:CPObservedKeyPathKey];
|
||||
|
||||
keyPath = [infoDictionary objectForKey:CPObserverKeyPathKey];
|
||||
object = [[infoDictionary objectForKey:CPObservedObjectKey] valueForKeyPath:keyPath];
|
||||
[invocation setArgument:[object valueForKeyPath:keyPath] atIndex:++count];
|
||||
|
||||
if (object)
|
||||
[invocation setArgument:object atIndex:++count];
|
||||
|
||||
bindingName = [CPString stringWithFormat:@"%@%i", argumentKey, count];
|
||||
bindingName = CPArgumentBinding + count;
|
||||
}
|
||||
|
||||
[invocation invoke];
|
||||
};
|
||||
[invocation setSelector:selector];
|
||||
[invocation invokeWithTarget:target];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPArgumentTargetBinding : _CPMultipleValueArgumentBinding
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_argumentBinding = CPArgumentBinding;
|
||||
_targetBinding = CPTargetBinding;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPDoubleClickArgumentTargetBinding : _CPMultipleValueArgumentBinding
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_argumentBinding = CPArgumentBinding;
|
||||
_targetBinding = CPTargetBinding;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*!
|
||||
Abstract superclass for CPValueWithPatternBinding and CPTitleWithPatternBinding.
|
||||
*/
|
||||
@implementation _CPPatternBinding : CPBinder
|
||||
{
|
||||
CPString _bindingKey;
|
||||
CPString _patternPlaceholder;
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)aBinding
|
||||
{
|
||||
var bindings = [bindingsMap valueForKey:[_source UID]];
|
||||
|
||||
if (!bindings)
|
||||
return;
|
||||
|
||||
// Strip off any trailing number from the binding name
|
||||
var baseBinding = aBinding.replace(/\d$/, ""),
|
||||
result = [self resolveMultipleValuesForBindings:bindings];
|
||||
|
||||
if (result.isPlaceholder)
|
||||
[self setPlaceholderValue:result.value withMarker:result.marker forBinding:baseBinding];
|
||||
else
|
||||
[self setValue:result.value forBinding:baseBinding];
|
||||
}
|
||||
|
||||
- (void)reverseSetValueFor:(CPString)theBinding
|
||||
{
|
||||
// read-only
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
||||
{
|
||||
[self setValueFor:context];
|
||||
}
|
||||
|
||||
- (JSObject)resolveMultipleValuesForBindings:(CPDictionary)bindings
|
||||
{
|
||||
var theBinding,
|
||||
result = { value:@"", isPlaceholder:NO, marker:nil };
|
||||
|
||||
for (var count = 1; theBinding = [bindings objectForKey:_bindingKey + count]; ++count)
|
||||
{
|
||||
var info = theBinding._info,
|
||||
object = [info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [info objectForKey:CPObservedKeyPathKey],
|
||||
options = [info objectForKey:CPOptionsKey],
|
||||
value = [object valueForKeyPath:keyPath];
|
||||
|
||||
if (count === 1)
|
||||
result.value = [options objectForKey:CPDisplayPatternBindingOption];
|
||||
|
||||
if (CPIsControllerMarker(value))
|
||||
{
|
||||
[self raiseIfNotApplicable:value forKeyPath:keyPath options:options];
|
||||
|
||||
result.isPlaceholder = YES;
|
||||
result.marker = value;
|
||||
|
||||
value = [theBinding _placeholderForMarker:value];
|
||||
}
|
||||
else
|
||||
value = [theBinding transformValue:value withOptions:options];
|
||||
|
||||
if (value === nil || value === undefined)
|
||||
value = @"";
|
||||
|
||||
result.value = result.value.replace("%{" + _patternPlaceholder + count + "}@", [value description]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/*!
|
||||
Users of this class must override setValue:forKey: and if the
|
||||
key is CPDisplayPatternValueBinding, set the appropriate value
|
||||
for the control class. For example, CPTextField uses setObjectValue.
|
||||
*/
|
||||
@implementation CPValueWithPatternBinding : _CPPatternBinding
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_bindingKey = CPDisplayPatternValueBinding;
|
||||
_patternPlaceholder = @"value";
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/*!
|
||||
Users of this class must override setValue:forKey: and if the
|
||||
key is CPDisplayPatternTitleBinding, set the appropriate value
|
||||
for the control class. For example, CPBox uses setTitle.
|
||||
*/
|
||||
@implementation CPTitleWithPatternBinding : _CPPatternBinding
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_bindingKey = CPDisplayPatternTitleBinding;
|
||||
_patternPlaceholder = @"title";
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// Keys in options dictionary
|
||||
|
||||
@@ -558,11 +815,16 @@ CPNullMarker = @"CPNullMarker";
|
||||
|
||||
// Binding name constants
|
||||
CPAlignmentBinding = @"alignment";
|
||||
CPArgumentBinding = @"argument";
|
||||
CPContentArrayBinding = @"contentArray";
|
||||
CPContentBinding = @"content";
|
||||
CPContentObjectBinding = @"contentObject";
|
||||
CPContentObjectsBinding = @"contentObjects";
|
||||
CPContentValuesBinding = @"contentValues";
|
||||
CPDisplayPatternTitleBinding = @"displayPatternTitle";
|
||||
CPDisplayPatternValueBinding = @"displayPatternValue";
|
||||
CPDoubleClickArgumentBinding = @"doubleClickArgument";
|
||||
CPDoubleClickTargetBinding = @"doubleClickTarget";
|
||||
CPEditableBinding = @"editable";
|
||||
CPEnabledBinding = @"enabled";
|
||||
CPFontBinding = @"font";
|
||||
@@ -581,6 +843,7 @@ CPSelectedTagBinding = @"selectedTag";
|
||||
CPSelectedValueBinding = @"selectedValue";
|
||||
CPSelectedValuesBinding = @"selectedValues";
|
||||
CPSelectionIndexesBinding = @"selectionIndexes";
|
||||
CPTargetBinding = @"target";
|
||||
CPTextColorBinding = @"textColor";
|
||||
CPTitleBinding = @"title";
|
||||
CPToolTipBinding = @"toolTip";
|
||||
@@ -589,7 +852,7 @@ CPValueURLBinding = @"valueURL";
|
||||
CPValuePathBinding = @"valuePath";
|
||||
CPDataBinding = @"data";
|
||||
|
||||
//Binding options constants
|
||||
// Binding options constants
|
||||
CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelection";
|
||||
CPAllowsNullArgumentBindingOption = @"CPAllowsNullArgument";
|
||||
CPConditionallySetsEditableBindingOption = @"CPConditionallySetsEditable";
|
||||
|
||||
@@ -72,8 +72,6 @@ CPRatingLevelIndicatorStyle = 3;
|
||||
1.0, @"spacing"];
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
@@ -84,17 +82,11 @@ CPRatingLevelIndicatorStyle = 3;
|
||||
_maxValue = 2;
|
||||
_warningValue = 2;
|
||||
_criticalValue = 2;
|
||||
|
||||
[self _init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var bezelView = [self layoutEphemeralSubviewNamed:"bezel"
|
||||
@@ -348,8 +340,6 @@ var CPLevelIndicatorStyleKey = "CPLevelIndicatorStyleKey",
|
||||
|
||||
_isEditable = [aCoder decodeBoolForKey:CPLevelIndicatorIsEditableKey];
|
||||
|
||||
[self _init];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ var _CPMenuBarVisible = NO,
|
||||
- (float)menuBarHeight
|
||||
{
|
||||
if (self === [CPApp mainMenu])
|
||||
return [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-height" forClass:_CPMenuView];
|
||||
return [CPMenu menuBarHeight];
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
@@ -359,7 +359,12 @@ var _CPMenuBarVisible = NO,
|
||||
*/
|
||||
- (void)itemChanged:(CPMenuItem)aMenuItem
|
||||
{
|
||||
if ([aMenuItem menu] !== self)
|
||||
/*
|
||||
During cib unarchiving, menu items will have a reference to their menu,
|
||||
but of course the items are still being unarchived and the menu's _items
|
||||
have not yet been instantiated. In that case we will not do anything here.
|
||||
*/
|
||||
if ([aMenuItem menu] !== self || !_items)
|
||||
return;
|
||||
|
||||
[aMenuItem setValue:[aMenuItem valueForKey:@"changeCount"] + 1 forKey:@"changeCount"];
|
||||
|
||||
@@ -105,6 +105,14 @@ var CPMenuItemStringRepresentationDictionary = [CPDictionary dictionaryWithObjec
|
||||
_CPMenuItemView _menuItemView;
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if ([aBinding hasPrefix:CPEnabledBinding])
|
||||
return [CPMultipleValueAndBinding class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithTitle:@"" action:nil keyEquivalent:nil];
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPCountedSet.j>
|
||||
@import <Foundation/_CPCollectionKVCOperators.j>
|
||||
|
||||
@import "CPController.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
@@ -517,8 +518,12 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
|
||||
- (void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
|
||||
{
|
||||
if (aKeyPath.indexOf("@") === 0)
|
||||
if (aKeyPath.charAt(0) === "@")
|
||||
{
|
||||
// Simple collection operators are scalar and can't be proxied
|
||||
if ([_CPCollectionKVCOperator isSimpleCollectionOperator:aKeyPath])
|
||||
return;
|
||||
|
||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self];
|
||||
|
||||
proxy._options = options;
|
||||
@@ -541,8 +546,12 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
|
||||
- (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
if (aKeyPath.indexOf("@") === 0)
|
||||
if (aKeyPath.charAt(0) === "@")
|
||||
{
|
||||
// Simple collection operators are scalar and can't be proxied
|
||||
if ([_CPCollectionKVCOperator isSimpleCollectionOperator:aKeyPath])
|
||||
return;
|
||||
|
||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self],
|
||||
index = [_observationProxies indexOfObject:proxy];
|
||||
|
||||
@@ -567,7 +576,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
{
|
||||
var proxy = [_observationProxies objectAtIndex:i],
|
||||
keyPath = [proxy keyPath],
|
||||
operator = keyPath.indexOf(".") === 0;
|
||||
operator = keyPath.charAt(0) === ".";
|
||||
|
||||
if (operator)
|
||||
[self willChangeValueForKey:keyPath];
|
||||
@@ -589,7 +598,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
{
|
||||
var proxy = [_observationProxies objectAtIndex:i],
|
||||
keyPath = [proxy keyPath],
|
||||
operator = keyPath.indexOf(".") === 0;
|
||||
operator = keyPath.charAt(0) === ".";
|
||||
|
||||
if (operator)
|
||||
[self willChangeValueForKey:keyPath];
|
||||
@@ -626,7 +635,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
{
|
||||
var proxy = [_observationProxies objectAtIndex:i],
|
||||
keyPath = [proxy keyPath],
|
||||
operator = keyPath.indexOf(".") === 0;
|
||||
operator = keyPath.charAt(0) === ".";
|
||||
|
||||
if (operator)
|
||||
[self willChangeValueForKey:keyPath];
|
||||
@@ -700,12 +709,18 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
|
||||
- (id)valueForKeyPath:(CPString)theKeyPath
|
||||
{
|
||||
var values = [[_controller selectedObjects] valueForKeyPath:theKeyPath],
|
||||
value = [self _controllerMarkerForValues:values];
|
||||
var values = [[_controller selectedObjects] valueForKeyPath:theKeyPath];
|
||||
|
||||
[_cachedValues setObject:value forKey:theKeyPath];
|
||||
// Simple collection operators like @count return a scalar value, not an array or set
|
||||
if ([values isKindOfClass:CPArray] || [values isKindOfClass:CPSet])
|
||||
{
|
||||
var value = [self _controllerMarkerForValues:values];
|
||||
[_cachedValues setObject:value forKey:theKeyPath];
|
||||
|
||||
return value;
|
||||
return value;
|
||||
}
|
||||
else
|
||||
return values;
|
||||
}
|
||||
|
||||
- (id)valueForKey:(CPString)theKeyPath
|
||||
|
||||
@@ -679,7 +679,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
if ([self pullsDown])
|
||||
{
|
||||
var positionedItem = nil,
|
||||
location = CGPointMake(0.0, CGRectGetMaxY(bounds));
|
||||
location = CGPointMake(0.0, CGRectGetMaxY(bounds) - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -30,19 +30,19 @@
|
||||
@global
|
||||
@group CPProgressIndicatorStyle
|
||||
*/
|
||||
CPProgressIndicatorBarStyle = 0;
|
||||
CPProgressIndicatorBarStyle = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPProgressIndicatorStyle
|
||||
*/
|
||||
CPProgressIndicatorSpinningStyle = 1;
|
||||
CPProgressIndicatorSpinningStyle = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPProgressIndicatorStyle
|
||||
*/
|
||||
CPProgressIndicatorHUDBarStyle = 2;
|
||||
CPProgressIndicatorHUDBarStyle = 2;
|
||||
|
||||
var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
var CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -61,7 +61,7 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
|
||||
CPControlSize _controlSize;
|
||||
|
||||
BOOL _isIndeterminate;
|
||||
BOOL _indeterminate;
|
||||
CPProgressIndicatorStyle _style;
|
||||
|
||||
BOOL _isAnimating;
|
||||
@@ -77,8 +77,22 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null], 25, [CPNull null], [CPNull null], [CPNull null], [CPNull null]]
|
||||
forKeys:[@"indeterminate-bar-color", @"bar-color", @"default-height", @"bezel-color", @"spinning-mini-gif", @"spinning-small-gif", @"spinning-regular-gif"]];
|
||||
return [CPDictionary dictionaryWithObjectsAndKeys:
|
||||
[CPNull null], @"indeterminate-bar-color",
|
||||
[CPNull null], @"bar-color",
|
||||
20, @"default-height",
|
||||
[CPNull null], @"bezel-color",
|
||||
[CPNull null], @"spinning-mini-gif",
|
||||
[CPNull null], @"spinning-small-gif",
|
||||
[CPNull null], @"spinning-regular-gif"];
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding === CPValueBinding || aBinding === @"isIndeterminate")
|
||||
return [_CPProgressIndicatorBinder class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -259,12 +273,12 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
Specifies whether this progress indicator should be indeterminate or display progress based on it's max and min.
|
||||
@param isDeterminate \c YES makes the indicator indeterminate
|
||||
*/
|
||||
- (void)setIndeterminate:(BOOL)isIndeterminate
|
||||
- (void)setIndeterminate:(BOOL)indeterminate
|
||||
{
|
||||
if (_isIndeterminate == isIndeterminate)
|
||||
if (_indeterminate == indeterminate)
|
||||
return;
|
||||
|
||||
_isIndeterminate = isIndeterminate;
|
||||
_indeterminate = indeterminate;
|
||||
|
||||
[self updateBackgroundColor];
|
||||
}
|
||||
@@ -274,7 +288,7 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
*/
|
||||
- (BOOL)isIndeterminate
|
||||
{
|
||||
return _isIndeterminate;
|
||||
return _indeterminate;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -369,7 +383,7 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
if (barWidth > 0.0 && barWidth < 4.0)
|
||||
barWidth = 4.0;
|
||||
|
||||
if (_isIndeterminate)
|
||||
if (_indeterminate)
|
||||
barWidth = width;
|
||||
|
||||
return _CGRectMake(0, 0, barWidth, [self valueForThemeAttribute:@"default-height"]);
|
||||
@@ -381,17 +395,18 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
/* @ignore */
|
||||
- (void)updateBackgroundColor
|
||||
{
|
||||
if ([CPProgressIndicatorSpinningStyleColors count] === 0)
|
||||
{
|
||||
CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [self valueForThemeAttribute:@"spinning-mini-gif"];
|
||||
CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [self valueForThemeAttribute:@"spinning-small-gif"];
|
||||
CPProgressIndicatorSpinningStyleColors[CPRegularControlSize] = [self valueForThemeAttribute:@"spinning-regular-gif"];
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [self valueForThemeAttribute:@"spinning-mini-gif"];
|
||||
CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [self valueForThemeAttribute:@"spinning-small-gif"];
|
||||
CPProgressIndicatorSpinningStyleColors[CPRegularControlSize] = [self valueForThemeAttribute:@"spinning-regular-gif"];
|
||||
|
||||
if (YES)//_isBezeled)
|
||||
{
|
||||
if (_style == CPProgressIndicatorSpinningStyle)
|
||||
@@ -411,11 +426,10 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
if (_isIndeterminate)
|
||||
if (_indeterminate)
|
||||
[barView setBackgroundColor:[self currentValueForThemeAttribute:@"indeterminate-bar-color"]];
|
||||
else
|
||||
[barView setBackgroundColor:[self currentValueForThemeAttribute:@"bar-color"]];
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -435,7 +449,7 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
_maxValue = [aCoder decodeObjectForKey:@"_maxValue"];
|
||||
_doubleValue = [aCoder decodeObjectForKey:@"_doubleValue"];
|
||||
_controlSize = [aCoder decodeObjectForKey:@"_controlSize"];
|
||||
_isIndeterminate = [aCoder decodeObjectForKey:@"_isIndeterminate"];
|
||||
_indeterminate = [aCoder decodeObjectForKey:@"_indeterminate"];
|
||||
_style = [aCoder decodeIntForKey:@"_style"];
|
||||
_isAnimating = [aCoder decodeObjectForKey:@"_isAnimating"];
|
||||
_isDisplayedWhenStoppedSet = [aCoder decodeObjectForKey:@"_isDisplayedWhenStoppedSet"];
|
||||
@@ -449,8 +463,8 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
// Don't encode the background colour. It can be recreated based on the flags and if encoded causes hardcoded
|
||||
// image paths in the cib while just wasting space.
|
||||
// Don't encode the background colour. It can be recreated based on the flags
|
||||
// and if encoded causes hardcoded image paths in the cib while just wasting space.
|
||||
var backgroundColor = [self backgroundColor];
|
||||
[self setBackgroundColor:nil];
|
||||
[super encodeWithCoder:aCoder];
|
||||
@@ -460,7 +474,7 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
[aCoder encodeObject:_maxValue forKey:@"_maxValue"];
|
||||
[aCoder encodeObject:_doubleValue forKey:@"_doubleValue"];
|
||||
[aCoder encodeObject:_controlSize forKey:@"_controlSize"];
|
||||
[aCoder encodeObject:_isIndeterminate forKey:@"_isIndeterminate"];
|
||||
[aCoder encodeObject:_indeterminate forKey:@"_indeterminate"];
|
||||
[aCoder encodeInt:_style forKey:@"_style"];
|
||||
[aCoder encodeObject:_isAnimating forKey:@"_isAnimating"];
|
||||
[aCoder encodeObject:_isDisplayedWhenStoppedSet forKey:@"_isDisplayedWhenStoppedSet"];
|
||||
@@ -468,3 +482,52 @@ var CPProgressIndicatorSpinningStyleColors = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation _CPProgressIndicatorBinder : CPBinder
|
||||
|
||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
|
||||
{
|
||||
var value = aBinding === CPValueBinding ? 0.0 : YES;
|
||||
|
||||
[self _setPlaceholder:value forMarker:CPMultipleValuesMarker isDefault:YES];
|
||||
[self _setPlaceholder:value forMarker:CPNoSelectionMarker isDefault:YES];
|
||||
[self _setPlaceholder:value forMarker:CPNotApplicableMarker isDefault:YES];
|
||||
[self _setPlaceholder:value forMarker:CPNullMarker isDefault:YES];
|
||||
}
|
||||
|
||||
- (id)valueForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding === CPValueBinding)
|
||||
return [_source doubleValue];
|
||||
else if (aBinding === @"isIndeterminate")
|
||||
[_source isIndeterminate];
|
||||
else
|
||||
return [super valueForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (BOOL)_setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding === CPValueBinding)
|
||||
[_source setDoubleValue:aValue];
|
||||
else if (aBinding === @"isIndeterminate")
|
||||
[_source setIndeterminate:aValue];
|
||||
else
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
if (![self _setValue:aValue forBinding:aBinding])
|
||||
[super setValue:aValue forBinding:aBinding];
|
||||
}
|
||||
|
||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
||||
{
|
||||
if (![self _setValue:aValue forBinding:aBinding])
|
||||
[super setPlaceholderValue:aValue withMarker:aMarker forBinding:aBinding];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -82,15 +82,15 @@
|
||||
}
|
||||
|
||||
/*! @cond */
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (theBinding == CPValueBinding)
|
||||
if (aBinding == CPValueBinding)
|
||||
return [CPPredicateEditorValueBinder class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)_replacementKeyPathForBinding:(CPString)aBinding
|
||||
- (CPString)_replacementKeyPathForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding == CPValueBinding)
|
||||
return @"predicate";
|
||||
|
||||
@@ -31,11 +31,9 @@
|
||||
*/
|
||||
@implementation CPShadow : CPObject
|
||||
{
|
||||
CGSize _offset;
|
||||
float _blurRadius;
|
||||
CPColor _color;
|
||||
|
||||
CPString _cssString;
|
||||
CGSize _offset @accessors(property=shadowOffset);
|
||||
float _blurRadius @accessors(property=shadowBlurRadius);
|
||||
CPColor _color @accessors(property=shadowColor);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -60,35 +58,16 @@
|
||||
_offset = anOffset;
|
||||
_blurRadius = aBlurRadius;
|
||||
_color = aColor;
|
||||
|
||||
_cssString = [_color cssString] + " " + ROUND(anOffset.width) + @"px " + ROUND(anOffset.height) + @"px " + ROUND(_blurRadius) + @"px";
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the shadow's offset.
|
||||
*/
|
||||
- (CGSize)shadowOffset
|
||||
- (void)set
|
||||
{
|
||||
return _offset;
|
||||
}
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
/*!
|
||||
Returns the shadow's blur radius
|
||||
*/
|
||||
- (float)shadowBlurRadius
|
||||
{
|
||||
return _blurRadius;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the shadow's color.
|
||||
*/
|
||||
- (CPColor)shadowColor
|
||||
{
|
||||
return _color;
|
||||
CGContextSetShadowWithColor(context, _offset, _blurRadius, _color);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -96,7 +75,7 @@
|
||||
*/
|
||||
- (CPString)cssString
|
||||
{
|
||||
return _cssString;
|
||||
return [_color cssString] + " " + ROUND(_offset.width) + @"px " + ROUND(_offset.height) + @"px " + ROUND(_blurRadius) + @"px";
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -80,15 +80,15 @@
|
||||
return [CPStepper stepperWithInitialValue:0.0 minValue:0.0 maxValue:59.0];
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (theBinding == CPValueBinding || theBinding == CPMinValueBinding || theBinding == CPMaxValueBinding)
|
||||
if (aBinding == CPValueBinding || aBinding == CPMinValueBinding || aBinding == CPMaxValueBinding)
|
||||
return [_CPStepperValueBinder class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)_replacementKeyPathForBinding:(CPString)aBinding
|
||||
- (CPString)_replacementKeyPathForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding == CPValueBinding)
|
||||
return @"doubleValue";
|
||||
|
||||
@@ -129,6 +129,18 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
CPControlSize _controlSize;
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding === CPValueBinding)
|
||||
return [_CPTextFieldValueBinder class];
|
||||
else if ([aBinding hasPrefix:CPDisplayPatternValueBinding])
|
||||
return [_CPTextFieldPatternValueBinder class];
|
||||
else if ([aBinding hasPrefix:CPEditableBinding])
|
||||
return [CPMultipleValueAndBinding class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
+ (CPTextField)textFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth
|
||||
{
|
||||
return [self textFieldWithStringValue:aStringValue placeholder:aPlaceholder width:aWidth theme:[CPTheme defaultTheme]];
|
||||
@@ -192,14 +204,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
return "textfield";
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
{
|
||||
if (theBinding === CPValueBinding)
|
||||
return [_CPTextFieldValueBinder class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMake(1.0, 0.0, 0.0, 0.0), [CPNull null]]
|
||||
@@ -612,8 +616,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
if (!CPFeatureIsCompatible(CPInputSetFontOutsideOfDOM))
|
||||
element.style.font = [font cssString];
|
||||
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
|
||||
CPTextFieldInputIsActive = YES;
|
||||
|
||||
if (document.attachEvent)
|
||||
@@ -1628,17 +1630,23 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
@end
|
||||
|
||||
@implementation _CPTextFieldValueBinder : CPBinder
|
||||
|
||||
+ (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPBinder)aBinding
|
||||
{
|
||||
var selector = "_updatePlaceholdersWithOptions:",
|
||||
implementation = class_getMethodImplementation([aBinding superclass], selector);
|
||||
|
||||
implementation.apply(aBinding, [aBinding, selector, options]);
|
||||
|
||||
[aBinding _setPlaceholder:@"Multiple Values" forMarker:CPMultipleValuesMarker isDefault:YES];
|
||||
[aBinding _setPlaceholder:@"No Selection" forMarker:CPNoSelectionMarker isDefault:YES];
|
||||
[aBinding _setPlaceholder:@"Not Applicable" forMarker:CPNotApplicableMarker isDefault:YES];
|
||||
[aBinding _setPlaceholder:@"" forMarker:CPNullMarker isDefault:YES];
|
||||
}
|
||||
|
||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPBinder)aBinding
|
||||
{
|
||||
[super _updatePlaceholdersWithOptions:options];
|
||||
|
||||
[self _setPlaceholder:@"Multiple Values" forMarker:CPMultipleValuesMarker isDefault:YES];
|
||||
[self _setPlaceholder:@"No Selection" forMarker:CPNoSelectionMarker isDefault:YES];
|
||||
[self _setPlaceholder:@"Not Applicable" forMarker:CPNotApplicableMarker isDefault:YES];
|
||||
[self _setPlaceholder:@"" forMarker:CPNullMarker isDefault:YES];
|
||||
[[self class] _updatePlaceholdersWithOptions:options forBinding:self];
|
||||
}
|
||||
|
||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
||||
@@ -1654,3 +1662,22 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPTextFieldPatternValueBinder : CPValueWithPatternBinding
|
||||
|
||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPBinder)aBinding
|
||||
{
|
||||
[_CPTextFieldValueBinder _updatePlaceholdersWithOptions:options forBinding:self];
|
||||
}
|
||||
|
||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
||||
{
|
||||
[_source setPlaceholderString:aValue];
|
||||
[_source setObjectValue:nil];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
[_source setObjectValue:aValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -27,13 +27,14 @@
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
@import <Foundation/CPTimer.j>
|
||||
|
||||
@import "_CPAutocompleteMenu.j"
|
||||
@import "CPButton.j"
|
||||
@import "CPPopUpButton.j"
|
||||
@import "CPScrollView.j"
|
||||
@import "CPTableView.j"
|
||||
@import "CPText.j"
|
||||
@import "CPTextField.j"
|
||||
@import "CPWindow_Constants.j"
|
||||
@import "_CPAutocompleteMenu.j"
|
||||
|
||||
@global CPApp
|
||||
|
||||
@@ -59,6 +60,9 @@ var CPScrollDestinationNone = 0,
|
||||
CPScrollDestinationLeft = 1,
|
||||
CPScrollDestinationRight = 2;
|
||||
|
||||
CPTokenFieldDisclosureButtonType = 0;
|
||||
CPTokenFieldDeleteButtonType = 1;
|
||||
|
||||
@implementation CPTokenField : CPTextField
|
||||
{
|
||||
CPScrollView _tokenScrollView;
|
||||
@@ -76,6 +80,8 @@ var CPScrollDestinationNone = 0,
|
||||
CPEvent _mouseDownEvent;
|
||||
|
||||
BOOL _shouldNotifyTarget;
|
||||
|
||||
int _buttonType @accessors(property=buttonType);
|
||||
}
|
||||
|
||||
+ (CPCharacterSet)defaultTokenizingCharacterSet
|
||||
@@ -104,6 +110,7 @@ var CPScrollDestinationNone = 0,
|
||||
{
|
||||
_completionDelay = [[self class] defaultCompletionDelay];
|
||||
_tokenizingCharacterSet = [[self class] defaultTokenizingCharacterSet];
|
||||
_buttonType = CPTokenFieldDisclosureButtonType;
|
||||
[self setBezeled:YES];
|
||||
|
||||
[self _init];
|
||||
@@ -561,7 +568,7 @@ var CPScrollDestinationNone = 0,
|
||||
|
||||
if (newToken === nil)
|
||||
{
|
||||
newToken = [[_CPTokenFieldToken alloc] init];
|
||||
newToken = [_CPTokenFieldToken new];
|
||||
[newToken setTokenField:self];
|
||||
[newToken setRepresentedObject:tokenObject];
|
||||
[newToken setStringValue:tokenValue];
|
||||
@@ -652,7 +659,7 @@ var CPScrollDestinationNone = 0,
|
||||
return CPTextFieldBlurFunction(
|
||||
anEvent,
|
||||
CPTokenFieldInputOwner,
|
||||
[CPTokenFieldInputOwner._tokenScrollView documentView]._DOMElement,
|
||||
CPTokenFieldInputOwner ? [CPTokenFieldInputOwner._tokenScrollView documentView]._DOMElement : nil,
|
||||
CPTokenFieldDOMInputElement,
|
||||
CPTokenFieldInputResigning,
|
||||
AT_REF(CPTokenFieldInputDidBlur));
|
||||
@@ -1089,6 +1096,8 @@ var CPScrollDestinationNone = 0,
|
||||
tokenFrame = fitAndFrame(tokenViewSize.width, tokenViewSize.height);
|
||||
|
||||
[tokenView setFrame:tokenFrame];
|
||||
|
||||
[tokenView setButtonType:_buttonType];
|
||||
}
|
||||
|
||||
if (isEditing && !_selectedRange.length && _CPMaxRange(_selectedRange) >= [tokens count])
|
||||
@@ -1241,6 +1250,30 @@ var CPScrollDestinationNone = 0,
|
||||
return aString;
|
||||
}
|
||||
|
||||
- (BOOL)_hasMenuForRepresentedObject:(id)aRepresentedObject
|
||||
{
|
||||
var delegate = [self delegate];
|
||||
if ([delegate respondsToSelector:@selector(tokenField:hasMenuForRepresentedObject:)] &&
|
||||
[delegate respondsToSelector:@selector(tokenField:menuForRepresentedObject:)])
|
||||
return [delegate tokenField:self hasMenuForRepresentedObject:aRepresentedObject];
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (CPMenu)_menuForRepresentedObject:(id)aRepresentedObject
|
||||
{
|
||||
var delegate = [self delegate];
|
||||
if ([delegate respondsToSelector:@selector(tokenField:hasMenuForRepresentedObject:)] &&
|
||||
[delegate respondsToSelector:@selector(tokenField:menuForRepresentedObject:)])
|
||||
{
|
||||
var hasMenu = [delegate tokenField:self hasMenuForRepresentedObject:aRepresentedObject];
|
||||
if (hasMenu)
|
||||
return [delegate tokenField:self menuForRepresentedObject:aRepresentedObject] || nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
// We put the string on the pasteboard before calling this delegate method.
|
||||
// By default, we write the NSStringPboardType as well as an array of NSStrings.
|
||||
// - (BOOL)tokenField:(NSTokenField *)tokenField writeRepresentedObjects:(NSArray *)objects toPasteboard:(NSPasteboard *)pboard;
|
||||
@@ -1265,13 +1298,25 @@ var CPScrollDestinationNone = 0,
|
||||
[_autocompleteMenu _hideCompletions];
|
||||
}
|
||||
|
||||
|
||||
- (void)setButtonType:(int)aButtonType
|
||||
{
|
||||
if (_buttonType === aButtonType)
|
||||
return;
|
||||
|
||||
_buttonType = aButtonType;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPTokenFieldToken : CPTextField
|
||||
{
|
||||
_CPTokenFieldTokenCloseButton _deleteButton;
|
||||
CPTokenField _tokenField;
|
||||
id _representedObject;
|
||||
_CPTokenFieldTokenCloseButton _deleteButton;
|
||||
_CPTokenFieldTokenDisclosureButton _disclosureButton;
|
||||
CPTokenField _tokenField;
|
||||
id _representedObject;
|
||||
int _buttonType;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
@@ -1288,12 +1333,10 @@ var CPScrollDestinationNone = 0,
|
||||
{
|
||||
if (self = [super initWithFrame:frame])
|
||||
{
|
||||
_deleteButton = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
[self addSubview:_deleteButton];
|
||||
|
||||
[self setEditable:NO];
|
||||
[self setHighlighted:NO];
|
||||
[self setBezeled:YES];
|
||||
[self setButtonType:CPTokenFieldDisclosureButtonType];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -1317,6 +1360,7 @@ var CPScrollDestinationNone = 0,
|
||||
- (void)setRepresentedObject:(id)representedObject
|
||||
{
|
||||
_representedObject = representedObject;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setEditable:(BOOL)shouldBeEditable
|
||||
@@ -1329,9 +1373,12 @@ var CPScrollDestinationNone = 0,
|
||||
{
|
||||
var r = [super setThemeState:aState];
|
||||
|
||||
// Share hover state with the delete button.
|
||||
if (r && aState === CPThemeStateHovered)
|
||||
[_deleteButton setThemeState:aState];
|
||||
// Share hover state with the disclosure and delete buttons.
|
||||
if (aState & CPThemeStateHovered)
|
||||
{
|
||||
[_disclosureButton setThemeState:CPThemeStateHovered];
|
||||
[_deleteButton setThemeState:CPThemeStateHovered];
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -1340,9 +1387,12 @@ var CPScrollDestinationNone = 0,
|
||||
{
|
||||
var r = [super unsetThemeState:aState];
|
||||
|
||||
// Share hover state with the delete button.
|
||||
if (r && aState === CPThemeStateHovered)
|
||||
[_deleteButton unsetThemeState:aState];
|
||||
// Share hover state with the disclosure and delete button.
|
||||
if (aState & CPThemeStateHovered)
|
||||
{
|
||||
[_disclosureButton unsetThemeState:CPThemeStateHovered];
|
||||
[_deleteButton unsetThemeState:CPThemeStateHovered];
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -1360,6 +1410,47 @@ var CPScrollDestinationNone = 0,
|
||||
return size;
|
||||
}
|
||||
|
||||
- (void)setButtonType:(int)aButtonType
|
||||
{
|
||||
if (_buttonType === aButtonType)
|
||||
return;
|
||||
|
||||
_buttonType = aButtonType;
|
||||
|
||||
if (_buttonType === CPTokenFieldDisclosureButtonType)
|
||||
{
|
||||
if (_deleteButton)
|
||||
{
|
||||
[_deleteButton removeFromSuperview];
|
||||
_deleteButton = nil;
|
||||
}
|
||||
|
||||
if (!_disclosureButton)
|
||||
{
|
||||
_disclosureButton = [[_CPTokenFieldTokenDisclosureButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
[self addSubview:_disclosureButton];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_disclosureButton)
|
||||
{
|
||||
[_disclosureButton removeFromSuperview];
|
||||
_disclosureButton = nil;
|
||||
}
|
||||
|
||||
if (!_deleteButton)
|
||||
{
|
||||
_deleteButton = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
[self addSubview:_deleteButton];
|
||||
[_deleteButton setTarget:self];
|
||||
[_deleteButton setAction:@selector(_delete:)];
|
||||
}
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
@@ -1368,17 +1459,33 @@ var CPScrollDestinationNone = 0,
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:@"content-view"];
|
||||
|
||||
if (bezelView)
|
||||
if (bezelView && _tokenField)
|
||||
{
|
||||
[_deleteButton setTarget:self];
|
||||
[_deleteButton setAction:@selector(_delete:)];
|
||||
[_deleteButton setEnabled:[self isEditable]];
|
||||
switch (_buttonType)
|
||||
{
|
||||
case CPTokenFieldDisclosureButtonType:
|
||||
var shouldBeEnabled = [self hasMenu];
|
||||
[_disclosureButton setHidden:!shouldBeEnabled];
|
||||
|
||||
var frame = [bezelView frame],
|
||||
buttonOffset = [_deleteButton currentValueForThemeAttribute:@"offset"],
|
||||
buttonSize = [_deleteButton currentValueForThemeAttribute:@"min-size"];
|
||||
if (shouldBeEnabled)
|
||||
[_disclosureButton setMenu:[self menu]];
|
||||
|
||||
[_deleteButton setFrame:_CGRectMake(CGRectGetMaxX(frame) - buttonOffset.x, CGRectGetMinY(frame) + buttonOffset.y, buttonSize.width, buttonSize.height)];
|
||||
var frame = [bezelView frame],
|
||||
buttonOffset = [_disclosureButton currentValueForThemeAttribute:@"offset"],
|
||||
buttonSize = [_disclosureButton currentValueForThemeAttribute:@"min-size"];
|
||||
|
||||
[_disclosureButton setFrame:_CGRectMake(CGRectGetMaxX(frame) - buttonOffset.x, CGRectGetMinY(frame) + buttonOffset.y, buttonSize.width, buttonSize.height)];
|
||||
break;
|
||||
case CPTokenFieldDeleteButtonType:
|
||||
[_deleteButton setEnabled:[self isEditable] && [self isEnabled]];
|
||||
|
||||
var frame = [bezelView frame],
|
||||
buttonOffset = [_deleteButton currentValueForThemeAttribute:@"offset"],
|
||||
buttonSize = [_deleteButton currentValueForThemeAttribute:@"min-size"];
|
||||
|
||||
[_deleteButton setFrame:_CGRectMake(CGRectGetMaxX(frame) - buttonOffset.x, CGRectGetMinY(frame) + buttonOffset.y, buttonSize.width, buttonSize.height)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1398,11 +1505,18 @@ var CPScrollDestinationNone = 0,
|
||||
[_tokenField _deleteToken:self];
|
||||
}
|
||||
|
||||
- (BOOL)hasMenu
|
||||
{
|
||||
return [_tokenField _hasMenuForRepresentedObject:_representedObject];
|
||||
}
|
||||
|
||||
- (CPMenu)menu
|
||||
{
|
||||
return [_tokenField _menuForRepresentedObject:_representedObject];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
Theming hook.
|
||||
*/
|
||||
@implementation _CPTokenFieldTokenCloseButton : CPButton
|
||||
{
|
||||
}
|
||||
@@ -1421,11 +1535,73 @@ var CPScrollDestinationNone = 0,
|
||||
return "tokenfield-token-close-button";
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(CPEvent)anEvent
|
||||
{
|
||||
// Don't toggle hover state from within the button - we use the hover state of the token field as a whole.
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)anEvent
|
||||
{
|
||||
// Don't toggle hover state from within the button - we use the hover state of the token field as a whole.
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPTokenFieldTokenDisclosureButton : CPPopUpButton
|
||||
{
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
var attributes = [CPButton themeAttributes];
|
||||
|
||||
[attributes setObject:_CGPointMake(15, 5) forKey:@"offset"];
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return "tokenfield-token-disclosure-button";
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
if (self = [self initWithFrame:aFrame pullsDown:YES])
|
||||
{
|
||||
[self setBordered:YES];
|
||||
[super setTitle:@""];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
// skip
|
||||
}
|
||||
|
||||
- (void)synchronizeTitleAndSelectedItem
|
||||
{
|
||||
// skip
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(CPEvent)anEvent
|
||||
{
|
||||
// Don't toggle hover state from within the button - we use the hover state of the token field as a whole.
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)anEvent
|
||||
{
|
||||
// Don't toggle hover state from within the button - we use the hover state of the token field as a whole.
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTokenFieldTokenizingCharacterSetKey = "CPTokenFieldTokenizingCharacterSetKey",
|
||||
CPTokenFieldCompletionDelayKey = "CPTokenFieldCompletionDelay";
|
||||
CPTokenFieldCompletionDelayKey = "CPTokenFieldCompletionDelay",
|
||||
CPTokenFieldButtonTypeKey = "CPTokenFieldButtonTypeKey";
|
||||
|
||||
@implementation CPTokenField (CPCoding)
|
||||
|
||||
@@ -1437,6 +1613,7 @@ var CPTokenFieldTokenizingCharacterSetKey = "CPTokenFieldTokenizingCharacterSe
|
||||
{
|
||||
_tokenizingCharacterSet = [aCoder decodeObjectForKey:CPTokenFieldTokenizingCharacterSetKey] || [[self class] defaultTokenizingCharacterSet];
|
||||
_completionDelay = [aCoder decodeDoubleForKey:CPTokenFieldCompletionDelayKey] || [[self class] defaultCompletionDelay];
|
||||
_buttonType = [aCoder decodeIntForKey:CPTokenFieldButtonTypeKey] || CPTokenFieldDisclosureButtonType;
|
||||
|
||||
[self _init];
|
||||
|
||||
@@ -1453,6 +1630,7 @@ var CPTokenFieldTokenizingCharacterSetKey = "CPTokenFieldTokenizingCharacterSe
|
||||
|
||||
[aCoder encodeInt:_tokenizingCharacterSet forKey:CPTokenFieldTokenizingCharacterSetKey];
|
||||
[aCoder encodeDouble:_completionDelay forKey:CPTokenFieldCompletionDelayKey];
|
||||
[aCoder encodeInt:_buttonType forKey:CPTokenFieldButtonTypeKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -227,6 +227,14 @@ var CPViewFlags = { },
|
||||
CachedNotificationCenter = [CPNotificationCenter defaultCenter];
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if ([aBinding hasPrefix:CPHiddenBinding])
|
||||
return [CPMultipleValueOrBinding class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (void)_setupViewFlags
|
||||
{
|
||||
var theClass = [self class],
|
||||
@@ -2318,13 +2326,11 @@ 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))
|
||||
// If aRect is empty no scrolling required.
|
||||
if (_CGRectIsEmpty(aRect))
|
||||
return NO;
|
||||
|
||||
var enclosingClipView = [self _enclosingClipView];
|
||||
@@ -2333,20 +2339,34 @@ setBoundsOrigin:
|
||||
if (!enclosingClipView)
|
||||
return NO;
|
||||
|
||||
var scrollPoint = _CGPointMakeCopy(visibleRect.origin);
|
||||
var documentView = [enclosingClipView documentView];
|
||||
|
||||
// If the clip view doesn't have a document view, then there isn't much we can do.
|
||||
if (!documentView)
|
||||
return NO;
|
||||
|
||||
// Get the document view visible rect and convert aRect to the document view's coordinate system
|
||||
var documentViewVisibleRect = [documentView visibleRect],
|
||||
rectInDocumentView = [self convertRect:aRect toView:documentView];
|
||||
|
||||
// If already visible then no scrolling required.
|
||||
if (CGRectContainsRect(documentViewVisibleRect, rectInDocumentView))
|
||||
return NO;
|
||||
|
||||
var scrollPoint = _CGPointMakeCopy(documentViewVisibleRect.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 (_CGRectGetMinX(rectInDocumentView) < _CGRectGetMinX(documentViewVisibleRect))
|
||||
scrollPoint.x = _CGRectGetMinX(rectInDocumentView);
|
||||
else if (_CGRectGetMaxX(rectInDocumentView) > _CGRectGetMaxX(documentViewVisibleRect))
|
||||
scrollPoint.x += _CGRectGetMaxX(rectInDocumentView) - _CGRectGetMaxX(documentViewVisibleRect);
|
||||
|
||||
if (_CGRectGetMinY(aRect) <= _CGRectGetMinY(visibleRect))
|
||||
scrollPoint.y = CGRectGetMinY(aRect);
|
||||
else if (_CGRectGetMaxY(aRect) > _CGRectGetMaxY(visibleRect))
|
||||
scrollPoint.y += _CGRectGetMaxY(aRect) - _CGRectGetMaxY(visibleRect);
|
||||
if (_CGRectGetMinY(rectInDocumentView) < _CGRectGetMinY(documentViewVisibleRect))
|
||||
scrollPoint.y = _CGRectGetMinY(rectInDocumentView);
|
||||
else if (_CGRectGetMaxY(rectInDocumentView) > _CGRectGetMaxY(documentViewVisibleRect))
|
||||
scrollPoint.y += _CGRectGetMaxY(rectInDocumentView) - _CGRectGetMaxY(documentViewVisibleRect);
|
||||
|
||||
[enclosingClipView scrollToPoint:CGPointMake(scrollPoint.x, scrollPoint.y)];
|
||||
[enclosingClipView scrollToPoint:scrollPoint];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -258,6 +258,7 @@ CPWebViewAppKitScrollMaxPollCount = 3;
|
||||
};
|
||||
|
||||
win.addEventListener("DOMMouseScroll", scrollEventHandler, false);
|
||||
win.addEventListener("wheel", scrollEventHandler, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -216,6 +216,14 @@ var CPWindowActionMessageKeys = [
|
||||
_CPWindowFrameAnimation _frameAnimation;
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if ([aBinding hasPrefix:CPDisplayPatternTitleBinding])
|
||||
return [CPTitleWithPatternBinding class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithContentRect:_CGRectMakeZero() styleMask:CPTitledWindowMask];
|
||||
@@ -1467,7 +1475,7 @@ CPTexturedBackgroundWindowMask
|
||||
- (void)setRepresentedFilename:(CPString)aFilePath
|
||||
{
|
||||
// FIXME: urls vs filepaths and all.
|
||||
[self setRepresentedURL:aFilePath];
|
||||
[self setRepresentedURL:[CPURL URLWithString:aFilePath]];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1475,7 +1483,7 @@ CPTexturedBackgroundWindowMask
|
||||
*/
|
||||
- (CPString)representedFilename
|
||||
{
|
||||
return _representedURL;
|
||||
return [_representedURL absoluteString];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -3105,6 +3113,14 @@ CPTexturedBackgroundWindowMask
|
||||
[self disableKeyEquivalentForDefaultButton];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forKey:(CPString)aKey
|
||||
{
|
||||
if (aKey === CPDisplayPatternTitleBinding)
|
||||
[self setTitle:aValue || @""];
|
||||
else
|
||||
[super setValue:aValue forKey:aKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var keyViewComparator = function(lhs, rhs, context)
|
||||
|
||||
@@ -309,11 +309,13 @@
|
||||
|
||||
// resize the window
|
||||
var theWindow = [self window],
|
||||
frame = [theWindow frame];
|
||||
frame = [theWindow frame],
|
||||
dy;
|
||||
|
||||
var dy = _CGRectGetHeight([_headView frame]) + _CGRectGetHeight([_dividerView frame]);
|
||||
if (enable)
|
||||
dy = -dy;
|
||||
dy = -(_CGRectGetHeight([_headView frame]) + _CGRectGetHeight([_dividerView frame]));
|
||||
else
|
||||
dy = [self toolbarMaxY] + 1.0;
|
||||
|
||||
var newHeight = _CGRectGetMaxY(frame) + dy,
|
||||
newWidth = _CGRectGetMaxX(frame);
|
||||
@@ -321,9 +323,11 @@
|
||||
frame.size.height += dy;
|
||||
|
||||
[self setFrameSize:_CGSizeMake(newWidth, newHeight)];
|
||||
|
||||
[self tile];
|
||||
[theWindow setFrame:frame display:NO animate:NO];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
@@ -335,22 +339,7 @@
|
||||
|
||||
[_minimizeButton setImage:[self valueForThemeAttribute:@"minimize-image-button"]];
|
||||
[_minimizeButton setAlternateImage:[self valueForThemeAttribute:@"minimize-image-highlighted-button"]];
|
||||
|
||||
if (![_headView isHidden])
|
||||
[_headView setFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), [self toolbarMaxY])];
|
||||
else
|
||||
[_headView setFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), 0)];
|
||||
|
||||
if (![_dividerView isHidden])
|
||||
[_dividerView setFrame:CGRectMake(0.0, CGRectGetMaxY([_headView frame]), CGRectGetWidth(bounds), 1.0)];
|
||||
else
|
||||
[_dividerView setFrame:CGRectMake(0.0, CGRectGetMaxY([_headView frame]), CGRectGetWidth(bounds), 0.0)];
|
||||
|
||||
[_dividerView setBackgroundColor:[self valueForThemeAttribute:@"divider-color"]];
|
||||
|
||||
var y = CGRectGetMaxY([_dividerView frame]);
|
||||
|
||||
[_bodyView setFrame:CGRectMake(0.0, y, CGRectGetWidth(bounds), CGRectGetHeight(bounds) - y)];
|
||||
[_bodyView setBackgroundColor:[self valueForThemeAttribute:@"body-color"]];
|
||||
|
||||
[_headView setNeedsLayout];
|
||||
|
||||
@@ -85,4 +85,5 @@ var _CPCibConnectorSourceKey = @"_CPCibConnectorSourceKey",
|
||||
@end
|
||||
|
||||
// For backwards compatibility.
|
||||
@implementation _CPCibConnector : CPCibConnector { } @end
|
||||
@implementation _CPCibConnector : CPCibConnector
|
||||
@end
|
||||
|
||||
@@ -260,31 +260,9 @@ var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey
|
||||
{
|
||||
_replacementObjects[[_fileOwner UID]] = anOwner;
|
||||
|
||||
var index = 0,
|
||||
count = _connections.length,
|
||||
runtimeAttributeConnectors = [],
|
||||
connection = nil;
|
||||
|
||||
for (; index < count; ++index)
|
||||
for (var i = 0, count = _connections.length; i < count; ++i)
|
||||
{
|
||||
connection = _connections[index];
|
||||
|
||||
if ([connection isKindOfClass:CPCibRuntimeAttributesConnector])
|
||||
// Defer runtime attribute connections until after all other connections are made
|
||||
runtimeAttributeConnectors.push(connection);
|
||||
else
|
||||
{
|
||||
[connection replaceObjects:_replacementObjects];
|
||||
[connection establishConnection];
|
||||
}
|
||||
}
|
||||
|
||||
count = runtimeAttributeConnectors.length;
|
||||
|
||||
for (index = 0; index < count; ++index)
|
||||
{
|
||||
connection = runtimeAttributeConnectors[index];
|
||||
|
||||
var connection = _connections[i];
|
||||
[connection replaceObjects:_replacementObjects];
|
||||
[connection establishConnection];
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
@import <Foundation/CPObjJRuntime.j>
|
||||
|
||||
@import "CGGeometry.j"
|
||||
@import "CPToolbar.j"
|
||||
@import "CPView.j"
|
||||
@import "CPWindow_Constants.j"
|
||||
|
||||
@class CPToolbar
|
||||
@import "_CPToolTip.j"
|
||||
|
||||
|
||||
@implementation _CPCibWindowTemplate : CPObject
|
||||
|
||||
@@ -438,6 +438,7 @@ var resizeTimer = nil;
|
||||
theDocument.addEventListener("touchcancel", touchEventCallback, NO);
|
||||
|
||||
_DOMWindow.addEventListener("DOMMouseScroll", scrollEventCallback, NO);
|
||||
_DOMWindow.addEventListener("wheel", scrollEventCallback, NO);
|
||||
_DOMWindow.addEventListener("mousewheel", scrollEventCallback, NO);
|
||||
|
||||
_DOMWindow.addEventListener("resize", resizeEventCallback, NO);
|
||||
@@ -468,6 +469,7 @@ var resizeTimer = nil;
|
||||
|
||||
//FIXME: does firefox really need a different value?
|
||||
_DOMWindow.removeEventListener("DOMMouseScroll", scrollEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("wheel", scrollEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("mousewheel", scrollEventCallback, NO);
|
||||
|
||||
//_DOMWindow.removeEventListener("beforeunload", this, NO);
|
||||
@@ -898,6 +900,7 @@ var resizeTimer = nil;
|
||||
timestamp = [CPEvent currentTimestamp], // fake event, might as well use current timestamp
|
||||
windowNumber = [[CPApp keyWindow] windowNumber],
|
||||
modifierFlags = CPPlatformActionKeyMask,
|
||||
location = _lastMouseEventLocation || _CGPointMakeZero(),
|
||||
event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags
|
||||
timestamp:timestamp windowNumber:windowNumber context:nil
|
||||
characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode];
|
||||
@@ -999,6 +1002,7 @@ var resizeTimer = nil;
|
||||
aDOMEvent = window.event;
|
||||
|
||||
var location = nil;
|
||||
|
||||
if (CPFeatureIsCompatible(CPJavaScriptMouseWheelValues_8_15))
|
||||
{
|
||||
var x = aDOMEvent._offsetX || 0.0,
|
||||
@@ -1059,7 +1063,7 @@ var resizeTimer = nil;
|
||||
{
|
||||
// Find the scroll delta
|
||||
var deltaX = _DOMScrollingElement.scrollLeft - 150,
|
||||
deltaY = _DOMScrollingElement.scrollTop - 150;
|
||||
deltaY = (_DOMScrollingElement.scrollTop - 150) || (aDOMEvent.deltaY===undefined?0: aDOMEvent.deltaY);
|
||||
|
||||
// If we scroll super with momentum,
|
||||
// there are so many events going off that
|
||||
|
||||
|
After Width: | Height: | Size: 203 B |
|
After Width: | Height: | Size: 509 B |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 698 B |
|
After Width: | Height: | Size: 234 B |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 993 B |
|
After Width: | Height: | Size: 993 B |
|
After Width: | Height: | Size: 993 B |
@@ -1174,6 +1174,32 @@ var themedButtonValues = nil,
|
||||
return token;
|
||||
}
|
||||
|
||||
+ (_CPTokenFieldTokenDisclosureButton)themedTokenFieldDisclosureButton
|
||||
{
|
||||
var button = [[_CPTokenFieldTokenDisclosureButton alloc] initWithFrame:CGRectMake(0, 0, 9, 9)],
|
||||
|
||||
arrowImage = PatternColor("token-disclosure.png", 7.0, 6.0),
|
||||
arrowImageHiglighted = PatternColor("token-disclosure-highlighted.png", 7.0, 6.0),
|
||||
|
||||
themeValues =
|
||||
[
|
||||
[@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateNormal],
|
||||
|
||||
[@"bezel-color", nil, CPThemeStateBordered],
|
||||
[@"bezel-color", arrowImage, CPThemeStateBordered | CPThemeStateHovered],
|
||||
[@"bezel-color", arrowImageHiglighted, CPThemeStateBordered | CPThemeStateHovered | CPThemeStateHighlighted],
|
||||
|
||||
[@"min-size", CGSizeMake(7.0, 6.0)],
|
||||
[@"max-size", CGSizeMake(7.0, 6.0)],
|
||||
|
||||
[@"offset", CGPointMake(17, 7)]
|
||||
];
|
||||
|
||||
[self registerThemeValues:themeValues forView:button];
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
+ (_CPTokenFieldTokenCloseButton)themedTokenFieldTokenCloseButton
|
||||
{
|
||||
var button = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:CGRectMake(0, 0, 9, 9)],
|
||||
@@ -2414,6 +2440,14 @@ var themedButtonValues = nil,
|
||||
[@"window-standard-bottom-right.png", 2.0, 2.0]
|
||||
]),
|
||||
|
||||
dividerColor = PatternColor(
|
||||
"window-standard-divider{position}.png",
|
||||
{
|
||||
width: 1.0,
|
||||
height: 1.0,
|
||||
orientation: PatternIsHorizontal
|
||||
}),
|
||||
|
||||
closeButtonImage = PatternImage(@"window-standard-close-button.png", 16, 16),
|
||||
closeButtonImageHighlighted = PatternImage(@"window-standard-button-highlighted.png",16, 16),
|
||||
unsavedButtonImage = PatternImage(@"window-standard-button-unsaved.png",16, 16),
|
||||
@@ -2441,7 +2475,7 @@ var themedButtonValues = nil,
|
||||
[@"title-vertical-alignment", CPCenterVerticalTextAlignment],
|
||||
[@"title-bar-height", 31],
|
||||
|
||||
[@"divider-color", [CPColor colorWithHexString:@"979797"]],
|
||||
[@"divider-color", dividerColor],
|
||||
[@"body-color", bezelColor],
|
||||
[@"title-bar-height", 31],
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 203 B |
|
After Width: | Height: | Size: 509 B |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 698 B |
|
After Width: | Height: | Size: 234 B |
|
Before Width: | Height: | Size: 98 B After Width: | Height: | Size: 1021 B |
|
Before Width: | Height: | Size: 113 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 120 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 87 B After Width: | Height: | Size: 1016 B |
|
Before Width: | Height: | Size: 84 B After Width: | Height: | Size: 1013 B |
|
Before Width: | Height: | Size: 84 B After Width: | Height: | Size: 1013 B |
|
Before Width: | Height: | Size: 562 B After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 142 B After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 87 B |
|
After Width: | Height: | Size: 87 B |
|
After Width: | Height: | Size: 156 B |
|
After Width: | Height: | Size: 400 B |
|
After Width: | Height: | Size: 157 B |
|
After Width: | Height: | Size: 545 B |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 992 B |
|
After Width: | Height: | Size: 993 B |
|
After Width: | Height: | Size: 993 B |
@@ -269,7 +269,7 @@ var themedButtonValues = nil,
|
||||
{
|
||||
var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 25.0) pullsDown:YES],
|
||||
color = PatternColor(
|
||||
"popup-bezel{state}{position}.png",
|
||||
"pulldown-bezel{state}{position}.png",
|
||||
{
|
||||
states: ["", "disabled"],
|
||||
width: 3.0,
|
||||
@@ -709,7 +709,7 @@ var themedButtonValues = nil,
|
||||
[@"text-color", textDisabledColor, CPThemeStateDisabled],
|
||||
|
||||
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBezeled],
|
||||
[@"content-inset", CGInsetMake(1.0, 24.0, 2.0, 16.0), CPThemeStateBezeled],
|
||||
[@"content-inset", CGInsetMake(1.0, 20.0, 2.0, 20.0), CPThemeStateBezeled],
|
||||
|
||||
// Minimum height == maximum height since tokens are fixed height.
|
||||
[@"min-size", CGSizeMake(0.0, 19.0)],
|
||||
@@ -723,6 +723,32 @@ var themedButtonValues = nil,
|
||||
return token;
|
||||
}
|
||||
|
||||
+ (_CPTokenFieldTokenDisclosureButton)themedTokenFieldDisclosureButton
|
||||
{
|
||||
var button = [[_CPTokenFieldTokenDisclosureButton alloc] initWithFrame:CGRectMake(0, 0, 9, 9)],
|
||||
|
||||
arrowImage = PatternColor("token-disclosure.png", 7.0, 6.0),
|
||||
arrowImageHiglighted = PatternColor("token-disclosure-highlighted.png", 7.0, 6.0),
|
||||
|
||||
themeValues =
|
||||
[
|
||||
[@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateNormal],
|
||||
|
||||
[@"bezel-color", nil, CPThemeStateBordered],
|
||||
[@"bezel-color", arrowImage, CPThemeStateBordered | CPThemeStateHovered],
|
||||
[@"bezel-color", arrowImageHiglighted, CPThemeStateBordered | CPThemeStateHovered | CPThemeStateHighlighted],
|
||||
|
||||
[@"min-size", CGSizeMake(7.0, 6.0)],
|
||||
[@"max-size", CGSizeMake(7.0, 6.0)],
|
||||
|
||||
[@"offset", CGPointMake(16, 7)]
|
||||
];
|
||||
|
||||
[self registerThemeValues:themeValues forView:button];
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
+ (_CPTokenFieldTokenCloseButton)themedTokenFieldTokenCloseButton
|
||||
{
|
||||
var button = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:CGRectMake(0, 0, 9, 9)],
|
||||
@@ -732,16 +758,17 @@ var themedButtonValues = nil,
|
||||
|
||||
themeValues =
|
||||
[
|
||||
[@"bezel-color", bezelColor, CPThemeStateBordered],
|
||||
[@"bezel-color", bezelHighlightedColor, CPThemeStateBordered | CPThemeStateHighlighted],
|
||||
[@"bezel-color", bezelColor, CPThemeStateBordered | CPThemeStateHovered],
|
||||
[@"bezel-color", [bezelColor colorWithAlphaComponent:0], CPThemeStateBordered | CPThemeStateDisabled],
|
||||
[@"bezel-color", bezelHighlightedColor, CPThemeStateBordered | CPThemeStateHighlighted],
|
||||
|
||||
[@"min-size", CGSizeMake(8.0, 8.0)],
|
||||
[@"max-size", CGSizeMake(8.0, 8.0)],
|
||||
|
||||
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered],
|
||||
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered | CPThemeStateHighlighted],
|
||||
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered],
|
||||
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered | CPThemeStateHighlighted],
|
||||
|
||||
[@"offset", CGPointMake(17, 6), CPThemeStateBordered]
|
||||
[@"offset", CGPointMake(16, 6), CPThemeStateBordered]
|
||||
];
|
||||
|
||||
[self registerThemeValues:themeValues forView:button];
|
||||
@@ -1523,14 +1550,14 @@ var themedButtonValues = nil,
|
||||
|
||||
+ (CPProgressIndicator)themedBarProgressIndicator
|
||||
{
|
||||
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0,0,75,25)];
|
||||
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 75, 20)];
|
||||
[progressBar setDoubleValue:30];
|
||||
|
||||
var bezelColor = PatternColor(
|
||||
"progress-indicator-bezel-border{position}.png",
|
||||
{
|
||||
width: 1.0,
|
||||
height: 25.0,
|
||||
height: 20.0,
|
||||
orientation: PatternIsHorizontal
|
||||
}),
|
||||
|
||||
@@ -1538,14 +1565,15 @@ var themedButtonValues = nil,
|
||||
"progress-indicator-bar{position}.png",
|
||||
{
|
||||
width: 1.0,
|
||||
height: 25.0,
|
||||
height: 20.0,
|
||||
orientation: PatternIsHorizontal
|
||||
});
|
||||
|
||||
themedProgressIndicator =
|
||||
[
|
||||
[@"bezel-color", bezelColor],
|
||||
[@"bar-color", barColor]
|
||||
[@"bar-color", barColor],
|
||||
[@"default-height", 20]
|
||||
];
|
||||
|
||||
[self registerThemeValues:themedProgressIndicator forView:progressBar];
|
||||
@@ -1555,7 +1583,7 @@ var themedButtonValues = nil,
|
||||
|
||||
+ (CPProgressIndicator)themedIndeterminateBarProgressIndicator
|
||||
{
|
||||
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0,0,75,25)];
|
||||
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 75, 20)];
|
||||
|
||||
[progressBar setIndeterminate:YES];
|
||||
|
||||
@@ -1563,7 +1591,7 @@ var themedButtonValues = nil,
|
||||
"progress-indicator-bezel-border{position}.png",
|
||||
{
|
||||
width: 1.0,
|
||||
height: 25.0,
|
||||
height: 20.0,
|
||||
orientation: PatternIsHorizontal
|
||||
}),
|
||||
|
||||
@@ -1571,7 +1599,7 @@ var themedButtonValues = nil,
|
||||
"progress-indicator-indeterminate-bar{position}.png",
|
||||
{
|
||||
width: 1.0,
|
||||
height: 25.0,
|
||||
height: 20.0,
|
||||
centerWidth: 20.0,
|
||||
orientation: PatternIsHorizontal
|
||||
});
|
||||
@@ -1579,7 +1607,8 @@ var themedButtonValues = nil,
|
||||
themedIndeterminateProgressIndicator =
|
||||
[
|
||||
[@"bezel-color", bezelColor],
|
||||
[@"indeterminate-bar-color", barColor]
|
||||
[@"indeterminate-bar-color", barColor],
|
||||
[@"default-height", 20]
|
||||
];
|
||||
|
||||
[self registerThemeValues:themedIndeterminateProgressIndicator forView:progressBar];
|
||||
@@ -1589,7 +1618,7 @@ var themedButtonValues = nil,
|
||||
|
||||
+ (CPProgressIndicator)themedSpinningProgressIndicator
|
||||
{
|
||||
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0,0,64,64)];
|
||||
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
|
||||
[progressBar setStyle:CPProgressIndicatorSpinningStyle];
|
||||
|
||||
var spinningMini = PatternColor(@"progress-indicator-spinning-style-mini.gif", 16.0, 16.0),
|
||||
@@ -1610,7 +1639,7 @@ var themedButtonValues = nil,
|
||||
|
||||
+ (CPBox)themedBox
|
||||
{
|
||||
var box = [[CPBox alloc] initWithFrame:CGRectMake(0,0,100,100)],
|
||||
var box = [[CPBox alloc] initWithFrame:CGRectMake(0, 0, 100, 100)],
|
||||
|
||||
themeValues =
|
||||
[
|
||||
@@ -1631,7 +1660,7 @@ var themedButtonValues = nil,
|
||||
|
||||
+ (CPLevelIndicator)themedLevelIndicator
|
||||
{
|
||||
var levelIndicator = [[CPLevelIndicator alloc] initWithFrame:CGRectMake(0,0,100,100)],
|
||||
var levelIndicator = [[CPLevelIndicator alloc] initWithFrame:CGRectMake(0, 0, 100, 100)],
|
||||
|
||||
bezelColor = PatternColor(
|
||||
"level-indicator-bezel{position}.png",
|
||||
@@ -1842,7 +1871,16 @@ var themedButtonValues = nil,
|
||||
{
|
||||
positions: "full",
|
||||
width: 5.0,
|
||||
height: 1.0
|
||||
height: 1.0,
|
||||
bottomHeight:5.0
|
||||
}),
|
||||
|
||||
dividerColor = PatternColor(
|
||||
"window-standard-divider{position}.png",
|
||||
{
|
||||
width: 1.0,
|
||||
height: 1.0,
|
||||
orientation: PatternIsHorizontal
|
||||
}),
|
||||
|
||||
closeButtonImage = PatternImage(@"window-standard-close-button.png", 16, 16),
|
||||
@@ -1877,7 +1915,7 @@ var themedButtonValues = nil,
|
||||
[@"title-vertical-alignment", CPCenterVerticalTextAlignment],
|
||||
[@"title-bar-height", 31],
|
||||
|
||||
[@"divider-color", [CPColor colorWithHexString:@"858585"]],
|
||||
[@"divider-color", dividerColor],
|
||||
[@"body-color", bezelColor],
|
||||
[@"title-bar-height", 31],
|
||||
|
||||
|
||||
@@ -115,8 +115,13 @@ var _CPAutocompleteMenuMaximumHeight = 307;
|
||||
|
||||
- (void)setIndexOfSelectedItem:(int)anIndex
|
||||
{
|
||||
[tableView selectRowIndexes:[CPIndexSet indexSetWithIndex:anIndex] byExtendingSelection:NO];
|
||||
[tableView scrollRowToVisible:anIndex];
|
||||
if (anIndex == CPNotFound)
|
||||
[tableView selectRowIndexes:[CPIndexSet indexSet] byExtendingSelection:NO];
|
||||
else
|
||||
{
|
||||
[tableView selectRowIndexes:[CPIndexSet indexSetWithIndex:anIndex] byExtendingSelection:NO];
|
||||
[tableView scrollRowToVisible:anIndex];
|
||||
}
|
||||
}
|
||||
|
||||
- (int)indexOfSelectedItem
|
||||
@@ -198,7 +203,7 @@ var _CPAutocompleteMenuMaximumHeight = 307;
|
||||
[self setIndexOfSelectedItem:indexOfSelectedItem];
|
||||
|
||||
[textField setThemeState:CPThemeStateAutocompleting];
|
||||
[_menuWindow orderFront:self];
|
||||
[[textField window] addChildWindow:_menuWindow ordered:CPWindowAbove];
|
||||
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CPButton.j"
|
||||
@import "CPMenu.j"
|
||||
@import "CPPanel.j"
|
||||
|
||||
// Use forward declaration because this file is imported by CPPopover
|
||||
@@ -189,7 +190,8 @@ var _CPPopoverWindow_shouldClose_ = 1 << 0,
|
||||
originRight = _CGPointCreateCopy(platformOrigin),
|
||||
originTop = _CGPointCreateCopy(platformOrigin),
|
||||
originBottom = _CGPointCreateCopy(platformOrigin),
|
||||
frameSize = [self frame].size;
|
||||
frameSize = [self frame].size,
|
||||
menuBarHeight = [CPMenu menuBarVisible] ? [CPMenu menuBarHeight] : 0;
|
||||
|
||||
// CPMaxXEdge
|
||||
originRight.x += platformRect.size.width;
|
||||
@@ -215,12 +217,15 @@ var _CPPopoverWindow_shouldClose_ = 1 << 0,
|
||||
case CPMaxXEdge:
|
||||
requestedOrigin = originRight;
|
||||
break;
|
||||
|
||||
case CPMinXEdge:
|
||||
requestedOrigin = originLeft;
|
||||
break;
|
||||
|
||||
case CPMinYEdge:
|
||||
requestedOrigin = originTop;
|
||||
break;
|
||||
|
||||
case CPMaxYEdge:
|
||||
requestedOrigin = originBottom;
|
||||
break;
|
||||
@@ -252,8 +257,8 @@ var _CPPopoverWindow_shouldClose_ = 1 << 0,
|
||||
|
||||
if (origin.y < 0)
|
||||
{
|
||||
[_windowView setArrowOffsetY:origin.y];
|
||||
origin.y = 0;
|
||||
[_windowView setArrowOffsetY:origin.y - menuBarHeight];
|
||||
origin.y = menuBarHeight;
|
||||
}
|
||||
|
||||
if (origin.y + frameSize.height > nativeRect.size.height)
|
||||
@@ -456,6 +461,13 @@ var _CPPopoverWindow_shouldClose_ = 1 << 0,
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.removeEventListener(CPBrowserStyleProperty('transitionend'), transitionCompleteFunction, YES);
|
||||
|
||||
// Make sure to clear these properties when the animation is done. Without this,
|
||||
// the window becomes blurry in Chrome, presumably because the browser composits
|
||||
// a layer with a transform differently even when it's an identity transform.
|
||||
[self setCSS3Property:@"Transform" value:nil];
|
||||
[self setCSS3Property:@"TransformOrigin" value:nil];
|
||||
[self setCSS3Property:@"Transition" value:nil];
|
||||
#endif
|
||||
if (_implementedDelegateMethods & _CPPopoverWindow_didShow_)
|
||||
[_delegate popoverWindowDidShow:self];
|
||||
|
||||
@@ -431,7 +431,7 @@
|
||||
|
||||
- (id)valueForKey:(CPString)aKey
|
||||
{
|
||||
if (aKey.indexOf("@") === 0)
|
||||
if (aKey.charAt(0) === "@")
|
||||
{
|
||||
if (aKey.indexOf(".") !== -1)
|
||||
[CPException raise:CPInvalidArgumentException reason:"called valueForKey: on an array with a complex key (" + aKey + "). use valueForKeyPath:"];
|
||||
@@ -528,7 +528,7 @@
|
||||
Raises an exception.
|
||||
|
||||
CPArray objects are not observable, so this method raises an exception when invoked on an CPArray object.
|
||||
Instead of observing a array, observe the ordered to-many relationship for which the array is the collection of related objects.
|
||||
Instead of observing an array, observe the ordered to-many relationship for which the array is the collection of related objects.
|
||||
*/
|
||||
- (void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)anOptions context:(id)aContext
|
||||
{
|
||||
@@ -539,7 +539,7 @@
|
||||
Raises an exception.
|
||||
|
||||
CPArray objects are not observable, so this method raises an exception when invoked on an CPArray object.
|
||||
Instead of observing a array, observe the ordered to-many relationship for which the array is the collection of related objects.
|
||||
Instead of observing an array, observe the ordered to-many relationship for which the array is the collection of related objects.
|
||||
*/
|
||||
- (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
|
||||
@@ -120,6 +120,29 @@ var concat = Array.prototype.concat,
|
||||
return self[anIndex];
|
||||
}
|
||||
|
||||
- (CPArray)objectsAtIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
if ([indexes lastIndex] >= self.length)
|
||||
[CPException raise:CPRangeException reason:_cmd + " indexes out of bounds"];
|
||||
|
||||
var ranges = indexes._ranges,
|
||||
count = ranges.length,
|
||||
result = [],
|
||||
i = 0;
|
||||
|
||||
for (; i < count; i++)
|
||||
{
|
||||
var range = ranges[i],
|
||||
loc = range.location,
|
||||
len = range.length,
|
||||
subArray = self.slice(loc, loc + len);
|
||||
|
||||
result.splice.apply(result, [result.length, 0].concat(subArray));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (CPUInteger)indexOfObject:(id)anObject inRange:(CPRange)aRange
|
||||
{
|
||||
// Only use isEqual: if our object is a CPObject.
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* CPByteCountFormatter.j
|
||||
* Foundation
|
||||
*
|
||||
* Created by Aparajita Fishman.
|
||||
* Copyright 2013, Cappuccino Foundation.
|
||||
*
|
||||
* 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 "CPNumberFormatter.j"
|
||||
@import "CPString.j"
|
||||
|
||||
|
||||
// Allowed units
|
||||
CPByteCountFormatterUseDefault = 0;
|
||||
CPByteCountFormatterUseBytes = 1 << 0;
|
||||
CPByteCountFormatterUseKB = 1 << 1;
|
||||
CPByteCountFormatterUseMB = 1 << 2;
|
||||
CPByteCountFormatterUseGB = 1 << 3;
|
||||
CPByteCountFormatterUseTB = 1 << 4;
|
||||
CPByteCountFormatterUsePB = 1 << 5;
|
||||
CPByteCountFormatterUseAll = 0xFFFF;
|
||||
|
||||
// Note: The Cocoa documentation says File is binary, but in practice it's decimal
|
||||
CPByteCountFormatterCountStyleFile = 0;
|
||||
CPByteCountFormatterCountStyleMemory = 1;
|
||||
CPByteCountFormatterCountStyleDecimal = 2;
|
||||
CPByteCountFormatterCountStyleBinary = 3;
|
||||
|
||||
var CPByteCountFormatterUnits = [ @"bytes", @"KB", @"MB", @"GB", @"TB", @"PB" ];
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup foundation
|
||||
@class CPByteCountFormatter
|
||||
|
||||
A complete implementation of NSByteCountFormatter. See
|
||||
https://developer.apple.com/library/mac/#documentation/Foundation/Reference/NSByteCountFormatter_Class/Reference/Reference.html
|
||||
*/
|
||||
@implementation CPByteCountFormatter : CPFormatter
|
||||
{
|
||||
int _countStyle;
|
||||
BOOL _allowsNonnumericFormatting;
|
||||
BOOL _includesActualByteCount;
|
||||
BOOL _includesCount;
|
||||
BOOL _includesUnit;
|
||||
BOOL _adaptive;
|
||||
BOOL _zeroPadsFractionDigits;
|
||||
int _allowedUnits;
|
||||
CPNumberFormatter _numberFormatter;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_adaptive = YES;
|
||||
_allowedUnits = CPByteCountFormatterUseDefault;
|
||||
_allowsNonnumericFormatting = YES;
|
||||
_countStyle = CPByteCountFormatterCountStyleFile;
|
||||
_includesActualByteCount = NO;
|
||||
_includesCount = YES;
|
||||
_includesUnit = YES;
|
||||
_zeroPadsFractionDigits = NO;
|
||||
_numberFormatter = [CPNumberFormatter new];
|
||||
[_numberFormatter setNumberStyle:CPNumberFormatterDecimalStyle];
|
||||
[_numberFormatter setMinimumFractionDigits:0];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*! @name Creating Strings from Byte Count */
|
||||
|
||||
+ (CPString)stringFromByteCount:(int)byteCount countStyle:(int)countStyle
|
||||
{
|
||||
var formatter = [CPByteCountFormatter new];
|
||||
|
||||
[formatter setCountStyle:countStyle];
|
||||
|
||||
return [formatter stringFromByteCount:byteCount];
|
||||
}
|
||||
|
||||
- (CPString)stringFromByteCount:(int)byteCount
|
||||
{
|
||||
var divisor,
|
||||
exponent = 0,
|
||||
unitIndex = ((_allowedUnits === 0) || (_allowedUnits & CPByteCountFormatterUseBytes)) ? 0 : -1,
|
||||
bytes = byteCount,
|
||||
unitBytes = bytes,
|
||||
unitCount = [CPByteCountFormatterUnits count];
|
||||
|
||||
if (_countStyle === CPByteCountFormatterCountStyleFile ||
|
||||
_countStyle === CPByteCountFormatterCountStyleDecimal)
|
||||
divisor = 1000;
|
||||
else
|
||||
divisor = 1024;
|
||||
|
||||
while ((bytes >= divisor) && (exponent < unitCount))
|
||||
{
|
||||
bytes /= divisor;
|
||||
++exponent;
|
||||
|
||||
// If there is a valid unit for this exponent,
|
||||
// update the unit we will use and the byte count for that unit
|
||||
if (_allowedUnits === 0 || (_allowedUnits & (1 << exponent)))
|
||||
{
|
||||
unitIndex = exponent;
|
||||
unitBytes = bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
If no allowed unit was found before bytes < divisor,
|
||||
keep dividing until we find an allowed unit. We can skip
|
||||
bytes, if that is allowed unit, unitIndex will be >= 0.
|
||||
*/
|
||||
if (unitIndex === -1)
|
||||
for (var i = 1; i < unitCount; ++i)
|
||||
{
|
||||
unitBytes /= divisor;
|
||||
|
||||
if ((_allowedUnits === 0) || (_allowedUnits & (1 << i)))
|
||||
{
|
||||
unitIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var minDigits = 0,
|
||||
maxDigits = CPDecimalNoScale;
|
||||
|
||||
// Fractional units get as many digits as they need
|
||||
if (unitBytes >= 1.0)
|
||||
{
|
||||
if (_adaptive)
|
||||
{
|
||||
// 0 fraction digits for bytes and K, 1 fraction digit for MB, 2 digits for GB and above
|
||||
var digits;
|
||||
|
||||
if (exponent <= 1)
|
||||
digits = 0;
|
||||
else if (exponent == 2)
|
||||
digits = 1;
|
||||
else
|
||||
digits = 2;
|
||||
|
||||
maxDigits = digits;
|
||||
|
||||
if (_zeroPadsFractionDigits)
|
||||
minDigits = digits;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_zeroPadsFractionDigits)
|
||||
minDigits = 2;
|
||||
|
||||
if (bytes >= 1)
|
||||
maxDigits = 2;
|
||||
}
|
||||
}
|
||||
|
||||
[_numberFormatter setMinimumFractionDigits:minDigits];
|
||||
[_numberFormatter setMaximumFractionDigits:maxDigits];
|
||||
|
||||
var parts = [];
|
||||
|
||||
if (_includesCount)
|
||||
{
|
||||
if (_allowsNonnumericFormatting && bytes === 0)
|
||||
[parts addObject:@"Zero"];
|
||||
else
|
||||
[parts addObject:[_numberFormatter stringFromNumber:unitBytes]];
|
||||
}
|
||||
|
||||
if (_includesUnit)
|
||||
[parts addObject:CPByteCountFormatterUnits[unitIndex]];
|
||||
|
||||
if ((unitIndex > 0) && _includesCount && _includesUnit && _includesActualByteCount)
|
||||
{
|
||||
[_numberFormatter setMaximumFractionDigits:0];
|
||||
[parts addObject:[CPString stringWithFormat:@"(%s bytes)", [_numberFormatter stringFromNumber:byteCount]]];
|
||||
}
|
||||
|
||||
var result = [parts componentsJoinedByString:@" "];
|
||||
|
||||
if (byteCount === 1)
|
||||
return [result stringByReplacingOccurrencesOfString:@"bytes" withString:@"byte"];
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
Cocoa returns nil if anObject is not a number.
|
||||
*/
|
||||
- (CPString)stringForObjectValue:(id)anObject
|
||||
{
|
||||
if ([anObject isKindOfClass:CPNumber])
|
||||
return [self stringFromByteCount:anObject];
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)getObjectValue:(id)anObject forString:(CPString)aString errorDescription:(CPString)anError
|
||||
{
|
||||
// Not implemented
|
||||
return NO;
|
||||
}
|
||||
|
||||
/*! @name Setting Formatting Styles */
|
||||
|
||||
- (int)countStyle
|
||||
{
|
||||
return _countStyle;
|
||||
}
|
||||
|
||||
- (void)setCountStyle:(int)style
|
||||
{
|
||||
_countStyle = style;
|
||||
}
|
||||
|
||||
- (BOOL)allowsNonnumericFormatting
|
||||
{
|
||||
return _allowsNonnumericFormatting;
|
||||
}
|
||||
|
||||
- (void)setAllowsNonnumericFormatting:(BOOL)shouldAllowNonnumericFormatting
|
||||
{
|
||||
_allowsNonnumericFormatting = shouldAllowNonnumericFormatting;
|
||||
}
|
||||
|
||||
- (BOOL)includesActualByteCount
|
||||
{
|
||||
return _includesActualByteCount;
|
||||
}
|
||||
|
||||
- (void)setIncludesActualByteCount:(BOOL)shouldIncludeActualByteCount
|
||||
{
|
||||
_includesActualByteCount = shouldIncludeActualByteCount;
|
||||
}
|
||||
|
||||
- (BOOL)isAdaptive
|
||||
{
|
||||
return _adaptive;
|
||||
}
|
||||
|
||||
- (void)setAdaptive:(BOOL)shouldBeAdaptive
|
||||
{
|
||||
_adaptive = shouldBeAdaptive;
|
||||
}
|
||||
|
||||
- (int)allowedUnits
|
||||
{
|
||||
return _allowedUnits;
|
||||
}
|
||||
|
||||
- (void)setAllowedUnits:(int)allowed
|
||||
{
|
||||
// Note: CPByteCountFormatterUseDefault is equivalent to UseAll
|
||||
_allowedUnits = allowed;
|
||||
}
|
||||
|
||||
- (BOOL)includesCount
|
||||
{
|
||||
return _includesCount;
|
||||
}
|
||||
|
||||
- (void)setIncludesCount:(BOOL)shouldIncludeCount
|
||||
{
|
||||
_includesCount = shouldIncludeCount;
|
||||
}
|
||||
|
||||
- (BOOL)includesUnit
|
||||
{
|
||||
return _includesUnit;
|
||||
}
|
||||
|
||||
- (void)setIncludesUnit:(BOOL)shouldIncludeUnit
|
||||
{
|
||||
_includesUnit = shouldIncludeUnit;
|
||||
}
|
||||
|
||||
- (BOOL)zeroPadsFractionDigits
|
||||
{
|
||||
return _zeroPadsFractionDigits;
|
||||
}
|
||||
|
||||
- (void)setZeroPadsFractionDigits:(BOOL)shouldZeroPad
|
||||
{
|
||||
_zeroPadsFractionDigits = shouldZeroPad;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPByteCountFormatterCountStyleKey = @"CPByteCountFormatterCountStyleKey",
|
||||
CPByteCountFormatterAllowsNonnumericFormattingKey = @"CPByteCountFormatterAllowsNonnumericFormattingKey",
|
||||
CPByteCountFormatterIncludesActualByteCountKey = @"CPByteCountFormatterIncludesActualByteCountKey",
|
||||
CPByteCountFormatterIncludesCountKey = @"CPByteCountFormatterIncludesCountKey",
|
||||
CPByteCountFormatterIncludesUnitKey = @"CPByteCountFormatterIncludesUnitKey",
|
||||
CPByteCountFormatterAdaptiveKey = @"CPByteCountFormatterAdaptiveKey",
|
||||
CPByteCountFormatterZeroPadsFractionDigitsKey = @"CPByteCountFormatterZeroPadsFractionDigitsKey",
|
||||
CPByteCountFormatterAllowedUnitsKey = @"CPByteCountFormatterAllowedUnitsKey";
|
||||
|
||||
@implementation CPByteCountFormatter (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_countStyle = [aCoder decodeIntForKey:CPByteCountFormatterCountStyleKey];
|
||||
_allowsNonnumericFormatting = [aCoder decodeBoolForKey:CPByteCountFormatterAllowsNonnumericFormattingKey];
|
||||
_includesActualByteCount = [aCoder decodeBoolForKey:CPByteCountFormatterIncludesActualByteCountKey];
|
||||
_includesCount = [aCoder decodeBoolForKey:CPByteCountFormatterIncludesCountKey];
|
||||
_includesUnit = [aCoder decodeBoolForKey:CPByteCountFormatterIncludesUnitKey];
|
||||
_adaptive = [aCoder decodeBoolForKey:CPByteCountFormatterAdaptiveKey];
|
||||
_zeroPadsFractionDigits = [aCoder decodeBoolForKey:CPByteCountFormatterZeroPadsFractionDigitsKey];
|
||||
_allowedUnits = [aCoder decodeIntForKey:CPByteCountFormatterAllowedUnitsKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeInt:_countStyle forKey:CPByteCountFormatterCountStyleKey];
|
||||
[aCoder encodeBool:_allowsNonnumericFormatting forKey:CPByteCountFormatterAllowsNonnumericFormattingKey];
|
||||
[aCoder encodeBool:_includesActualByteCount forKey:CPByteCountFormatterIncludesActualByteCountKey];
|
||||
[aCoder encodeBool:_includesCount forKey:CPByteCountFormatterIncludesCountKey];
|
||||
[aCoder encodeBool:_includesUnit forKey:CPByteCountFormatterIncludesUnitKey];
|
||||
[aCoder encodeBool:_adaptive forKey:CPByteCountFormatterAdaptiveKey];
|
||||
[aCoder encodeBool:_zeroPadsFractionDigits forKey:CPByteCountFormatterZeroPadsFractionDigitsKey];
|
||||
[aCoder encodeInt:_allowedUnits forKey:CPByteCountFormatterAllowedUnitsKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -65,6 +65,7 @@ CPDateFormatterFullStyle = 4;
|
||||
case CPDateFormatterShortStyle:
|
||||
var format = "d/m/Y";
|
||||
return aDate.dateFormat(format);
|
||||
|
||||
default:
|
||||
return [aDate description];
|
||||
}
|
||||
@@ -74,11 +75,13 @@ CPDateFormatterFullStyle = 4;
|
||||
{
|
||||
if (!aString)
|
||||
return nil;
|
||||
|
||||
switch (_dateStyle)
|
||||
{
|
||||
case CPDateFormatterShortStyle:
|
||||
var format = "d/m/Y";
|
||||
return Date.parseDate(aString, format);
|
||||
|
||||
default:
|
||||
return Date.parseDate(aString);
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ function CPDecimalMakeWithString(string, locale)
|
||||
// If yes simply add '?' after integer part group, i.e. ([+\-]?)((?:0|[1-9]\d*)?)
|
||||
// Note: now it accept .01 style.
|
||||
var matches = string.match(/^([+\-]?)((?:0|[1-9]\d*)?)(?:\.(\d*))?(?:[eE]([+\-]?)(\d+))?$/);
|
||||
|
||||
if (!matches)
|
||||
return CPDecimalMakeNaN();
|
||||
|
||||
@@ -127,10 +128,12 @@ function CPDecimalMakeWithString(string, locale)
|
||||
exp = matches[5];
|
||||
|
||||
var isNegative = NO;
|
||||
|
||||
if (ds && ds === "-")
|
||||
isNegative = YES;
|
||||
|
||||
var exponent = 0;
|
||||
|
||||
if (exp)
|
||||
exponent = parseInt(exp) * ((es && es === "-")?-1:1);
|
||||
|
||||
@@ -138,6 +141,7 @@ function CPDecimalMakeWithString(string, locale)
|
||||
exponent -= decpart.length;
|
||||
|
||||
var inputlength = (intpart?intpart.length:0) + (decpart?decpart.length:0);
|
||||
|
||||
if (inputlength > CPDecimalMaxDigits)
|
||||
{
|
||||
// input is too long, increase exponent and truncate
|
||||
@@ -154,17 +158,21 @@ function CPDecimalMakeWithString(string, locale)
|
||||
// Representation internally starts at most significant digit
|
||||
var m = [],
|
||||
i = 0;
|
||||
|
||||
for (; i < (intpart?intpart.length:0); i++)
|
||||
{
|
||||
if (i >= CPDecimalMaxDigits)
|
||||
break; // truncate
|
||||
Array.prototype.push.call(m, parseInt(intpart.charAt(i)));
|
||||
}
|
||||
|
||||
var j = 0;
|
||||
|
||||
for (; j < (decpart?decpart.length:0); j++)
|
||||
{
|
||||
if ((i + j) >= CPDecimalMaxDigits)
|
||||
break; // truncate
|
||||
|
||||
Array.prototype.push.call(m, parseInt(decpart.charAt(j)));
|
||||
}
|
||||
|
||||
@@ -199,7 +207,7 @@ function CPDecimalMakeWithParts(mantissa, exponent)
|
||||
return CPDecimalMakeNaN();
|
||||
|
||||
// remaining digits are disposed of via truncation
|
||||
while ((mantissa > 0) && (m.length < CPDecimalMaxDigits)) // count selector here could be optimised away
|
||||
while ((mantissa > 0) && (m.length < CPDecimalMaxDigits))
|
||||
{
|
||||
Array.prototype.unshift.call(m, parseInt(mantissa % 10));
|
||||
mantissa = FLOOR(mantissa / 10);
|
||||
@@ -294,6 +302,7 @@ function CPDecimalIsZero(dcm)
|
||||
function CPDecimalIsOne(dcm)
|
||||
{
|
||||
CPDecimalCompact(dcm);
|
||||
|
||||
// exponent doesn't matter as long as mantissa = 0
|
||||
if (!dcm._isNaN)
|
||||
{
|
||||
@@ -304,7 +313,7 @@ function CPDecimalIsOne(dcm)
|
||||
}
|
||||
|
||||
//private method to copy attribute values
|
||||
function _CPDecimalSet(t,s)
|
||||
function _CPDecimalSet(t, s)
|
||||
{
|
||||
// should all be [x copy] ?
|
||||
t._exponent = s._exponent;
|
||||
@@ -386,16 +395,19 @@ function CPDecimalCompare(leftOperand, rightOperand)
|
||||
s1 = leftOperand._exponent + leftOperand._mantissa.length,
|
||||
s2 = rightOperand._exponent + rightOperand._mantissa.length;
|
||||
|
||||
if (leftIsZero || s1 < s2)
|
||||
if (leftIsZero && rightIsZero)
|
||||
return CPOrderedSame;
|
||||
|
||||
if (leftIsZero || (s1 < s2 && !rightIsZero))
|
||||
{
|
||||
if (rightOperand._isNegative)
|
||||
return CPOrderedDescending;
|
||||
else
|
||||
return CPOrderedAscending;
|
||||
}
|
||||
if (rightIsZero || s1 > s2)
|
||||
if (rightIsZero || (s1 > s2 && !leftIsZero))
|
||||
{
|
||||
if (rightOperand._isNegative)
|
||||
if (leftOperand._isNegative)
|
||||
return CPOrderedAscending;
|
||||
else
|
||||
return CPOrderedDescending;
|
||||
@@ -525,20 +537,22 @@ function CPDecimalAdd(result, leftOperand, rightOperand, roundingMode, longMode)
|
||||
result._isNaN = YES;
|
||||
return CPCalculationNoError;
|
||||
}
|
||||
|
||||
// check for zero
|
||||
if (CPDecimalIsZero(leftOperand))
|
||||
{
|
||||
_CPDecimalSet(result, rightOperand);
|
||||
return CPCalculationNoError;
|
||||
}
|
||||
|
||||
if (CPDecimalIsZero(rightOperand))
|
||||
{
|
||||
_CPDecimalSet(result, leftOperand);
|
||||
return CPCalculationNoError;
|
||||
}
|
||||
|
||||
var n1 = CPDecimalCopy(leftOperand),
|
||||
n2 = CPDecimalCopy(rightOperand);
|
||||
var n1 = CPDecimalCopy(leftOperand),
|
||||
n2 = CPDecimalCopy(rightOperand);
|
||||
|
||||
// For different signs use subtraction
|
||||
if (leftOperand._isNegative != rightOperand._isNegative)
|
||||
@@ -561,6 +575,7 @@ function CPDecimalAdd(result, leftOperand, rightOperand, roundingMode, longMode)
|
||||
var comp = 0,
|
||||
ll = n1._mantissa.length,
|
||||
lr = n2._mantissa.length;
|
||||
|
||||
if (ll == lr)
|
||||
comp = CPOrderedSame;
|
||||
else if (ll > lr)
|
||||
@@ -573,6 +588,7 @@ function CPDecimalAdd(result, leftOperand, rightOperand, roundingMode, longMode)
|
||||
{
|
||||
n1._isNegative = NO;
|
||||
n2._isNegative = NO;
|
||||
|
||||
// SimpleCompare does not look at sign
|
||||
if (comp == CPOrderedDescending)
|
||||
{
|
||||
@@ -582,7 +598,9 @@ function CPDecimalAdd(result, leftOperand, rightOperand, roundingMode, longMode)
|
||||
{
|
||||
adderror = _SimpleAdd(result, n2, n1, roundingMode, longMode);
|
||||
}
|
||||
|
||||
result._isNegative = YES;
|
||||
|
||||
// swap sign over over/underflow exception
|
||||
if (CPCalculationUnderflow == adderror)
|
||||
adderror = CPCalculationOverflow;
|
||||
@@ -624,6 +642,7 @@ function _SimpleSubtract(result, leftOperand, rightOperand, roundingMode)
|
||||
for (; i >= 0; i--)
|
||||
{
|
||||
var d = result._mantissa[i + j] - rightOperand._mantissa[i] - borrow;
|
||||
|
||||
if (d < 0)
|
||||
{
|
||||
d = d + 10;
|
||||
@@ -680,15 +699,16 @@ function CPDecimalSubtract(result, leftOperand, rightOperand, roundingMode)
|
||||
result._isNegative = !result._isNegative;
|
||||
return CPCalculationNoError;
|
||||
}
|
||||
|
||||
if (CPDecimalIsZero(rightOperand))
|
||||
{
|
||||
_CPDecimalSet(result, leftOperand);
|
||||
return CPCalculationNoError;
|
||||
}
|
||||
|
||||
var n1 = CPDecimalCopy(leftOperand),
|
||||
n2 = CPDecimalCopy(rightOperand),
|
||||
error1 = CPCalculationNoError;
|
||||
var n1 = CPDecimalCopy(leftOperand),
|
||||
n2 = CPDecimalCopy(rightOperand),
|
||||
error1 = CPCalculationNoError;
|
||||
|
||||
// For different signs use addition
|
||||
if (leftOperand._isNegative != rightOperand._isNegative)
|
||||
@@ -698,10 +718,12 @@ function CPDecimalSubtract(result, leftOperand, rightOperand, roundingMode)
|
||||
n1._isNegative = NO;
|
||||
error1 = CPDecimalAdd(result, n1, rightOperand, roundingMode);
|
||||
result._isNegative = YES;
|
||||
|
||||
if (error1 == CPCalculationUnderflow)
|
||||
error1 = CPCalculationOverflow;
|
||||
else if (error1 == CPCalculationOverflow) // gnustep has bug here
|
||||
error1 = CPCalculationUnderflow;
|
||||
|
||||
return error1;
|
||||
}
|
||||
else
|
||||
@@ -725,6 +747,7 @@ function CPDecimalSubtract(result, leftOperand, rightOperand, roundingMode)
|
||||
{
|
||||
n1._isNegative = NO;
|
||||
n2._isNegative = NO;
|
||||
|
||||
if (comp == CPOrderedAscending)
|
||||
{
|
||||
error1 = _SimpleSubtract(result, n1, n2, roundingMode);
|
||||
@@ -777,6 +800,7 @@ function _SimpleDivide(result, leftOperand, rightOperand, roundingMode)
|
||||
{
|
||||
if (stopk == k)
|
||||
break;
|
||||
|
||||
if (n1._exponent)
|
||||
{
|
||||
// Put back zeros removed by compacting
|
||||
@@ -795,6 +819,7 @@ function _SimpleDivide(result, leftOperand, rightOperand, roundingMode)
|
||||
Array.prototype.push.call(n1._mantissa, (leftOperand._mantissa[used]));
|
||||
n1._isCompact = NO;
|
||||
}
|
||||
|
||||
used++;
|
||||
}
|
||||
else
|
||||
@@ -805,6 +830,7 @@ function _SimpleDivide(result, leftOperand, rightOperand, roundingMode)
|
||||
k = stopk;
|
||||
break;
|
||||
}
|
||||
|
||||
// Borrow one digit
|
||||
Array.prototype.push.call(n1._mantissa, 0);
|
||||
result._exponent--;
|
||||
@@ -839,6 +865,7 @@ function _SimpleDivide(result, leftOperand, rightOperand, roundingMode)
|
||||
}
|
||||
|
||||
error1 = CPDecimalSubtract(n1, n1, rightOperand, roundingMode);
|
||||
|
||||
if (error1 != CPCalculationNoError)
|
||||
error = error1;
|
||||
|
||||
@@ -875,6 +902,7 @@ function CPDecimalDivide(result, leftOperand, rightOperand, roundingMode)
|
||||
result._isNaN = YES;
|
||||
return CPCalculationDivideByZero;
|
||||
}
|
||||
|
||||
if (CPDecimalIsZero(leftOperand))
|
||||
{
|
||||
_CPDecimalSetZero(result);
|
||||
@@ -914,6 +942,7 @@ function CPDecimalDivide(result, leftOperand, rightOperand, roundingMode)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
result._exponent += exp;
|
||||
result._isNegative = neg;
|
||||
return error;
|
||||
@@ -930,8 +959,7 @@ function _SimpleMultiply(result, leftOperand, rightOperand, roundingMode, powerM
|
||||
_CPDecimalSetZero(result);
|
||||
|
||||
// Do every digit of the second number
|
||||
var i = 0;
|
||||
for (; i < rightOperand._mantissa.length; i++)
|
||||
for (var i = 0; i < rightOperand._mantissa.length; i++)
|
||||
{
|
||||
_CPDecimalSetZero(n);
|
||||
|
||||
@@ -942,10 +970,10 @@ function _SimpleMultiply(result, leftOperand, rightOperand, roundingMode, powerM
|
||||
if (d == 0)
|
||||
continue;
|
||||
|
||||
var j = 0;
|
||||
for (j = leftOperand._mantissa.length - 1; j >= 0; j--)
|
||||
for (var j = leftOperand._mantissa.length - 1; j >= 0; j--)
|
||||
{
|
||||
e = leftOperand._mantissa[j] * d + carry;
|
||||
|
||||
if (e >= 10)
|
||||
{
|
||||
carry = FLOOR(e / 10);
|
||||
@@ -957,6 +985,7 @@ function _SimpleMultiply(result, leftOperand, rightOperand, roundingMode, powerM
|
||||
// This is one off to allow final carry
|
||||
n._mantissa[j + 1] = e;
|
||||
}
|
||||
|
||||
n._mantissa[0] = carry;
|
||||
|
||||
CPDecimalCompact(n);
|
||||
@@ -973,17 +1002,20 @@ function _SimpleMultiply(result, leftOperand, rightOperand, roundingMode, powerM
|
||||
result._isNaN = YES;
|
||||
return CPCalculationOverflow;
|
||||
}
|
||||
|
||||
result._exponent += exp;
|
||||
|
||||
// perform round to CPDecimalMaxDigits
|
||||
if (result._mantissa.length > CPDecimalMaxDigits && !powerMode)
|
||||
{
|
||||
result._isCompact = NO;
|
||||
|
||||
var scale = CPDecimalMaxDigits - (result._mantissa.length + result._exponent);
|
||||
CPDecimalRound(result, result, scale, roundingMode); // calls compact
|
||||
|
||||
error = CPCalculationLossOfPrecision;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -1020,6 +1052,7 @@ function CPDecimalMultiply(result, leftOperand, rightOperand, roundingMode, powe
|
||||
if (exp > CPDecimalMaxExponent)
|
||||
{
|
||||
result._isNaN = YES;
|
||||
|
||||
if (neg)
|
||||
return CPCalculationUnderflow;
|
||||
else
|
||||
@@ -1038,6 +1071,7 @@ function CPDecimalMultiply(result, leftOperand, rightOperand, roundingMode, powe
|
||||
var comp = 0,
|
||||
ll = n1._mantissa.length,
|
||||
lr = n2._mantissa.length;
|
||||
|
||||
if (ll == lr)
|
||||
comp = CPOrderedSame;
|
||||
else if (ll > lr)
|
||||
@@ -1045,7 +1079,6 @@ function CPDecimalMultiply(result, leftOperand, rightOperand, roundingMode, powe
|
||||
else
|
||||
comp = CPOrderedAscending;
|
||||
|
||||
|
||||
if (comp == CPOrderedDescending)
|
||||
{
|
||||
error = _SimpleMultiply(result, n1, n2, roundingMode, powerMode);
|
||||
@@ -1060,6 +1093,7 @@ function CPDecimalMultiply(result, leftOperand, rightOperand, roundingMode, powe
|
||||
if (result._exponent + exp > CPDecimalMaxExponent)
|
||||
{
|
||||
result._isNaN = YES;
|
||||
|
||||
if (neg)
|
||||
return CPCalculationUnderflow;
|
||||
else
|
||||
@@ -1096,17 +1130,21 @@ function CPDecimalMultiply(result, leftOperand, rightOperand, roundingMode, powe
|
||||
function CPDecimalMultiplyByPowerOf10(result, dcm, power, roundingMode)
|
||||
{
|
||||
_CPDecimalSet(result, dcm);
|
||||
|
||||
var p = result._exponent + power;
|
||||
|
||||
if (p > CPDecimalMaxExponent)
|
||||
{
|
||||
result._isNaN = YES;
|
||||
return CPCalculationOverflow;
|
||||
}
|
||||
|
||||
if (p < CPDecimalMinExponent)
|
||||
{
|
||||
result._isNaN = YES;
|
||||
return CPCalculationUnderflow;
|
||||
}
|
||||
|
||||
result._exponent += power;
|
||||
return CPCalculationNoError;
|
||||
}
|
||||
@@ -1131,6 +1169,7 @@ function CPDecimalPower(result, dcm, power, roundingMode)
|
||||
_CPDecimalSetOne(result);
|
||||
|
||||
var e = power;
|
||||
|
||||
while (e)
|
||||
{
|
||||
if (e & 1)
|
||||
@@ -1174,7 +1213,7 @@ function CPDecimalPower(result, dcm, power, roundingMode)
|
||||
*/
|
||||
function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode)
|
||||
{
|
||||
var factor = (longMode)?2:1;
|
||||
var factor = (longMode) ? 2 : 1;
|
||||
|
||||
if (dcm1._isNaN || dcm2._isNaN)
|
||||
return CPCalculationNoError; // FIXME: correct behavior?
|
||||
@@ -1182,6 +1221,7 @@ function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode)
|
||||
// ensure compact
|
||||
if (!dcm1._isCompact)
|
||||
CPDecimalCompact(dcm1);
|
||||
|
||||
if (!dcm2._isCompact)
|
||||
CPDecimalCompact(dcm2);
|
||||
|
||||
@@ -1197,6 +1237,7 @@ function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode)
|
||||
l = 0;
|
||||
|
||||
var e = 0;
|
||||
|
||||
if (e2 > e1 && e1 >= 0 && e2 >= 0)
|
||||
e = e2 - e1;
|
||||
else if (e2 > e1 && e1 < 0 && e2 >= 0)
|
||||
@@ -1222,6 +1263,7 @@ function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode)
|
||||
else
|
||||
Array.prototype.push.call(dcm1._mantissa, 0);
|
||||
}
|
||||
|
||||
if (e2 > e1)
|
||||
{
|
||||
dcm2._exponent -= l;
|
||||
@@ -1276,6 +1318,7 @@ function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode)
|
||||
dcm2._isCompact = NO;
|
||||
}
|
||||
}
|
||||
|
||||
return CPCalculationLossOfPrecision;
|
||||
}
|
||||
|
||||
@@ -1294,8 +1337,10 @@ function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode)
|
||||
@param roundingMode the rounding mode for the operation
|
||||
@return a CPCalculationError status value.
|
||||
*/
|
||||
function CPDecimalRound(result, dcm, scale ,roundingMode)
|
||||
function CPDecimalRound(result, dcm, scale, roundingMode)
|
||||
{
|
||||
_CPDecimalSet(result, dcm);
|
||||
|
||||
if (dcm._isNaN)
|
||||
return;
|
||||
|
||||
@@ -1308,18 +1353,19 @@ function CPDecimalRound(result, dcm, scale ,roundingMode)
|
||||
if (scale == CPDecimalNoScale)
|
||||
return;
|
||||
|
||||
_CPDecimalSet(result,dcm);
|
||||
|
||||
var mc = result._mantissa.length,
|
||||
l = mc + scale + result._exponent;
|
||||
|
||||
if (mc <= l)
|
||||
return;
|
||||
|
||||
else if (l <= 0)
|
||||
{
|
||||
_CPDecimalSetZero(result);
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var c = 0,
|
||||
n = 0,
|
||||
up = 0;
|
||||
@@ -1329,39 +1375,46 @@ function CPDecimalRound(result, dcm, scale ,roundingMode)
|
||||
|
||||
switch (roundingMode)
|
||||
{
|
||||
case CPRoundDown:
|
||||
up = result._isNegative;
|
||||
break;
|
||||
case CPRoundUp:
|
||||
up = !result._isNegative;
|
||||
break;
|
||||
case CPRoundPlain:
|
||||
n = result._mantissa[l];
|
||||
up = (n >= 5);
|
||||
break;
|
||||
case _CPRoundHalfDown:
|
||||
n = result._mantissa[l];
|
||||
up = (n > 5);
|
||||
break;
|
||||
case CPRoundBankers:
|
||||
n = result._mantissa[l];
|
||||
if (n > 5)
|
||||
up = YES;
|
||||
else if (n < 5)
|
||||
up = NO;
|
||||
else
|
||||
{
|
||||
if (l == 0)
|
||||
c = 0;
|
||||
case CPRoundDown:
|
||||
up = result._isNegative;
|
||||
break;
|
||||
|
||||
case CPRoundUp:
|
||||
up = !result._isNegative;
|
||||
break;
|
||||
|
||||
case CPRoundPlain:
|
||||
n = result._mantissa[l];
|
||||
up = (n >= 5);
|
||||
break;
|
||||
|
||||
case _CPRoundHalfDown:
|
||||
n = result._mantissa[l];
|
||||
up = (n > 5);
|
||||
break;
|
||||
|
||||
case CPRoundBankers:
|
||||
n = result._mantissa[l];
|
||||
|
||||
if (n > 5)
|
||||
up = YES;
|
||||
else if (n < 5)
|
||||
up = NO;
|
||||
else
|
||||
c = result._mantissa[l - 1];
|
||||
up = ((c % 2) != 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
up = NO;
|
||||
break;
|
||||
{
|
||||
if (l == 0)
|
||||
c = 0;
|
||||
else
|
||||
c = result._mantissa[l - 1];
|
||||
up = ((c % 2) != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
up = NO;
|
||||
break;
|
||||
}
|
||||
|
||||
// cut mantissa
|
||||
result._mantissa = Array.prototype.slice.call(result._mantissa, 0, l);
|
||||
|
||||
@@ -1374,13 +1427,16 @@ function CPDecimalRound(result, dcm, scale ,roundingMode)
|
||||
result._mantissa[i]++;
|
||||
break;
|
||||
}
|
||||
|
||||
result._mantissa[i] = 0;
|
||||
}
|
||||
|
||||
// Final overflow?
|
||||
if (i == -1)
|
||||
{
|
||||
// As all digits are zeros, just change the first
|
||||
result._mantissa[0] = 1;
|
||||
|
||||
if (result._exponent >= CPDecimalMaxExponent)
|
||||
{
|
||||
// Overflow in rounding.
|
||||
@@ -1414,17 +1470,18 @@ function CPDecimalCompact(dcm)
|
||||
_CPDecimalSetZero(dcm);
|
||||
return;
|
||||
}
|
||||
|
||||
// leading zeros, when exponent is zero these mean we need to move our decimal point to compact
|
||||
// if exp is zero does it make sense to have them? don't think so so delete them
|
||||
while (dcm._mantissa[0] === 0)
|
||||
{
|
||||
Array.prototype.shift.call(dcm._mantissa);
|
||||
}
|
||||
|
||||
// trailing zeros, strip them
|
||||
while (dcm._mantissa[dcm._mantissa.length - 1] === 0)
|
||||
{
|
||||
Array.prototype.pop.call(dcm._mantissa);
|
||||
dcm._exponent++;
|
||||
|
||||
if (dcm._exponent + 1 > CPDecimalMaxExponent)
|
||||
{
|
||||
// TODO: test case for this
|
||||
@@ -1478,6 +1535,7 @@ function CPDecimalString(dcm, locale)
|
||||
{
|
||||
string += dcm._mantissa[i];
|
||||
}
|
||||
|
||||
if (l < k)
|
||||
{
|
||||
string += ".";
|
||||
@@ -1486,6 +1544,7 @@ function CPDecimalString(dcm, locale)
|
||||
string += dcm._mantissa[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < dcm._exponent; i++)
|
||||
{
|
||||
string += "0";
|
||||
|
||||
@@ -123,6 +123,7 @@ var CPDefaultDcmHandler = nil;
|
||||
{
|
||||
if (!CPDefaultDcmHandler)
|
||||
CPDefaultDcmHandler = [[CPDecimalNumberHandler alloc] init];
|
||||
|
||||
return CPDefaultDcmHandler;
|
||||
}
|
||||
|
||||
@@ -172,26 +173,37 @@ var CPDefaultDcmHandler = nil;
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case CPCalculationNoError: break;
|
||||
case CPCalculationOverflow: if (_raiseOnOverflow)
|
||||
[CPException raise:CPDecimalNumberOverflowException reason:("A CPDecimalNumber overflow has occurred. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
|
||||
else
|
||||
return [CPDecimalNumber notANumber];
|
||||
break;
|
||||
case CPCalculationUnderflow: if (_raiseOnUnderflow)
|
||||
[CPException raise:CPDecimalNumberUnderflowException reason:("A CPDecimalNumber underflow has occurred. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
|
||||
else
|
||||
return [CPDecimalNumber notANumber];
|
||||
break;
|
||||
case CPCalculationLossOfPrecision: if (_raiseOnExactness)
|
||||
[CPException raise:CPDecimalNumberExactnessException reason:("A CPDecimalNumber has been rounded off during a calculation. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
|
||||
break;
|
||||
case CPCalculationDivideByZero: if (_raiseOnDivideByZero)
|
||||
[CPException raise:CPDecimalNumberDivideByZeroException reason:("A CPDecimalNumber divide by zero has occurred. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
|
||||
else
|
||||
return [CPDecimalNumber notANumber]; // Div by zero returns NaN
|
||||
break;
|
||||
default: [CPException raise:CPInvalidArgumentException reason:("An unknown CPDecimalNumber error has occurred. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
|
||||
case CPCalculationNoError:
|
||||
break;
|
||||
|
||||
case CPCalculationOverflow:
|
||||
if (_raiseOnOverflow)
|
||||
[CPException raise:CPDecimalNumberOverflowException reason:("A CPDecimalNumber overflow has occurred. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
|
||||
else
|
||||
return [CPDecimalNumber notANumber];
|
||||
break;
|
||||
|
||||
case CPCalculationUnderflow:
|
||||
if (_raiseOnUnderflow)
|
||||
[CPException raise:CPDecimalNumberUnderflowException reason:("A CPDecimalNumber underflow has occurred. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
|
||||
else
|
||||
return [CPDecimalNumber notANumber];
|
||||
break;
|
||||
|
||||
case CPCalculationLossOfPrecision:
|
||||
if (_raiseOnExactness)
|
||||
[CPException raise:CPDecimalNumberExactnessException reason:("A CPDecimalNumber has been rounded off during a calculation. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
|
||||
break;
|
||||
|
||||
case CPCalculationDivideByZero:
|
||||
if (_raiseOnDivideByZero)
|
||||
[CPException raise:CPDecimalNumberDivideByZeroException reason:("A CPDecimalNumber divide by zero has occurred. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')") ];
|
||||
else
|
||||
return [CPDecimalNumber notANumber]; // Div by zero returns NaN
|
||||
break;
|
||||
|
||||
default:
|
||||
[CPException raise:CPInvalidArgumentException reason:("An unknown CPDecimalNumber error has occurred. (Left operand= '" + [leftOperand descriptionWithLocale:nil] + "' Right operand= '" + [rightOperand descriptionWithLocale:nil] + "' Selector= '" + operation + "')")];
|
||||
}
|
||||
|
||||
return nil;
|
||||
@@ -537,6 +549,7 @@ var CPDecimalNumberHandlerRoundingModeKey = @"CPDecimalNumberHandlerRoundi
|
||||
if (res != nil)
|
||||
return res;
|
||||
}
|
||||
|
||||
return [CPDecimalNumber decimalNumberWithDecimal:result];
|
||||
}
|
||||
|
||||
@@ -570,9 +583,11 @@ var CPDecimalNumberHandlerRoundingModeKey = @"CPDecimalNumberHandlerRoundi
|
||||
if (error > CPCalculationNoError)
|
||||
{
|
||||
var res = [behavior exceptionDuringOperation:_cmd error:error leftOperand:self rightOperand:decimalNumber];
|
||||
|
||||
if (res != nil)
|
||||
return res;
|
||||
}
|
||||
|
||||
return [CPDecimalNumber decimalNumberWithDecimal:result];
|
||||
}
|
||||
|
||||
@@ -609,6 +624,7 @@ var CPDecimalNumberHandlerRoundingModeKey = @"CPDecimalNumberHandlerRoundi
|
||||
if (res != nil)
|
||||
return res;
|
||||
}
|
||||
|
||||
return [CPDecimalNumber decimalNumberWithDecimal:result];
|
||||
}
|
||||
|
||||
@@ -642,9 +658,11 @@ var CPDecimalNumberHandlerRoundingModeKey = @"CPDecimalNumberHandlerRoundi
|
||||
if (error > CPCalculationNoError)
|
||||
{
|
||||
var res = [behavior exceptionDuringOperation:_cmd error:error leftOperand:self rightOperand:decimalNumber];
|
||||
|
||||
if (res != nil)
|
||||
return res;
|
||||
}
|
||||
|
||||
return [CPDecimalNumber decimalNumberWithDecimal:result];
|
||||
}
|
||||
|
||||
@@ -678,9 +696,11 @@ var CPDecimalNumberHandlerRoundingModeKey = @"CPDecimalNumberHandlerRoundi
|
||||
if (error > CPCalculationNoError)
|
||||
{
|
||||
var res = [behavior exceptionDuringOperation:_cmd error:error leftOperand:self rightOperand:[CPDecimalNumber decimalNumberWithString:power.toString()]];
|
||||
|
||||
if (res != nil)
|
||||
return res;
|
||||
}
|
||||
|
||||
return [CPDecimalNumber decimalNumberWithDecimal:result];
|
||||
}
|
||||
|
||||
@@ -717,9 +737,11 @@ var CPDecimalNumberHandlerRoundingModeKey = @"CPDecimalNumberHandlerRoundi
|
||||
if (error > CPCalculationNoError)
|
||||
{
|
||||
var res = [behavior exceptionDuringOperation:_cmd error:error leftOperand:self rightOperand:[CPDecimalNumber decimalNumberWithString:power.toString()]];
|
||||
|
||||
if (res != nil)
|
||||
return res;
|
||||
}
|
||||
|
||||
return [CPDecimalNumber decimalNumberWithDecimal:result];
|
||||
}
|
||||
|
||||
@@ -751,6 +773,7 @@ var CPDecimalNumberHandlerRoundingModeKey = @"CPDecimalNumberHandlerRoundi
|
||||
// aNumber type is checked to convert if appropriate
|
||||
if (![aNumber isKindOfClass:[CPDecimalNumber class]])
|
||||
aNumber = [CPDecimalNumber decimalNumberWithString:aNumber.toString()];
|
||||
|
||||
return CPDecimalCompare([self decimalValue], [aNumber decimalValue]);
|
||||
}
|
||||
|
||||
|
||||
@@ -300,7 +300,7 @@
|
||||
var value = arguments[index];
|
||||
|
||||
if (value === nil)
|
||||
break;
|
||||
continue;
|
||||
|
||||
[self setObject:value forKey:arguments[index + 1]];
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ CPUndefinedKeyException = @"CPUndefinedKeyException";
|
||||
CPTargetObjectUserInfoKey = @"CPTargetObjectUserInfoKey";
|
||||
CPUnknownUserInfoKey = @"CPUnknownUserInfoKey";
|
||||
|
||||
var CPObjectAccessorsForClassKey = @"$CPObjectAccessorsForClassKey",
|
||||
CPObjectModifiersForClassKey = @"$CPObjectModifiersForClassKey";
|
||||
var CPObjectAccessorsForClassKey = @"$CPObjectAccessorsForClassKey",
|
||||
CPObjectModifiersForClassKey = @"$CPObjectModifiersForClassKey";
|
||||
|
||||
@implementation CPObject (CPKeyValueCoding)
|
||||
|
||||
@@ -107,13 +107,20 @@ var CPObjectAccessorsForClassKey = @"$CPObjectAccessorsForClassKey",
|
||||
|
||||
switch (accessor[0])
|
||||
{
|
||||
case 0: return objj_msgSend(self, accessor[1]);
|
||||
// FIXME: We shouldn't be creating a new one every time.
|
||||
case 1: return [[_CPKeyValueCodingArray alloc] initWithTarget:self key:aKey];
|
||||
// FIXME: We shouldn't be creating a new one every time.
|
||||
case 2: return [[_CPKeyValueCodingSet alloc] initWithTarget:self key:aKey];
|
||||
case 3: if ([theClass accessInstanceVariablesDirectly])
|
||||
return self[accessor[1]];
|
||||
case 0:
|
||||
return objj_msgSend(self, accessor[1]);
|
||||
|
||||
case 1:
|
||||
// FIXME: We shouldn't be creating a new one every time.
|
||||
return [[_CPKeyValueCodingArray alloc] initWithTarget:self key:aKey];
|
||||
|
||||
case 2:
|
||||
// FIXME: We shouldn't be creating a new one every time.
|
||||
return [[_CPKeyValueCodingSet alloc] initWithTarget:self key:aKey];
|
||||
|
||||
case 3:
|
||||
if ([theClass accessInstanceVariablesDirectly])
|
||||
return self[accessor[1]];
|
||||
}
|
||||
|
||||
return [self valueForUndefinedKey:aKey];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* CPKeyValueCoding.j
|
||||
* CPKeyValueObserving.j
|
||||
* Foundation
|
||||
*
|
||||
* Created by Ross Boucher.
|
||||
@@ -788,7 +788,11 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
|
||||
if (aPath.indexOf('.') != CPNotFound)
|
||||
{
|
||||
var forwarder = [observers objectForKey:[anObserver UID]].forwarder;
|
||||
// During cib instantiation, it is possible for the forwarder to not yet be available,
|
||||
// so we have to check for nil.
|
||||
var observer = [observers objectForKey:[anObserver UID]],
|
||||
forwarder = observer ? observer.forwarder : nil;
|
||||
|
||||
[forwarder finalize];
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
@import "CPFormatter.j"
|
||||
@import "CPDecimalNumber.j"
|
||||
|
||||
#define UPDATE_NUMBER_HANDLER_IF_NECESSARY() if (!_numberHandler) \
|
||||
_numberHandler = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:_roundingMode scale:_maximumFractionDigits raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:YES];
|
||||
#define SET_NEEDS_NUMBER_HANDLER_UPDATE() _numberHandler = nil;
|
||||
|
||||
CPNumberFormatterNoStyle = 0;
|
||||
CPNumberFormatterDecimalStyle = 1;
|
||||
@@ -47,6 +44,9 @@ CPNumberFormatterRoundHalfUp = CPRoundPlain;
|
||||
|
||||
var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
|
||||
|
||||
#define SET_NEEDS_NUMBER_HANDLER_UPDATE() _numberHandler = nil
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup foundation
|
||||
@class CPNumberFormatter
|
||||
@@ -105,7 +105,7 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
|
||||
case CPNumberFormatterCurrencyStyle:
|
||||
case CPNumberFormatterDecimalStyle:
|
||||
case CPNumberFormatterPercentStyle:
|
||||
UPDATE_NUMBER_HANDLER_IF_NECESSARY();
|
||||
[self _updateNumberHandlerIfNecessary];
|
||||
|
||||
dcmn = [dcmn decimalNumberByRoundingAccordingToBehavior:_numberHandler];
|
||||
|
||||
@@ -133,6 +133,7 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
|
||||
}
|
||||
|
||||
var string = preFraction;
|
||||
|
||||
if (fraction)
|
||||
string += "." + fraction;
|
||||
|
||||
@@ -151,6 +152,7 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
|
||||
string = negativePrefix + string;
|
||||
|
||||
return string;
|
||||
|
||||
default:
|
||||
return [number description];
|
||||
}
|
||||
@@ -197,6 +199,7 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
|
||||
_maximumFractionDigits = 3;
|
||||
SET_NEEDS_NUMBER_HANDLER_UPDATE();
|
||||
break;
|
||||
|
||||
case CPNumberFormatterCurrencyStyle:
|
||||
_minimumFractionDigits = 2;
|
||||
_maximumFractionDigits = 2;
|
||||
@@ -223,9 +226,17 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
|
||||
SET_NEEDS_NUMBER_HANDLER_UPDATE();
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (void)_updateNumberHandlerIfNecessary
|
||||
{
|
||||
if (!_numberHandler)
|
||||
_numberHandler = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:_roundingMode scale:_maximumFractionDigits raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPNumberFormatterStyleKey = "CPNumberFormatterStyleKey",
|
||||
var CPNumberFormatterStyleKey = @"CPNumberFormatterStyleKey",
|
||||
CPNumberFormatterMinimumFractionDigitsKey = @"CPNumberFormatterMinimumFractionDigitsKey",
|
||||
CPNumberFormatterMaximumFractionDigitsKey = @"CPNumberFormatterMaximumFractionDigitsKey",
|
||||
CPNumberFormatterRoundingModeKey = @"CPNumberFormatterRoundingModeKey",
|
||||
|
||||
@@ -190,13 +190,18 @@
|
||||
if (self === anObject)
|
||||
return YES;
|
||||
|
||||
if (self.isa !== anObject.isa || _value !== [anObject evaluateObject:nil])
|
||||
if (self.isa !== anObject.isa || _value !== [anObject evaluateWithObject:nil])
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)evaluateObject:(id)object
|
||||
- (BOOL)evaluateWithObject:(id)object
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
- (BOOL)evaluateWithObject:(id)object substitutionVariables:(CPDictionary)variables
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
@import "_CGGeometry.j"
|
||||
@import "CPArray.j"
|
||||
@import "CPBundle.j"
|
||||
@import "CPByteCountFormatter.j"
|
||||
@import "CPCharacterSet.j"
|
||||
@import "CPCoder.j"
|
||||
@import "CPComparisonPredicate.j"
|
||||
|
||||
@@ -22,8 +22,16 @@
|
||||
|
||||
@import "CPObject.j"
|
||||
|
||||
var _CPCollectionKVCOperatorSimpleRE = /^@(avg|count|m(ax|in)|sum)(\.|$)/;
|
||||
|
||||
|
||||
@implementation _CPCollectionKVCOperator : CPObject
|
||||
|
||||
+ (BOOL)isSimpleCollectionOperator:(CPString)operator
|
||||
{
|
||||
return _CPCollectionKVCOperatorSimpleRE.test(operator);
|
||||
}
|
||||
|
||||
+ (id)performOperation:(CPString)operator withCollection:(id)aCollection propertyPath:(CPString)propertyPath
|
||||
{
|
||||
var selector = CPSelectorFromString(operator + @"ForCollection:propertyPath:");
|
||||
|
||||
@@ -111,7 +111,7 @@ function environmentFlags()
|
||||
|
||||
var SHRINKSAFE = require("minify/shrinksafe");
|
||||
function compressor(code) {
|
||||
return SHRINKSAFE.compress(code, { charset : "UTF-16", useServer : true });
|
||||
return SHRINKSAFE.compress(code, { charset : "UTF-8", useServer : true });
|
||||
}
|
||||
|
||||
function gcc(inputFilePath, outputFilePath, flags, compress)
|
||||
|
||||
@@ -184,7 +184,46 @@
|
||||
[self assert:[CPNumber numberWithInt:2] equals:[_arrayController arrangedObjects][1]];
|
||||
}
|
||||
|
||||
- (void)testAdd
|
||||
{
|
||||
_contentArray = [];
|
||||
_arrayController = [[CPArrayController alloc] initWithContent:_contentArray];
|
||||
[self _testAdd];
|
||||
}
|
||||
|
||||
- (void)_testAdd
|
||||
{
|
||||
[_arrayController setObjectClass:[Employee class]];
|
||||
[self assert:[[_arrayController contentArray] count] equals:0];
|
||||
[_arrayController add:nil];
|
||||
[self assert:[[_arrayController contentArray] count] equals:1];
|
||||
[self assert:[[_arrayController arrangedObjects][0] class] equals:[Employee class]];
|
||||
|
||||
// switch it up so that we can tell where this one is.
|
||||
[_arrayController setObjectClass:[Department class]];
|
||||
[_arrayController add:nil];
|
||||
[self assert:[[[_arrayController arrangedObjects] lastObject] class] equals:[Department class]];
|
||||
}
|
||||
|
||||
- (void)testInsert
|
||||
{
|
||||
_contentArray = [];
|
||||
_arrayController = [[CPArrayController alloc] initWithContent:_contentArray];
|
||||
[self _testInsert];
|
||||
}
|
||||
|
||||
- (void)_testInsert
|
||||
{
|
||||
[_arrayController setObjectClass:[Employee class]];
|
||||
[_arrayController add:nil];
|
||||
[_arrayController add:nil];
|
||||
[_arrayController add:nil];
|
||||
|
||||
[_arrayController setObjectClass:[Department class]];
|
||||
[_arrayController setSelectionIndex:1];
|
||||
[_arrayController insert:nil];
|
||||
[self assert:[[[_arrayController arrangedObjects] objectAtIndex:1] class] equals:[Department class]];
|
||||
}
|
||||
|
||||
- (void)_initTestRemoveObjects_SimpleArray
|
||||
{
|
||||
|
||||
@@ -214,5 +214,53 @@
|
||||
[self assert:replacementDocumentView equals:[scrollView documentView] message:@"documentView was not replaced properly"];
|
||||
}
|
||||
|
||||
- (void)testScrollRectToVisible
|
||||
{
|
||||
var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)],
|
||||
documentView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 1000, 1000)],
|
||||
textField1 = [CPTextField textFieldWithStringValue:@"Martin" placeholder:@"" width:10],
|
||||
textField2 = [CPTextField textFieldWithStringValue:@"Malte" placeholder:@"" width:10],
|
||||
textField1Size = CGSizeMakeCopy([textField1 bounds].size),
|
||||
textField2Size = CGSizeMakeCopy([textField2 bounds].size);
|
||||
|
||||
[scrollView setDocumentView:documentView];
|
||||
|
||||
[textField1 setFrameOrigin:CGPointMake(0, 0)];
|
||||
[textField2 setFrameOrigin:CGPointMake(500, 500)];
|
||||
|
||||
[documentView addSubview:textField1];
|
||||
[documentView addSubview:textField2];
|
||||
|
||||
var visibleRect = [documentView visibleRect],
|
||||
originalVisibleSize = CGSizeMakeCopy(visibleRect.size);
|
||||
|
||||
// Make sure we are at the top left corner
|
||||
[self assertPoint:CGPointMake(0, 0) equals:visibleRect.origin message:@"VisibleRect origin not at top left corner"];
|
||||
|
||||
// Make the second text field visible
|
||||
[textField2 scrollRectToVisible:[textField2 bounds]];
|
||||
|
||||
var visibleRectOriginShouldBeAt = CGPointMake(500 - originalVisibleSize.width + textField2Size.width, 500 -originalVisibleSize.height + textField2Size.height);
|
||||
|
||||
visibleRect = [documentView visibleRect];
|
||||
|
||||
// We should now have the text field in the lower right corner
|
||||
[self assertPoint:visibleRectOriginShouldBeAt equals:visibleRect.origin message:@"Second text field not at lower right corner in visible rect"];
|
||||
|
||||
// Make the first text field visible again
|
||||
[textField1 scrollRectToVisible:[textField2 bounds]];
|
||||
|
||||
visibleRect = [documentView visibleRect];
|
||||
|
||||
// We should now be back at top left corner
|
||||
[self assertPoint:CGPointMake(0, 0) equals:visibleRect.origin message:@"VisibleRect origin not at top left corner again"];
|
||||
}
|
||||
|
||||
- (void)assertPoint:(CGPoint)expected equals:(CGPoint)actual message:(CPString)message
|
||||
{
|
||||
[self assert:expected.x equals:actual.x message:@"X: " + message];
|
||||
[self assert:expected.y equals:actual.y message:@"Y: " + message];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
@import <AppKit/CPWindow.j>
|
||||
@import <AppKit/_CPBorderlessWindowView.j>
|
||||
@import <Foundation/CPURL.j>
|
||||
|
||||
[CPApplication sharedApplication];
|
||||
|
||||
@@ -109,4 +110,22 @@
|
||||
message:@"nextTextField should be the previous valid key view of contentView"];
|
||||
}
|
||||
|
||||
- (void)testRepresentedFilename
|
||||
{
|
||||
/*
|
||||
representedURL and representedFilename
|
||||
are drawn from the _representedURL variable.
|
||||
*/
|
||||
var aURL = @"http://www.cappuccino-project.org";
|
||||
|
||||
[[self window] setRepresentedURL:[CPURL URLWithString:aURL]];
|
||||
[self assertTrue:[[[self window] representedURL] class] === [CPURL class]];
|
||||
/*
|
||||
Test for Issue 1633
|
||||
Make sure that changing it via setRepresentedFilename doesn't change the type
|
||||
*/
|
||||
[[self window] setRepresentedFilename:aURL];
|
||||
[self assertTrue:[[[self window] representedURL] class] === [CPURL class]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -212,6 +212,57 @@ var ELEMENTS = 100,
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testObjectsAtIndexesSpeed
|
||||
{
|
||||
REPEATS = 100;
|
||||
|
||||
var SIZE = 1000,
|
||||
c = SIZE,
|
||||
r = REPEATS,
|
||||
rr = r,
|
||||
location = 0,
|
||||
array = [CPArray array],
|
||||
indexes = [CPIndexSet indexSet];
|
||||
|
||||
while (c--)
|
||||
array.push(""+c);
|
||||
|
||||
while (location < SIZE - 10)
|
||||
{
|
||||
var rangeLength = ROUND(10 * RAND());
|
||||
[indexes addIndexesInRange:CPMakeRange(location, rangeLength)];
|
||||
|
||||
location += rangeLength + ROUND(10 * RAND());
|
||||
}
|
||||
|
||||
var d = new Date(),
|
||||
test1,
|
||||
test2;
|
||||
while (r--)
|
||||
test1 = [array _previous_objectsAtIndexes:indexes];
|
||||
var dd = new Date();
|
||||
|
||||
while (rr--)
|
||||
test2 = [array objectsAtIndexes:indexes];
|
||||
var ddd = new Date();
|
||||
|
||||
CPLog.warn("\n_CPJavaScriptArray -objectsAtIndexes:");
|
||||
CPLog.warn(" CPArray -objectsAtIndexes: " + (dd - d) + "ms.");
|
||||
CPLog.warn("_CPJavaScriptArray -objectsAtIndexes: " + (ddd - dd) + "ms.");
|
||||
|
||||
if (![test1 isEqual:test2])
|
||||
[self fail:"_CPJavaScriptArray -objectsAtIndexes: returns an wrong value"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPJavaScriptArray (ObjectsAtIndexes)
|
||||
|
||||
- (CPArray)_previous_objectsAtIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
return [super objectsAtIndexes:indexes];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPArray (NativeSort)
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
{
|
||||
if (!_dcmnh)
|
||||
_dcmnh = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundPlain scale:0 raiseOnExactness:NO raiseOnOverflow:YES raiseOnUnderflow:YES raiseOnDivideByZero:YES];
|
||||
|
||||
if (!_dcmnhWithExactness)
|
||||
_dcmnhWithExactness = [CPDecimalNumberHandler decimalNumberHandlerWithRoundingMode:CPRoundPlain scale:0 raiseOnExactness:YES raiseOnOverflow:YES raiseOnUnderflow:YES raiseOnDivideByZero:YES];
|
||||
}
|
||||
@@ -42,6 +43,7 @@
|
||||
// CPDecimalNumber class initialisers
|
||||
var dcmn = [[CPDecimalNumber alloc] initWithString:@"10.72342"],
|
||||
dcm = [dcmn decimalValue];
|
||||
|
||||
[self assert:-5 equals:dcm._exponent message:"initWithString: - exponent"];
|
||||
[self assert:[1,0,7,2,3,4,2] equals:dcm._mantissa message:"initWithString: - mantissa"];
|
||||
[self assert:NO equals:dcm._isNegative message:"initWithString: - sign"];
|
||||
@@ -157,11 +159,11 @@
|
||||
|
||||
- (void)testAdd
|
||||
{
|
||||
|
||||
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"123"],
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"321"],
|
||||
dcmn3 = [dcmn1 decimalNumberByAdding:dcmn2],
|
||||
d1 = [dcmn3 decimalValue];
|
||||
|
||||
[self assert:0 equals:d1._exponent message:"decimalNumberByAdding: - exponent"];
|
||||
[self assert:[4,4,4] equals:d1._mantissa message:"decimalNumberByAdding: - mantissa"];
|
||||
[self assert:NO equals:d1._isNegative message:"decimalNumberByAdding: - sign"];
|
||||
@@ -170,6 +172,7 @@
|
||||
// precision throw - with behavior
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"10000000000000000000000000000000000001"];
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"1.00001"];
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByAdding:dcmn2 withBehavior:_dcmnhWithExactness];
|
||||
@@ -202,7 +205,6 @@
|
||||
if ((e.isa) && [e name] == AssertionFailedError)
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)testSubtract
|
||||
@@ -211,6 +213,7 @@
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"153"],
|
||||
dcmn3 = [dcmn1 decimalNumberBySubtracting:dcmn2],
|
||||
d1 = [dcmn3 decimalValue];
|
||||
|
||||
[self assert:0 equals:d1._exponent message:"decimalNumberBySubtracting: - exponent"];
|
||||
[self assert:[1,4,8] equals:d1._mantissa message:"decimalNumberBySubtracting: - mantissa"];
|
||||
[self assert:NO equals:d1._isNegative message:"decimalNumberBySubtracting: - sign"];
|
||||
@@ -219,6 +222,7 @@
|
||||
// precision throw - with behavior
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1e-128"];
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"1"];
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberBySubtracting:dcmn2 withBehavior:_dcmnhWithExactness];
|
||||
@@ -247,6 +251,7 @@
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"1235"],
|
||||
dcmn3 = [dcmn1 decimalNumberByDividingBy:dcmn2],
|
||||
d1 = [dcmn3 decimalValue];
|
||||
|
||||
[self assert:-34 equals:d1._exponent message:"decimalNumberByDividingBy: - exponent"];
|
||||
[self assert:[5,7,7,0,2,9,3,9,2,7,1,2,5,5,0,6,0,7,2,8,7,4,4,9,3,9,2,7,1,2,5,5,0,6,0,7,2,8] equals:d1._mantissa message:"decimalNumberByDividingBy: T1: mantissa"];
|
||||
[self assert:NO equals:d1._isNegative message:"decimalNumberByDividingBy: - sign"];
|
||||
@@ -264,6 +269,7 @@
|
||||
// Exceptions
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1"];
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"3"];
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByDividingBy:dcmn2 withBehavior:_dcmnhWithExactness];
|
||||
@@ -286,7 +292,9 @@
|
||||
// underflow , if dcnm2 is < 10 this is LossOfPrecision, is this correct? check vs cocoa.
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"-1e-128"];
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"10"];
|
||||
try {
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByDividingBy:dcmn2];
|
||||
[self fail:"decimalNumberByDividingBy: TEX3 - should have thrown underflow error "];
|
||||
}
|
||||
@@ -298,6 +306,7 @@
|
||||
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"-1e-128"];
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"0"];
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByDividingBy:dcmn2];
|
||||
@@ -322,11 +331,11 @@
|
||||
|
||||
- (void)testMultiply
|
||||
{
|
||||
|
||||
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"17"],
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"512"],
|
||||
dcmn3 = [dcmn1 decimalNumberByMultiplyingBy:dcmn2],
|
||||
d1 = [dcmn3 decimalValue];
|
||||
|
||||
[self assert:0 equals:d1._exponent message:"decimalNumberByMultiplyingBy: - exponent"];
|
||||
[self assert:[8,7,0,4] equals:d1._mantissa message:"decimalNumberByMultiplyingBy: - mantissa"];
|
||||
[self assert:NO equals:d1._isNegative message:"decimalNumberByMultiplyingBy: - sign"];
|
||||
@@ -344,6 +353,7 @@
|
||||
// Exceptions
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"18302741890374918"];
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"123847190277982223422346878934534"];
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByMultiplyingBy:dcmn2 withBehavior:_dcmnhWithExactness];
|
||||
@@ -354,6 +364,7 @@
|
||||
if ((e.isa) && [e name] == AssertionFailedError)
|
||||
throw e;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByMultiplyingBy:dcmn2];
|
||||
@@ -366,6 +377,7 @@
|
||||
// overflow
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"23e100"];
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"-13e76"];
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByMultiplyingBy:dcmn2];
|
||||
@@ -384,6 +396,7 @@
|
||||
p = 21,
|
||||
dcmn3 = [dcmn1 decimalNumberByMultiplyingByPowerOf10:p],
|
||||
d1 = [dcmn3 decimalValue];
|
||||
|
||||
[self assert:21 equals:d1._exponent message:"decimalNumberByMultiplyingByPowerOf10: - exponent"];
|
||||
[self assert:[1,7] equals:d1._mantissa message:"decimalNumberByMultiplyingByPowerOf10: - mantissa"];
|
||||
[self assert:NO equals:d1._isNegative message:"decimalNumberByMultiplyingByPowerOf10: - sign"];
|
||||
@@ -402,7 +415,9 @@
|
||||
// overflow
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1e23"];
|
||||
p = 127;
|
||||
try {
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByMultiplyingByPowerOf10:p];
|
||||
[self fail:"decimalNumberByMultiplyingByPowerOf10: TEX1 - should have overflowed "];
|
||||
}
|
||||
@@ -420,6 +435,7 @@
|
||||
p = 10,
|
||||
dcmn3 = [dcmn1 decimalNumberByRaisingToPower:p],
|
||||
d1 = [dcmn3 decimalValue];
|
||||
|
||||
[self assert:0 equals:d1._exponent message:"decimalNumberByRaisingToPower: - exponent"];
|
||||
[self assert:[2,0,1,5,9,9,3,9,0,0,4,4,9] equals:d1._mantissa message:"decimalNumberByRaisingToPower: - mantissa"];
|
||||
[self assert:NO equals:d1._isNegative message:"decimalNumberByRaisingToPower: - sign"];
|
||||
@@ -437,6 +453,7 @@
|
||||
// Exceptions
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"123198771093489073413"];
|
||||
p = 2;
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByRaisingToPower:p withBehavior:_dcmnhWithExactness];
|
||||
@@ -447,6 +464,7 @@
|
||||
if ((e.isa) && [e name] == AssertionFailedError)
|
||||
throw e;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByRaisingToPower:p];
|
||||
@@ -459,6 +477,7 @@
|
||||
// overflow
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1234"];
|
||||
p = 123;
|
||||
|
||||
try
|
||||
{
|
||||
dcmn3 = [dcmn1 decimalNumberByRaisingToPower:p];
|
||||
@@ -477,6 +496,7 @@
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1.4"],
|
||||
dcmn3 = [dcmn1 decimalNumberByRoundingAccordingToBehavior:h],
|
||||
d1 = [dcmn3 decimalValue];
|
||||
|
||||
[self assert:0 equals:d1._exponent message:"decimalNumberByRoundingAccordingToBehavior: - exponent"];
|
||||
[self assert:[1] equals:d1._mantissa message:"decimalNumberByRoundingAccordingToBehavior: - mantissa"];
|
||||
[self assert:NO equals:d1._isNegative message:"decimalNumberByRoundingAccordingToBehavior: - sign"];
|
||||
@@ -493,6 +513,7 @@
|
||||
var dcmn1 = [CPDecimalNumber decimalNumberWithString:@"17"],
|
||||
dcmn2 = [CPDecimalNumber decimalNumberWithString:@"512"],
|
||||
c = [dcmn1 compare:dcmn2];
|
||||
|
||||
[self assert:c equals:CPOrderedAscending message:"compare: - ascending"];
|
||||
|
||||
dcmn1 = [CPDecimalNumber decimalNumberWithString:@"1234"];
|
||||
@@ -509,6 +530,7 @@
|
||||
- (void)testStringValue
|
||||
{
|
||||
var dcmn = [CPDecimalNumber decimalNumberWithString:@"512"];
|
||||
|
||||
[self assert:"512" equals:[dcmn stringValue] message:"stringValue: - small number"];
|
||||
dcmn = [CPDecimalNumber decimalNumberWithString:@"10823478917236482"];
|
||||
[self assert:"10823478917236482" equals:[dcmn stringValue] message:"stringValue: - medium number"];
|
||||
|
||||
@@ -304,6 +304,7 @@
|
||||
[self assert:-3 equals:dcm1._exponent message:"CPDecimalRound() Tb5: exponent"];
|
||||
|
||||
// Noscale
|
||||
dcm1 = CPDecimalMakeZero();
|
||||
CPDecimalRound(dcm1,dcm,CPDecimalNoScale,CPRoundPlain);
|
||||
[self assert:[1,2,3,4,5,6] equals:dcm1._mantissa message:"CPDecimalRound() Tns1: mantissa"];
|
||||
[self assert:-3 equals:dcm1._exponent message:"CPDecimalRound() Tns1: exponent"];
|
||||
@@ -339,12 +340,37 @@
|
||||
dcm1 = CPDecimalMakeWithString(@"0.0");
|
||||
dcm = CPDecimalMakeWithString(@"0.5");
|
||||
c = CPDecimalCompare(dcm1,dcm);
|
||||
[self assert:CPOrderedAscending equals:c message:"CPDecimalCompare() Tc4: should be descending"];
|
||||
[self assert:CPOrderedAscending equals:c message:"CPDecimalCompare() (0.0, 0.5) should be ascending"];
|
||||
|
||||
dcm1 = CPDecimalMakeWithString(@"0.0");
|
||||
dcm = CPDecimalMakeWithString(@"-0.5");
|
||||
c = CPDecimalCompare(dcm1,dcm);
|
||||
[self assert:CPOrderedDescending equals:c message:"CPDecimalCompare() Tc4: should be descending"];
|
||||
[self assert:CPOrderedDescending equals:c message:"CPDecimalCompare() (0, -0.5) should be descending"];
|
||||
|
||||
dcm1 = CPDecimalMakeZero();
|
||||
dcm = CPDecimalMakeZero();
|
||||
c = CPDecimalCompare(dcm1,dcm);
|
||||
[self assert:CPOrderedSame equals:c message:"CPDecimalCompare(): zeros should be same"];
|
||||
|
||||
dcm1 = CPDecimalMakeWithString(@"0.0001");
|
||||
dcm = CPDecimalMakeZero();
|
||||
c = CPDecimalCompare(dcm1,dcm);
|
||||
[self assert:CPOrderedDescending equals:c message:"CPDecimalCompare(): (0.0001, 0) should be descending"];
|
||||
|
||||
dcm1 = CPDecimalMakeZero();
|
||||
dcm = CPDecimalMakeWithString(@"0.0001");
|
||||
c = CPDecimalCompare(dcm1,dcm);
|
||||
[self assert:CPOrderedAscending equals:c message:"CPDecimalCompare(): (0, 0.0001) should be ascending"];
|
||||
|
||||
dcm1 = CPDecimalMakeWithString(@"-0.0001");
|
||||
dcm = CPDecimalMakeZero();
|
||||
c = CPDecimalCompare(dcm1,dcm);
|
||||
[self assert:CPOrderedAscending equals:c message:"CPDecimalCompare(): (-0.0001, 0) should be ascending"];
|
||||
|
||||
dcm1 = CPDecimalMakeZero();
|
||||
dcm = CPDecimalMakeWithString(@"-0.0001");
|
||||
c = CPDecimalCompare(dcm1,dcm);
|
||||
[self assert:CPOrderedDescending equals:c message:"CPDecimalCompare(): (0, -0.0001) should be descending"];
|
||||
}
|
||||
|
||||
- (void)testCompact
|
||||
|
||||
@@ -399,4 +399,13 @@
|
||||
[self assert:'@{\n @"key1": @[\n @"1",\n @"2",\n @"3"\n ],\n @"key2": @"This is a string",\n @"key3": @{\n @"another": @"object"\n }\n}' equals:[json_dict description]];
|
||||
}
|
||||
|
||||
- (void)testInitWithObjectsAndKeys
|
||||
{
|
||||
var dict = [[CPDictionary alloc] initWithObjectsAndKeys:@"Value1", @"Key1", nil, @"Key2", @"Value3", @"Key3"];
|
||||
|
||||
[self assert:2 equals:[dict count]];
|
||||
[self assert:@"Value1" equals:[dict objectForKey:@"Key1"]];
|
||||
[self assert:nil equals:[dict objectForKey:@"Key2"]]; // No key/value pair
|
||||
[self assert:@"Value3" equals:[dict objectForKey:@"Key3"]];
|
||||
}
|
||||
@end
|
||||
|
||||