Merge branch 'refs/heads/master' into nib2cib-cpbrowser-compat

This commit is contained in:
Andrew Hankinson
2013-02-15 17:40:01 -05:00
185 changed files with 24635 additions and 2884 deletions
-2
View File
@@ -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
+34 -44
View File
@@ -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
+14 -6
View File
@@ -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])
+74 -25
View File
@@ -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",
+18 -27
View File
@@ -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
+20 -13
View File
@@ -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"
+3 -3
View File
@@ -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
+537 -197
View File
@@ -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];
+26 -1
View File
@@ -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
+1 -1
View File
@@ -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];
+14 -25
View File
@@ -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
+11 -9
View File
@@ -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;
}
}
+75 -63
View File
@@ -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)
+8 -3
View File
@@ -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];
}
+4 -2
View File
@@ -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
+38 -2
View File
@@ -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
+5 -3
View File
@@ -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
+342 -79
View File
@@ -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";
-10
View File
@@ -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];
}
+7 -2
View File
@@ -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"];
+8
View File
@@ -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];
+24 -9
View File
@@ -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
+1 -1
View File
@@ -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
{
+87 -24
View File
@@ -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
+4 -4
View File
@@ -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";
+7 -28
View File
@@ -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
+4 -4
View File
@@ -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";
+44 -17
View File
@@ -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
+205 -27
View File
@@ -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
+34 -14
View File
@@ -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;
}
+1
View File
@@ -258,6 +258,7 @@ CPWebViewAppKitScrollMaxPollCount = 3;
};
win.addEventListener("DOMMouseScroll", scrollEventHandler, false);
win.addEventListener("wheel", scrollEventHandler, false);
}
}
+18 -2
View File
@@ -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)
+7 -18
View File
@@ -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];
+2 -1
View File
@@ -85,4 +85,5 @@ var _CPCibConnectorSourceKey = @"_CPCibConnectorSourceKey",
@end
// For backwards compatibility.
@implementation _CPCibConnector : CPCibConnector { } @end
@implementation _CPCibConnector : CPCibConnector
@end
+2 -24
View File
@@ -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];
}
+2 -2
View File
@@ -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
+5 -1
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

+35 -1
View File
@@ -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],
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 B

After

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 B

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 B

After

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 B

After

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 992 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

+58 -20
View File
@@ -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],
+8 -3
View File
@@ -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];
}
+15 -3
View File
@@ -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];
+3 -3
View File
@@ -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
{
+23
View File
@@ -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.
+353
View File
@@ -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
+3
View File
@@ -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);
}
+112 -53
View File
@@ -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";
+43 -20
View File
@@ -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]);
}
+1 -1
View File
@@ -300,7 +300,7 @@
var value = arguments[index];
if (value === nil)
break;
continue;
[self setObject:value forKey:arguments[index + 1]];
}
+16 -9
View File
@@ -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];
+6 -2
View File
@@ -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];
}
+16 -5
View File
@@ -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",
+7 -2
View File
@@ -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;
}
+1
View File
@@ -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"
+8
View File
@@ -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:");
+1 -1
View File
@@ -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)
+39
View File
@@ -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
{
+48
View File
@@ -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
+19
View File
@@ -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
+51
View File
@@ -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)
+27 -5
View File
@@ -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"];
+28 -2
View File
@@ -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
+9
View File
@@ -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

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