Merge branch 'refs/heads/master' into my_master

Conflicts:
	AppKit/CPSearchField.j
	AppKit/CPTableView.j
	AppKit/CPTextField.j
	Jakefile
	Tools/nib2cib/NSTableView.j
This commit is contained in:
Aparajita Fishman committed 2010-08-11 10:27:35 -04:00
commit 1e3958652a
62 files changed
+7807 -246

No files matched your search

+13 -6
View File
@@ -197,14 +197,21 @@
- (CPArray)arrangeObjects:(CPArray)objects
{
var sortedObjects = objects;
var filterPredicate = [self filterPredicate],
sortDescriptors = [self sortDescriptors];
if ([self filterPredicate])
sortedObjects = [sortedObjects filteredArrayUsingPredicate:[self filterPredicate]];
if ([self sortDescriptors])
sortedObjects = [sortedObjects sortedArrayUsingDescriptors:[self sortDescriptors]];
if (filterPredicate && sortDescriptors)
{
var sortedObjects = [objects filteredArrayUsingPredicate:filterPredicate];
[sortedObjects sortUsingDescriptors:sortDescriptors];
return sortedObjects;
}
else if (filterPredicate)
return [objects filteredArrayUsingPredicate:filterPredicate];
else if (sortDescriptors)
return [objects sortedArrayUsingDescriptors:sortDescriptors];
return sortedObjects;
return [objects copy];
}
- (void)rearrangeObjects
+1 -1
View File
@@ -452,7 +452,7 @@ var CPObjectControllerObjectClassNameKey = @"CPObjectControllerOb
[self didChangeValueForKey:keyPath];
}
[self removeObjectAtIndex:anIndex];
[super removeObjectAtIndex:anIndex];
}
- (void)addObject:(id)anObject
+12 -4
View File
@@ -624,7 +624,7 @@ CPOutlineViewDropOnItemIndex = -1;
if (_dropItem)
{
[_dropOperationFeedbackView blink];
[CPTimer scheduledTimerWithTimeInterval:.3 callback:objj_msgSend(self, "expandItem:", _dropItem) repeats:NO]; //[self expandItem:_dropItem];
[CPTimer scheduledTimerWithTimeInterval:.3 callback:objj_msgSend(self, "expandItem:", _dropItem) repeats:NO];
}
}
@@ -645,10 +645,18 @@ CPOutlineViewDropOnItemIndex = -1;
_shouldRetargetChildIndex = YES;
// set CPTableView's _retargetedDropRow based on retargetedItem and retargetedChildIndex
var retargetedItemInfo = (_retargetedItem !== nil) ? _itemInfosForItems[[_retargetedItem UID]] : _rootItemInfo,
retargetedChildItem = (_retargedChildIndex !== CPOutlineViewDropOnItemIndex) ? retargetedItemInfo.children[_retargedChildIndex] : _retargetedItem;
var retargetedItemInfo = (_retargetedItem !== nil) ? _itemInfosForItems[[_retargetedItem UID]] : _rootItemInfo;
_retargetedDropRow = [self rowForItem:retargetedChildItem];
if (_retargedChildIndex === [retargetedItemInfo.children count])
{
var retargetedChildItem = [retargetedItemInfo.children lastObject];
_retargetedDropRow = [self rowForItem:retargetedChildItem] + 1;
}
else
{
var retargetedChildItem = (_retargedChildIndex !== CPOutlineViewDropOnItemIndex) ? retargetedItemInfo.children[_retargedChildIndex] : _retargetedItem;
_retargetedDropRow = [self rowForItem:retargetedChildItem];
}
}
- (void)_draggingEnded
-1
View File
@@ -798,7 +798,6 @@ var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
{
if (self = [super initWithCoder:coder])
{
_recentsAutosaveName = [coder decodeObjectForKey:CPRecentsAutosaveNameKey];
_sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
_sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
+53 -50
View File
@@ -82,7 +82,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (int)selectedTag
{
return _segments[_selectedSegment].tag;
return [_segments[_selectedSegment] tag];
}
// Specifying the number of segments
@@ -183,7 +183,7 @@ CPSegmentSwitchTrackingMomentary = 2;
selected = NO;
for (; index < _segments.length; ++index)
if (_segments[index].selected)
if ([_segments[index] selected])
if (selected)
[self setSelected:NO forSegment:index];
else
@@ -195,7 +195,7 @@ CPSegmentSwitchTrackingMomentary = 2;
var index = 0;
for (; index < _segments.length; ++index)
if (_segments[index].selected)
if ([_segments[index] selected])
[self setSelected:NO forSegment:index];
}
}
@@ -217,7 +217,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
{
_segments[aSegment].width = aWidth;
[_segments[aSegment] setWidth:aWidth];
[self tileWithChangedSegment:aSegment];
}
@@ -229,7 +229,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (float)widthForSegment:(unsigned)aSegment
{
return _segments[aSegment].width;
return [_segments[aSegment] width];
}
/*!
@@ -240,9 +240,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
{
var segment = _segments[aSegment];
segment.image = anImage;
[_segments[aSegment] setImage:anImage];
[self tileWithChangedSegment:aSegment];
}
@@ -254,7 +252,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPImage)imageForSegment:(unsigned)aSegment
{
return _segments[aSegment].image;
return [_segments[aSegment] image];
}
/*!
@@ -265,9 +263,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
{
var segment = _segments[aSegment];
_segments[aSegment].label = aLabel;
[_segments[aSegment] setLabel:aLabel];
[self tileWithChangedSegment:aSegment];
}
@@ -279,7 +275,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPString)labelForSegment:(unsigned)aSegment
{
return _segments[aSegment].label;
return [_segments[aSegment] label];
}
/*!
@@ -290,7 +286,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
{
_segments[aSegment].menu = aMenu;
[_segments[aSegment] setMenu:aMenu];
}
/*!
@@ -300,7 +296,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPMenu)menuForSegment:(unsigned)aSegment
{
return _segments[aSegment].menu;
return [_segments[aSegment] menu];
}
/*!
@@ -315,10 +311,10 @@ CPSegmentSwitchTrackingMomentary = 2;
var segment = _segments[aSegment];
// If we're already in this state, bail.
if (segment.selected == isSelected)
if ([segment selected] == isSelected)
return;
segment.selected = isSelected;
[segment setSelected:isSelected];
_themeStates[aSegment] = isSelected ? CPThemeStateSelected : CPThemeStateNormal;
@@ -331,7 +327,7 @@ CPSegmentSwitchTrackingMomentary = 2;
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
{
_segments[oldSelectedSegment].selected = NO;
[_segments[oldSelectedSegment] setSelected:NO];
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
[self drawSegmentBezel:oldSelectedSegment highlight:NO];
@@ -352,7 +348,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (BOOL)isSelectedForSegment:(unsigned)aSegment
{
return _segments[aSegment].selected;
return [_segments[aSegment] selected];
}
/*!
@@ -363,6 +359,8 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment
{
[_segments[aSegment] setEnabled:isEnabled];
if (isEnabled)
_themeStates[aSegment] &= ~CPThemeStateDisabled;
else
@@ -379,7 +377,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (BOOL)isEnabledForSegment:(unsigned)aSegment
{
return !(_themeStates[aSegment] & CPThemeStateDisabled)
return [_segments[aSegment] enabled];
}
/*!
@@ -389,7 +387,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setTag:(int)aTag forSegment:(unsigned)aSegment
{
_segments[aSegment].tag = aTag;
[_segments[aSegment] setTag:aTag];
}
/*!
@@ -398,7 +396,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (int)tagForSegment:(unsigned)aSegment
{
return _segments[aSegment].tag;
return [_segments[aSegment] tag];
}
// Drawings
@@ -461,7 +459,7 @@ CPSegmentSwitchTrackingMomentary = 2;
else if (aName.indexOf("segment-bezel") === 0)
{
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
frame = CGRectCreateCopy(_segments[segment].frame);
frame = CGRectCreateCopy([_segments[segment] frame]);
if (segment === 0)
{
@@ -556,8 +554,8 @@ CPSegmentSwitchTrackingMomentary = 2;
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"segment-bezel-"+i];
[contentView setText:segment.label];
[contentView setImage:segment.image];
[contentView setText:[segment label]];
[contentView setImage:[segment image]];
[contentView setFont:[self valueForThemeAttribute:@"font" inState:themeState]];
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:themeState]];
@@ -568,9 +566,9 @@ CPSegmentSwitchTrackingMomentary = 2;
[contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:themeState]];
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling" inState:themeState]];
if (segment.image && segment.label)
if ([segment image] && [segment label])
[contentView setImagePosition:[self valueForThemeAttribute:@"image-position" inState:themeState]];
else if (segment.image)
else if ([segment image])
[contentView setImagePosition:CPImageOnly];
if (i == count - 1)
@@ -609,27 +607,32 @@ CPSegmentSwitchTrackingMomentary = 2;
return;
var segment = _segments[aSegment],
segmentWidth = segment.width,
segmentWidth = [segment width],
themeState = _themeStates[aSegment] | (_themeState & CPThemeStateDisabled),
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
font = [self valueForThemeAttribute:@"font" inState:themeState];
if (!segmentWidth)
{
if (segment.image && segment.label)
segmentWidth = [segment.label sizeWithFont:font].width + [segment.image size].width + contentInset.left + contentInset.right;
if ([segment image] && [segment label])
segmentWidth = [[segment label] sizeWithFont:font].width + [[segment image] size].width + contentInset.left + contentInset.right;
else if (segment.image)
segmentWidth = [segment.image size].width + contentInset.left + contentInset.right;
segmentWidth = [[segment image] size].width + contentInset.left + contentInset.right;
else if (segment.label)
segmentWidth = [segment.label sizeWithFont:font].width + contentInset.left + contentInset.right;
segmentWidth = [[segment label] sizeWithFont:font].width + contentInset.left + contentInset.right;
else
segmentWidth = 0.0;
}
var delta = segmentWidth - CGRectGetWidth(segment.frame);
var delta = segmentWidth - CGRectGetWidth([segment frame]);
if (!delta)
{
[self setNeedsLayout];
[self setNeedsDisplay:YES];
return;
}
// Update Contorl Size
var frame = [self frame];
@@ -637,15 +640,15 @@ CPSegmentSwitchTrackingMomentary = 2;
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
// Update Segment Width
segment.width = segmentWidth;
segment.frame = [self frameForSegment:aSegment];;
[segment setWidth:segmentWidth];
[segment setFrame:[self frameForSegment:aSegment]];
// Update Following Segments Widths
var index = aSegment + 1;
for (; index < _segments.length; ++index)
{
_segments[index].frame.origin.x += delta;
[_segments[index] frame].origin.x += delta;
[self drawSegmentBezel:index highlight:NO];
[self drawSegment:index highlight:NO];
@@ -698,12 +701,12 @@ CPSegmentSwitchTrackingMomentary = 2;
count = _segments.length;
while (count--)
if (CGRectContainsPoint(_segments[count].frame, aPoint))
if (CGRectContainsPoint([_segments[count] frame], aPoint))
return count;
if (_segments.length)
{
var adjustedLastFrame = CGRectCreateCopy(_segments[_segments.length - 1].frame);
var adjustedLastFrame = CGRectCreateCopy([_segments[_segments.length - 1] frame]);
adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x;
if (CGRectContainsPoint(adjustedLastFrame, aPoint))
@@ -844,7 +847,7 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
for (var i = 0; i < _segments.length; i++)
{
_themeStates[i] = _segments[i].selected ? CPThemeStateSelected : CPThemeStateNormal;
_themeStates[i] = [_segments[i] selected] ? CPThemeStateSelected : CPThemeStateNormal;
[self tileWithChangedSegment:i];
}
@@ -852,7 +855,7 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
remainingWidth = FLOOR(difference / _segments.length);
for (var i=0; i < _segments.length; i++)
[self setWidth:_segments[i].width + remainingWidth forSegment:i];
[self setWidth:[_segments[i] width] + remainingWidth forSegment:i];
[self tileWithChangedSegment:0];
}
@@ -875,15 +878,15 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
@implementation _CPSegmentItem : CPObject
{
CPImage image;
CPString label;
CPMenu menu;
BOOL selected;
BOOL enabled;
int tag;
int width;
CPImage image @accessors;
CPString label @accessors;
CPMenu menu @accessors;
BOOL selected @accessors;
BOOL enabled @accessors;
int tag @accessors;
int width @accessors;
CGRect frame;
CGRect frame @accessors;
}
- (id)init
@@ -894,8 +897,8 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
label = @"";
menu = nil;
selected = NO;
enabled = NO;
tag = 0;
enabled = YES;
tag = -1;
width = 0;
frame = CGRectMakeZero();
+26 -9
View File
@@ -47,6 +47,7 @@ var CPSplitViewHorizontalImage = nil,
int _currentDivider;
float _initialOffset;
float _preCollapsePosition;
CPString _originComponent;
CPString _sizeComponent;
@@ -293,7 +294,7 @@ var CPSplitViewHorizontalImage = nil,
if ([_delegate respondsToSelector:@selector(splitView:additionalEffectiveRectOfDividerAtIndex:)])
additionalRect = [_delegate splitView:self additionalEffectiveRectOfDividerAtIndex:anIndex];
return CGRectContainsPoint(effectiveRect, aPoint) ||
return CGRectContainsPoint(effectiveRect, aPoint) ||
(additionalRect && CGRectContainsPoint(additionalRect, aPoint)) ||
(buttonBarRect && CGRectContainsPoint(buttonBarRect, aPoint));
}
@@ -358,14 +359,14 @@ var CPSplitViewHorizontalImage = nil,
if ([_delegate splitView:self canCollapseSubview:_subviews[i]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
{
if ([self isSubviewCollapsed:_subviews[i]])
[self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
[self setPosition:_preCollapsePosition ? _preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
else
[self setPosition:minPosition ofDividerAtIndex:i];
}
else if ([_delegate splitView:self canCollapseSubview:_subviews[i+1]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i+1] forDoubleClickOnDividerAtIndex:i])
{
if ([self isSubviewCollapsed:_subviews[i+1]])
[self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
[self setPosition:_preCollapsePosition ? _preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
else
[self setPosition:maxPosition ofDividerAtIndex:i];
}
@@ -444,11 +445,19 @@ var CPSplitViewHorizontalImage = nil,
if (_currentDivider === i || (_currentDivider == CPNotFound && [self cursorAtPoint:point hitDividerAtIndex:i]))
{
var frame = [_subviews[i] frame],
startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent],
size = frame.size[_sizeComponent],
startPosition = frame.origin[_originComponent] + size,
canShrink = [self _realPositionForPosition:startPosition-1 ofDividerAtIndex:i] < startPosition,
canGrow = [self _realPositionForPosition:startPosition+1 ofDividerAtIndex:i] > startPosition,
cursor = [CPCursor arrowCursor];
if (size === 0)
canGrow = YES; // Subview is collapsed.
else if (!canShrink &&
[_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
[_delegate splitView:self canCollapseSubview:_subviews[i]])
canShrink = YES; // Subview is collapsible.
if (_isVertical && canShrink && canGrow)
cursor = [CPCursor resizeLeftRightCursor];
else if (_isVertical && canShrink)
@@ -531,10 +540,18 @@ var CPSplitViewHorizontalImage = nil,
viewB = _subviews[dividerIndex + 1],
frameB = [viewB frame];
_preCollapsePosition = 0;
var preSize = frameA.size[_sizeComponent];
frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
if (preSize !== 0 && frameA.size[_sizeComponent] === 0)
_preCollapsePosition = preSize;
[_subviews[dividerIndex] setFrame:frameA];
preSize = frameB.size[_sizeComponent];
frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
if (preSize !== 0 && frameB.size[_sizeComponent] === 0)
_preCollapsePosition = preSize;
frameB.origin[_originComponent] = realPosition + [self dividerThickness];
[_subviews[dividerIndex + 1] setFrame:frameB];
@@ -641,14 +658,14 @@ var CPSplitViewHorizontalImage = nil,
/*!
Set the button bar who's resize control should act as a control for this splitview.
Each divider can have at most one button bar assigned to it, and that button bar must be
Each divider can have at most one button bar assigned to it, and that button bar must be
a subview of one of the split view's subviews.
Calling this method with nil as the button bar will remove any currently assigned button bar
for the divider at that index. Indexes will not be adjusted as new subviews are added, so you
should usually call this method after adding all the desired subviews to the split view.
This method will automatically configure the hasResizeControl and resizeControlIsLeftAligned
This method will automatically configure the hasResizeControl and resizeControlIsLeftAligned
parameters of the button bar, and will override any currently set values.
*/
- (void)setButtonBar:(CPButtonBar)aButtonBar forDividerAtIndex:(unsigned)dividerIndex
@@ -669,7 +686,7 @@ var CPSplitViewHorizontalImage = nil,
}
if (view !== self)
[CPException raise:CPInvalidArgumentException
[CPException raise:CPInvalidArgumentException
reason:@"CPSplitView button bar must be a subview of the split view."];
var viewIndex = [[self subviews] indexOfObject:subview];
@@ -677,7 +694,7 @@ var CPSplitViewHorizontalImage = nil,
[aButtonBar setHasResizeControl:YES];
[aButtonBar setResizeControlIsLeftAligned:dividerIndex < viewIndex];
_buttonBars[dividerIndex] = aButtonBar;
_buttonBars[dividerIndex] = aButtonBar;
}
- (void)_postNotificationWillResize
@@ -714,7 +731,7 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
_DOMDividerElements = [];
_buttonBars = [aCoder decodeObjectForKey:CPSplitViewButtonBarsKey] || [];
_delegate = [aCoder decodeObjectForKey:CPSplitViewDelegateKey];
_isPaneSplitter = [aCoder decodeBoolForKey:CPSplitViewIsPaneSplitterKey];
+7 -5
View File
@@ -462,9 +462,9 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
CPTableColumnMinWidthKey = @"CPTableColumnMinWidthKey",
CPTableColumnMaxWidthKey = @"CPTableColumnMaxWidthKey",
CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey",
CPTableColumnIsHiddenkey = @"CPTableColumnIsHiddenKey",
CPSortDescriptorPrototypeKey = @"CPSortDescriptorPrototypeKey";
CPTableColumnIsHiddenkey = @"CPTableColumnIsHiddenKey";
CPTableColumnIsHiddenKey = @"CPTableColumnIsHiddenKey",
CPSortDescriptorPrototypeKey = @"CPSortDescriptorPrototypeKey",
CPTableColumnIsEditableKey = @"CPTableColumnIsEditableKey";
@implementation CPTableColumn (CPCoding)
@@ -486,7 +486,8 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
_resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
_isHidden = [aCoder decodeBoolForKey:CPTableColumnIsHiddenkey];
_isHidden = [aCoder decodeBoolForKey:CPTableColumnIsHiddenKey];
_isEditable = [aCoder decodeBoolForKey:CPTableColumnIsEditableKey];
_sortDescriptorPrototype = [aCoder decodeObjectForKey:CPSortDescriptorPrototypeKey];
}
@@ -506,7 +507,8 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
[aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
[aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
[aCoder encodeBool:_isHidden forKey:CPTableColumnIsHiddenkey];
[aCoder encodeBool:_isHidden forKey:CPTableColumnIsHiddenKey];
[aCoder encodeBool:_isEditable forKey:CPTableColumnIsEditableKey];
[aCoder encodeObject:_sortDescriptorPrototype forKey:CPSortDescriptorPrototypeKey];
}
+47 -43
View File
@@ -71,7 +71,7 @@
var inset = [self currentValueForThemeAttribute:@"text-inset"],
bounds = [self bounds];
[_textField setFrame:CGRectMake(inset.right, inset.top, bounds.size.width - inset.right - inset.left, bounds.size.height - inset.top - inset.bottom)];
[_textField setFrame:_CGRectMake(inset.right, inset.top, bounds.size.width - inset.right - inset.left, bounds.size.height - inset.top - inset.bottom)];
[_textField setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
[_textField setFont:[self currentValueForThemeAttribute:@"text-font"]];
[_textField setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
@@ -151,8 +151,8 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
@implementation CPTableHeaderView : CPView
{
CPPoint _mouseDownLocation;
CPPoint _previousTrackingLocation;
CGPoint _mouseDownLocation;
CGPoint _previousTrackingLocation;
int _activeColumn;
int _pressedColumn;
@@ -179,14 +179,15 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
- (void)_init
{
_mouseDownLocation = CPPointMakeZero();
_previousTrackingLocation = CPPointMakeZero();
_mouseDownLocation = _CGPointMakeZero();
_previousTrackingLocation = _CGPointMakeZero();
_activeColumn = -1;
_pressedColumn = -1;
_isResizing = NO;
_isDragging = NO;
_isTrackingColumn = NO;
_drawsColumnLines = YES;
_columnOldWidth = 0.0;
@@ -213,8 +214,8 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
var headerRect = [self bounds],
columnRect = [_tableView rectOfColumn:aColumnIndex];
headerRect.origin.x = CPRectGetMinX(columnRect);
headerRect.size.width = CPRectGetWidth(columnRect);
headerRect.origin.x = _CGRectGetMinX(columnRect);
headerRect.size.width = _CGRectGetWidth(columnRect);
return headerRect;
}
@@ -232,11 +233,11 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
- (CGRect)_cursorRectForColumn:(int)column
{
if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
return CGRectMakeZero();
return _CGRectMakeZero();
var rect = [self headerRectOfColumn:column];
rect.origin.x = CGRectGetMaxX(rect) - 5;
rect.origin.x = _CGRectGetMaxX(rect) - 5;
rect.size.width = 20;
return rect;
@@ -273,7 +274,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
currentLocation.x -= 5.0;
var columnIndex = [self columnAtPoint:currentLocation],
shouldResize = [self shouldResizeTableColumn:columnIndex at:CPPointMake(currentLocation.x + 5.0, currentLocation.y)];
shouldResize = [self shouldResizeTableColumn:columnIndex at:_CGPointMake(currentLocation.x + 5.0, currentLocation.y)];
if (type === CPLeftMouseUp)
{
@@ -317,7 +318,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
[self continueResizingTableColumn:_activeColumn at:currentLocation];
else
{
if (_activeColumn === columnIndex && CPRectContainsPoint([self headerRectOfColumn:columnIndex], currentLocation))
if (_activeColumn === columnIndex && _CGRectContainsPoint([self headerRectOfColumn:columnIndex], currentLocation))
{
if (_isTrackingColumn && _pressedColumn !== -1)
{
@@ -335,24 +336,24 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
[CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
}
- (void)startTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
- (void)startTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
[self _setPressedColumn:aColumnIndex];
}
- (BOOL)continueTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
- (BOOL)continueTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
if ([self _shouldDragTableColumn:aColumnIndex at:aPoint])
{
var columnRect = [self headerRectOfColumn:aColumnIndex],
offset = CPPointMakeZero(),
offset = _CGPointMakeZero(),
view = [_tableView _dragViewForColumn:aColumnIndex event:[CPApp currentEvent] offset:offset],
viewLocation = CPPointMakeZero();
viewLocation = _CGPointMakeZero();
viewLocation.x = ( CPRectGetMinX(columnRect) + offset.x ) + ( aPoint.x - _mouseDownLocation.x );
viewLocation.y = CPRectGetMinY(columnRect) + offset.y;
viewLocation.x = ( _CGRectGetMinX(columnRect) + offset.x ) + ( aPoint.x - _mouseDownLocation.x );
viewLocation.y = _CGRectGetMinY(columnRect) + offset.y;
[self dragView:view at:viewLocation offset:CPSizeMakeZero() event:[CPApp currentEvent]
[self dragView:view at:viewLocation offset:_CGSizeMakeZero() event:[CPApp currentEvent]
pasteboard:[CPPasteboard pasteboardWithName:CPDragPboard] source:self slideBack:YES];
return NO;
@@ -361,24 +362,24 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
return YES;
}
- (BOOL)_shouldStopTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
- (BOOL)_shouldStopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
return _isTrackingColumn && _activeColumn === aColumnIndex &&
CPRectContainsPoint([self headerRectOfColumn:aColumnIndex], aPoint);
_CGRectContainsPoint([self headerRectOfColumn:aColumnIndex], aPoint);
}
- (void)stopTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
- (void)stopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
[self _setPressedColumn:CPNotFound];
[self _updateResizeCursor:[CPApp currentEvent]];
}
- (BOOL)_shouldDragTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
- (BOOL)_shouldDragTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
return [_tableView allowsColumnReordering] && ABS(aPoint.x - _mouseDownLocation.x) >= 10.0;
}
- (CPRect)_headerRectOfLastVisibleColumn
- (CGRect)_headerRectOfLastVisibleColumn
{
var tableColumns = [_tableView tableColumns],
columnIndex = [tableColumns count];
@@ -394,7 +395,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
return nil;
}
- (void)_constrainDragView:(CPView)theDragView at:(CPPoint)aPoint
- (void)_constrainDragView:(CPView)theDragView at:(CGPoint)aPoint
{
var tableColumns = [_tableView tableColumns],
lastColumnRect = [self _headerRectOfLastVisibleColumn];
@@ -408,10 +409,10 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
frame.origin = [self convertPoint:frame.origin fromView:nil];
// This effectively clamps the value between the minimum and maximum
frame.origin.x = MAX(0.0, MIN(CGRectGetMinX(frame), CGRectGetMaxX(lastColumnRect) - CGRectGetWidth(activeColumnRect)));
frame.origin.x = MAX(0.0, MIN(_CGRectGetMinX(frame), _CGRectGetMaxX(lastColumnRect) - _CGRectGetWidth(activeColumnRect)));
// Make sure the column cannot move vertically
frame.origin.y = CPRectGetMinY(lastColumnRect);
frame.origin.y = _CGRectGetMinY(lastColumnRect);
// Convert the calculated origin back to the window coordinate system
frame.origin = [self convertPoint:frame.origin toView:nil];
@@ -430,7 +431,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
[_tableView _setDraggedColumn:_activeColumn];
}
- (void)draggedView:(CPView)aView beganAt:(CPPoint)aPoint
- (void)draggedView:(CPView)aView beganAt:(CGPoint)aPoint
{
_isDragging = YES;
@@ -440,7 +441,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
[self setNeedsDisplay:YES];
}
- (void)draggedView:(CPView)aView movedTo:(CPPoint)aPoint
- (void)draggedView:(CPView)aView movedTo:(CGPoint)aPoint
{
[self _constrainDragView:aView at:aPoint];
@@ -450,9 +451,9 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
var hoverPoint = CGPointCreateCopy(aPoint);
if (aPoint.x < _previousTrackingLocation.x)
hoverPoint = CGPointMake(CGRectGetMinX(dragWindowFrame), CGRectGetMinY(dragWindowFrame));
hoverPoint = _CGPointMake(_CGRectGetMinX(dragWindowFrame), _CGRectGetMinY(dragWindowFrame));
else if (aPoint.x > _previousTrackingLocation.x)
hoverPoint = CGPointMake(CGRectGetMaxX(dragWindowFrame), CGRectGetMinY(dragWindowFrame));
hoverPoint = _CGPointMake(_CGRectGetMaxX(dragWindowFrame), _CGRectGetMinY(dragWindowFrame));
// Convert the hover point from the global coordinate system to windows' coordinate system
hoverPoint = [[self window] convertGlobalToBase:hoverPoint];
@@ -464,7 +465,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
if (hoveredColumn !== -1)
{
var columnRect = [self headerRectOfColumn:hoveredColumn],
columnCenterPoint = [self convertPoint:CGPointMake(CGRectGetMidX(columnRect), CGRectGetMidY(columnRect)) fromView:self];
columnCenterPoint = [self convertPoint:CGPointMake(_CGRectGetMidX(columnRect), _CGRectGetMidY(columnRect)) fromView:self];
if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
@@ -486,7 +487,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
[self setNeedsDisplay:YES];
}
- (BOOL)shouldResizeTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
- (BOOL)shouldResizeTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
if (_isResizing)
return YES;
@@ -494,10 +495,10 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
if (_isTrackingColumn)
return NO;
return [_tableView allowsColumnResizing] && CPRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
return [_tableView allowsColumnResizing] && _CGRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
}
- (void)startResizingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
- (void)startResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
_isResizing = YES;
@@ -507,7 +508,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
[_tableView setDisableAutomaticResizing:YES];
}
- (void)continueResizingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
- (void)continueResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
newWidth = [tableColumn width] + aPoint.x - _previousTrackingLocation.x;
@@ -527,7 +528,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
}
}
- (void)stopResizingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
- (void)stopResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
{
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
[tableColumn _postDidResizeNotificationWithOldWidth:_columnOldWidth];
@@ -550,7 +551,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
mouseOverLocation = CGPointMake(mouseLocation.x - 5, mouseLocation.y),
overColumn = [self columnAtPoint:mouseOverLocation];
if (overColumn >= 0 && CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
if (overColumn >= 0 && _CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
{
var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
width = [tableColumn width];
@@ -638,25 +639,26 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
var columnIndex = columnsArray[columnArrayIndex],
columnToStroke = [self headerRectOfColumn:columnIndex];
columnMaxX = CGRectGetMaxX(columnToStroke);
columnMaxX = _CGRectGetMaxX(columnToStroke);
CGContextMoveToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(CGRectGetMinY(columnToStroke)));
CGContextAddLineToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(CGRectGetMaxY(columnToStroke)));
CGContextMoveToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMinY(columnToStroke)));
CGContextAddLineToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMaxY(columnToStroke)));
}
CGContextClosePath(context);
CGContextStrokePath(context);
if (_isDragging)
/*if (_isDragging)
{
CGContextSetFillColor(context, [CPColor grayColor]);
CGContextFillRect(context, [self headerRectOfColumn:_activeColumn])
}
}*/
}
@end
var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey",
CPTableHeaderViewDrawsColumnLines = @"CPTableHeaderViewDrawsColumnLines";
@implementation CPTableHeaderView (CPCoding)
@@ -666,6 +668,7 @@ var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
{
[self _init];
_tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
_drawsColumnLines = [aCoder decodeBoolForKey:CPTableHeaderViewDrawsColumnLines];
}
return self;
@@ -675,6 +678,7 @@ var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:_tableView forKey:CPTableHeaderViewTableViewKey];
[aCoder encodeBool:_drawsColumnLines forKey:CPTableHeaderViewDrawsColumnLines];
}
@end
+44 -27
View File
@@ -1444,31 +1444,30 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
if (!superview)
return;
var superviewSize = [superview bounds].size;
UPDATE_COLUMN_RANGES_IF_NECESSARY();
var count = NUMBER_OF_COLUMNS(),
visColumns = [[CPArray alloc] init],
columnToResize = nil,
totalWidth = 0,
i = 0;
for(; i < count; i++)
for (; i < count; i++)
{
if(![_tableColumns[i] isHidden])
var column = _tableColumns[i];
if (![column isHidden])
{
[visColumns addObject:i];
totalWidth += [_tableColumns[i] width] + _intercellSpacing.width;
if (!columnToResize)
columnToResize = column;
totalWidth += [column width] + _intercellSpacing.width;
}
}
count = [visColumns count];
//if there are rows
if (count > 0)
// If there is a visible column
if (columnToResize)
{
var columnToResize = _tableColumns[visColumns[0]],
newWidth = superviewSize.width - totalWidth;// - [columnToResize width];
var superviewSize = [superview bounds].size,
newWidth = superviewSize.width - totalWidth;
newWidth += [columnToResize width];
newWidth = MAX([columnToResize minWidth], newWidth);
@@ -1917,7 +1916,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
- (void)setIndicatorImage:(CPImage)anImage inTableColumn:(CPTableColumn)aTableColumn
{
if (aTableColumn)
[[aTableColumn headerView] _setIndicatorImage:anImage];
{
var headerView = [aTableColumn headerView];
if ([headerView respondsToSelector:@selector(_setIndicatorImage:)])
[headerView _setIndicatorImage:anImage];
}
}
- (CPImage)_tableHeaderSortImage
@@ -2016,13 +2019,13 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
*/
- (CPView)_dragViewForColumn:(int)theColumnIndex event:(CPEvent)theDragEvent offset:(CPPointPointer)theDragViewOffset
{
var dragView = [[CPView alloc] initWithFrame:CPRectMakeZero()];
var dragView = [[_CPColumnDragView alloc] initWithFrame:CPRectMakeZero()];
tableColumn = [[self tableColumns] objectAtIndex:theColumnIndex],
bounds = CPRectMake(0.0, 0.0, [tableColumn width], CPRectGetHeight([self _exposedRect]) + 23.0),
columnRect = [self rectOfColumn:theColumnIndex],
headerView = [tableColumn headerView];
headerView = [tableColumn headerView],
row = [_exposedRows firstIndex];
row = [_exposedRows firstIndex];
while (row !== CPNotFound)
{
var dataView = [self _newDataViewForRow:row tableColumn:tableColumn],
@@ -2045,7 +2048,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
var headerFrame = [headerView frame];
headerFrame.origin = CPPointMakeZero();
columnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:headerFrame];
var columnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:headerFrame];
[columnHeaderView setStringValue:[headerView stringValue]];
[columnHeaderView setThemeState:[headerView themeState]];
[dragView addSubview:columnHeaderView];
@@ -2481,15 +2484,6 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
[self drawBackgroundInClipRect:exposedRect];
[self drawGridInClipRect:exposedRect];
[self highlightSelectionInClipRect:exposedRect];
if (_draggedColumnIndex === -1)
return;
var context = [[CPGraphicsContext currentContext] graphicsPort],
columnRect = [self rectOfColumn:_draggedColumnIndex];
CGContextSetFillColor(context, [CPColor grayColor]);
CGContextFillRect(context, columnRect);
}
- (void)drawBackgroundInClipRect:(CGRect)aRect
@@ -3725,3 +3719,26 @@ var CPDropOperationIndicatorHeight = 8;
[CPTimer scheduledTimerWithTimeInterval:0.27 callback:showCallback repeats:NO];
}
@end
@implementation _CPColumnDragView : CPView
- (void)drawRect:(CGRect)aRect
{
var context = [[CPGraphicsContext currentContext] graphicsPort];
CGContextSetStrokeColor(context, [CPColor grayColor]);
var points = [
_CGPointMake(0.5, 0),
_CGPointMake(0.5, aRect.size.height)
];
CGContextStrokeLineSegments(context, points, 2);
points = [
_CGPointMake(aRect.size.width - 0.5, 0),
_CGPointMake(aRect.size.width - 0.5, aRect.size.height)
];
CGContextStrokeLineSegments(context, points, 2);
}
@end
+91 -71
View File
@@ -49,7 +49,7 @@ var CPTextFieldDOMInputElement = nil,
CPTextFieldCachedSelectStartFunction = nil,
CPTextFieldCachedDragFunction = nil,
CPTextFieldBlurFunction = nil;
#endif
var CPSecureTextFieldCharacter = "\u2022";
@@ -82,13 +82,13 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
BOOL _isSecure;
BOOL _drawsBackground;
CPColor _textFieldBackgroundColor;
id _placeholderString;
id _delegate;
CPString _textDidChangeValue;
// NS-style Display Properties
@@ -199,7 +199,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
}
CPTextFieldHandleBlur = function(anEvent)
{
{
CPTextFieldInputOwner = nil;
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
@@ -207,10 +207,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
//FIXME make this not onblur
CPTextFieldDOMInputElement.onblur = CPTextFieldBlurFunction;
CPTextFieldDOMStandardInputElement = CPTextFieldDOMInputElement;
}
if (CPFeatureIsCompatible(CPInputTypeCanBeChangedFeature))
{
if ([self isSecure])
@@ -237,14 +237,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
CPTextFieldDOMPasswordInputElement.onblur = CPTextFieldBlurFunction;
}
CPTextFieldDOMInputElement = CPTextFieldDOMPasswordInputElement;
}
else
{
CPTextFieldDOMInputElement = CPTextFieldDOMStandardInputElement;
}
return CPTextFieldDOMInputElement;
}
#endif
@@ -262,18 +262,25 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
}
return self;
}
#pragma mark Controlling Editability and Selectability
/*!
Sets whether or not the receiver text field can be edited
/*!
Sets whether or not the receiver text field can be edited. If NO, any
ongoing edit is ended.
*/
- (void)setEditable:(BOOL)shouldBeEditable
{
if (_isEditable === shouldBeEditable)
return;
_isEditable = shouldBeEditable;
// We only allow first responder status if the field is editable and enabled.
if (!shouldBeEditable && [[self window] firstResponder] === self)
[[self window] makeFirstResponder:nil];
}
/*!
@@ -284,6 +291,19 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
return _isEditable;
}
/*!
Sets whether the field reacts to events. If NO, any ongoing edit is
ended.
*/
- (void)setEnabled:(BOOL)shouldBeEnabled
{
[super setEnabled:shouldBeEnabled];
// We only allow first responder status if the field is editable and enabled.
if (!shouldBeEnabled && [[self window] firstResponder] === self)
[[self window] makeFirstResponder:nil];
}
/*!
Sets whether the field's text is selectable by the user.
@param aFlag \c YES makes the text selectable
@@ -346,7 +366,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)setBezelStyle:(CPTextFieldBezelStyle)aBezelStyle
{
var shouldBeRounded = aBezelStyle === CPTextFieldRoundedBezel;
if (shouldBeRounded)
[self setThemeState:CPTextFieldStateRounded];
else
@@ -392,9 +412,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
if (_drawsBackground == shouldDrawBackground)
return;
_drawsBackground = shouldDrawBackground;
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
@@ -415,9 +435,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
if (_textFieldBackgroundColor == aColor)
return;
_textFieldBackgroundColor = aColor;
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
@@ -480,24 +500,24 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
_DOMElement.appendChild(element);
window.setTimeout(function()
{
window.setTimeout(function()
{
element.focus();
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
CPTextFieldInputOwner = self;
}, 0.0);
element.value = [self stringValue];
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
CPTextFieldInputIsActive = YES;
if (document.attachEvent)
{
CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
CPTextFieldCachedDragFunction = [[self window] platformWindow]._DOMBodyElement.ondrag;
[[self window] platformWindow]._DOMBodyElement.ondrag = function () {};
[[self window] platformWindow]._DOMBodyElement.onselectstart = function () {};
}
@@ -523,10 +543,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
CPTextFieldInputResigning = YES;
element.blur();
if (!CPTextFieldInputDidBlur)
CPTextFieldBlurFunction();
CPTextFieldInputDidBlur = NO;
CPTextFieldInputResigning = NO;
@@ -536,14 +556,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
CPTextFieldInputIsActive = NO;
if (document.attachEvent)
{
{
[[self window] platformWindow]._DOMBodyElement.ondrag = CPTextFieldCachedDragFunction;
[[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
CPTextFieldCachedSelectStartFunction = nil;
CPTextFieldCachedDragFunction = nil;
}
#endif
//post CPControlTextDidEndEditingNotification
@@ -580,7 +600,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
CPTextFieldCachedDragFunction = [[self window] platformWindow]._DOMBodyElement.ondrag;
[[self window] platformWindow]._DOMBodyElement.ondrag = function () {};
[[self window] platformWindow]._DOMBodyElement.onselectstart = function () {};
}
@@ -599,7 +619,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (document.attachEvent)
{
[[self window] platformWindow]._DOMBodyElement.ondrag = CPTextFieldCachedDragFunction;
[[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
[[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
CPTextFieldCachedSelectStartFunction = nil
CPTextFieldCachedDragFunction = nil;
@@ -709,7 +729,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)setObjectValue:(id)aValue
{
[super setObjectValue:aValue];
#if PLATFORM(DOM)
if (CPTextFieldInputOwner === self || [[self window] firstResponder] === self)
@@ -737,7 +757,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
if (_placeholderString === aStringValue)
return;
_placeholderString = aStringValue;
// Only update things if we need to show the placeholder
@@ -758,17 +778,17 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
/*!
Size to fit has two behavior, depending on if the receiver is an editable text field or not.
For non-editable text fields (typically, a label), sizeToFit will change the frame of the
For non-editable text fields (typically, a label), sizeToFit will change the frame of the
receiver to perfectly fit the current text in stringValue in the current font, and respecting
the current theme values for content-inset, min-size, and max-size.
For editable text fields, sizeToFit will ONLY change the HEIGHT of the text field. It will not
change the width of the text field. You can use setFrameSize: with the current height to set the
width, and you can get the size of a string with [CPString sizeWithFont:].
For editable text fields, sizeToFit will ONLY change the HEIGHT of the text field. It will not
change the width of the text field. You can use setFrameSize: with the current height to set the
width, and you can get the size of a string with [CPString sizeWithFont:].
The logic behind this decision is that most of the time you do not know what content will be placed
in an editable text field, so you want to just choose a fixed width and leave it at that size.
in an editable text field, so you want to just choose a fixed width and leave it at that size.
However, since you don't know how tall it needs to be if you change the font, sizeToFit will still be
useful for making the textfield an appropriate height.
*/
@@ -802,7 +822,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
#if PLATFORM(DOM)
var element = [self _inputElement];
if (([self isEditable] || [self isSelectable]))
{
if ([[self window] firstResponder] === self)
@@ -867,14 +887,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
return CPMakeRange(0, 0);
// we wrap this in try catch because firefox will throw an exception in certain instances
try
try
{
var inputElement = [self _inputElement],
selectionStart = inputElement.selectionStart,
selectionEnd = inputElement.selectionEnd;
if ([selectionStart isKindOfClass:CPNumber])
return CPMakeRange(selectionStart, selectionEnd - selectionStart);
return CPMakeRange(selectionStart, selectionEnd - selectionStart);
// browsers which don't support selectionStart/selectionEnd (aka IE).
var theDocument = inputElement.ownerDocument || inputElement.document,
@@ -886,8 +906,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
range.setEndPoint('EndToStart', selectionRange);
return CPMakeRange(range.text.length, selectionRange.text.length);
}
}
catch (e)
}
catch (e)
{
// fall through to the return
}
@@ -902,7 +922,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
var inputElement = [self _inputElement];
try
try
{
if ([inputElement.selectionStart isKindOfClass:CPNumber])
{
@@ -915,7 +935,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
var theDocument = inputElement.ownerDocument || inputElement.document,
existingRange = theDocument.selection.createRange(),
range = inputElement.createTextRange();
if (range.inRange(existingRange))
{
range.collapse(true);
@@ -950,7 +970,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)setDelegate:(id)aDelegate
{
var defaultCenter = [CPNotificationCenter defaultCenter];
//unsubscribe the existing delegate if it exists
if (_delegate)
{
@@ -960,24 +980,24 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[defaultCenter removeObserver:_delegate name:CPTextFieldDidFocusNotification object:self];
[defaultCenter removeObserver:_delegate name:CPTextFieldDidBlurNotification object:self];
}
_delegate = aDelegate;
if ([_delegate respondsToSelector:@selector(controlTextDidBeginEditing:)])
[defaultCenter
addObserver:_delegate
selector:@selector(controlTextDidBeginEditing:)
name:CPControlTextDidBeginEditingNotification
object:self];
if ([_delegate respondsToSelector:@selector(controlTextDidChange:)])
[defaultCenter
addObserver:_delegate
selector:@selector(controlTextDidChange:)
name:CPControlTextDidChangeNotification
object:self];
if ([_delegate respondsToSelector:@selector(controlTextDidEndEditing:)])
[defaultCenter
addObserver:_delegate
@@ -1008,15 +1028,15 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (CGRect)contentRectForBounds:(CGRect)bounds
{
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
if (!contentInset)
return bounds;
bounds.origin.x += contentInset.left;
bounds.origin.y += contentInset.top;
bounds.size.width -= contentInset.left + contentInset.right;
bounds.size.height -= contentInset.top + contentInset.bottom;
return bounds;
}
@@ -1026,12 +1046,12 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (_CGInsetIsEmpty(bezelInset))
return bounds;
bounds.origin.x += bezelInset.left;
bounds.origin.y += bezelInset.top;
bounds.size.width -= bezelInset.left + bezelInset.right;
bounds.size.height -= bezelInset.top + bezelInset.bottom;
return bounds;
}
@@ -1039,10 +1059,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
if (aName === "bezel-view")
return [self bezelRectForBounds:[self bounds]];
else if (aName === "content-view")
return [self contentRectForBounds:[self bounds]];
return [super rectForEphemeralSubviewNamed:aName];
}
@@ -1053,19 +1073,19 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[view setHitTests:NO];
return view;
}
else
{
var view = [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
//[view setImagePosition:CPNoImage];
[view setHitTests:NO];
return view;
}
return [super createEphemeralSubviewNamed:aName];
}
@@ -1074,10 +1094,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:@"content-view"];
if (bezelView)
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"bezel-view"];
@@ -1087,7 +1107,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[contentView setHidden:[self hasThemeState:CPThemeStateEditing]];
var string = "";
if ([self hasThemeState:CPTextFieldStatePlaceholder])
string = [self placeholderString];
else
@@ -1159,7 +1179,7 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
[self setEditable:[aCoder decodeBoolForKey:CPTextFieldIsEditableKey]];
@@ -1174,7 +1194,7 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
[self setPlaceholderString:[aCoder decodeObjectForKey:CPTextFieldPlaceholderStringKey]];
}
return self;
}
@@ -1185,12 +1205,12 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeBool:_isEditable forKey:CPTextFieldIsEditableKey];
[aCoder encodeBool:_isSelectable forKey:CPTextFieldIsSelectableKey];
[aCoder encodeBool:_drawsBackground forKey:CPTextFieldDrawsBackgroundKey];
[aCoder encodeObject:_textFieldBackgroundColor forKey:CPTextFieldBackgroundColorKey];
[aCoder encodeInt:[self lineBreakMode] forKey:CPTextFieldLineBreakModeKey];
+12 -1
View File
@@ -1362,10 +1362,21 @@ CPTexturedBackgroundWindowMask
*/
- (void)center
{
if (_isFullPlatformWindow)
return;
var size = [self frame].size,
containerSize = [CPPlatform isBrowser] ? [_platformWindow contentBounds].size : [[self screen] visibleFrame].size;
[self setFrameOrigin:CGPointMake((containerSize.width - size.width) / 2.0, (containerSize.height - size.height) / 2.0)];
var origin = CGPointMake((containerSize.width - size.width) / 2.0, (containerSize.height - size.height) / 2.0);
if (origin.x < 0.0)
origin.x = 0.0;
if (origin.y < 0.0)
origin.y = 0.0;
[self setFrameOrigin:origin];
}
/*!
+39 -4
View File
@@ -136,6 +136,7 @@ var CPDOMEventGetClickCount,
//might be mac only, we should investigate futher later.
var KeyCodesToPrevent = {},
CharacterKeysToPrevent = {},
KeyCodesToAllow = {},
MozKeyCodeToKeyCodeMap = {
61: 187, // =, equals
59: 186 // ;, semicolon
@@ -144,6 +145,19 @@ var KeyCodesToPrevent = {},
KeyCodesToPrevent[CPKeyCodes.A] = YES;
KeyCodesToAllow[CPKeyCodes.F1] = YES;
KeyCodesToAllow[CPKeyCodes.F2] = YES;
KeyCodesToAllow[CPKeyCodes.F3] = YES;
KeyCodesToAllow[CPKeyCodes.F4] = YES;
KeyCodesToAllow[CPKeyCodes.F5] = YES;
KeyCodesToAllow[CPKeyCodes.F6] = YES;
KeyCodesToAllow[CPKeyCodes.F7] = YES;
KeyCodesToAllow[CPKeyCodes.F8] = YES;
KeyCodesToAllow[CPKeyCodes.F9] = YES;
KeyCodesToAllow[CPKeyCodes.F10] = YES;
KeyCodesToAllow[CPKeyCodes.F11] = YES;
KeyCodesToAllow[CPKeyCodes.F12] = YES;
KeyCodesToUnicodeMap[CPKeyCodes.BACKSPACE] = CPDeleteCharacter;
KeyCodesToUnicodeMap[CPKeyCodes.DELETE] = CPDeleteFunctionKey;
KeyCodesToUnicodeMap[CPKeyCodes.TAB] = CPTabCharacter;
@@ -634,10 +648,31 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
(aDOMEvent.altKey ? CPAlternateKeyMask : 0) |
(aDOMEvent.metaKey ? CPCommandKeyMask : 0);
//We want to stop propagation if this is a command key AND this character or keycode has been added to our blacklist
StopDOMEventPropagation = !!(!(modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) ||
CharacterKeysToPrevent[String.fromCharCode(aDOMEvent.keyCode || aDOMEvent.charCode).toLowerCase()] ||
KeyCodesToPrevent[aDOMEvent.keyCode]);
// With a few exceptions, all key events are blocked from propagating to
// the browser. Here the following exceptions are being allowed:
//
// - All keys pressed along with a ctrl or cmd key _unless_ they are in
// one of the two blacklists.
// - Any key listed in the whitelist.
//
// The ctrl/cmd keys are used for browser hotkeys as are the keys listed in
// the whitelist (F1-F12 at the time of writing).
//
// If a key is listed in both the blacklist and whitelist, the blacklist is
// checked first. The key will be blocked from propagating in that case.
StopDOMEventPropagation = YES;
// Make sure it is not in the blacklists.
if(! (CharacterKeysToPrevent[String.fromCharCode(aDOMEvent.keyCode || aDOMEvent.charCode).toLowerCase()] || KeyCodesToPrevent[aDOMEvent.keyCode]))
{
// It is not in the blacklist, let it through if the ctrl/cmd key is
// also down or it's in the whitelist.
if((modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) || KeyCodesToAllow[aDOMEvent.keyCode])
{
StopDOMEventPropagation = NO;
}
}
var isNativePasteEvent = NO,
isNativeCopyOrCutEvent = NO,
+59 -15
View File
@@ -26,6 +26,9 @@
@import "CPRange.j"
@import "CPSortDescriptor.j"
CPEnumerationNormal = 0;
CPEnumerationConcurrent = 1 << 0;
CPEnumerationReverse = 1 << 1;
CPEnumerationNormal = 0;
CPEnumerationConcurrent = 1 << 0;
@@ -604,6 +607,12 @@ CPEnumerationReverse = 1 << 1;
- (unsigned)insertObject:(id)anObject inArraySortedByDescriptors:(CPArray)descriptors
{
if (!descriptors || ![descriptors count])
{
[self addObject:anObject];
return [self count] - 1;
}
var index = [self _insertObject:anObject sortedByFunction:function(lhs, rhs)
{
var i = 0,
@@ -1312,19 +1321,7 @@ CPEnumerationReverse = 1 << 1;
- (CPArray)sortUsingDescriptors:(CPArray)descriptors
{
var count = [descriptors count];
sort(function(lhs, rhs)
{
var i = 0,
result = CPOrderedSame;
while (i < count)
if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame)
return result;
return result;
});
[self sortUsingFunction:compareObjectsUsingDescriptors context:descriptors];
}
/*!
@@ -1334,7 +1331,36 @@ CPEnumerationReverse = 1 << 1;
*/
- (void)sortUsingFunction:(Function)aFunction context:(id)aContext
{
sort(function(lhs, rhs) { return aFunction(lhs, rhs, aContext); });
var h, i, j, k, l, m, n = [self count];
var A, B = [];
for (h = 1; h < n; h += h)
{
for (m = n - 1 - h; m >= 0; m -= h + h)
{
l = m - h + 1;
if (l < 0)
l = 0;
for (i = 0, j = l; j <= m; i++, j++)
B[i] = self[j];
for (i = 0, k = l; k < j && j <= m + h; k++)
{
A = self[j];
if (aFunction(A, B[i], aContext) == CPOrderedDescending)
self[k] = B[i++];
else
{
self[k] = A;
j++;
}
}
while (k < j)
self[k++] = B[i++];
}
}
}
/*!
@@ -1343,11 +1369,29 @@ CPEnumerationReverse = 1 << 1;
*/
- (void)sortUsingSelector:(SEL)aSelector
{
sort(function(lhs, rhs) { return objj_msgSend(lhs, aSelector, rhs); });
[self sortUsingFunction:selectorCompare context:aSelector];
}
@end
var selectorCompare = function selectorCompare(object1, object2, selector)
{
return [object1 performSelector:selector withObject:object2];
}
// sort using sort descriptors
var compareObjectsUsingDescriptors= function compareObjectsUsingDescriptors(lhs, rhs, descriptors)
{
var result,
i = 0,
n = [descriptors count];
while (i < n && result == CPOrderedSame);
result = [descriptors[i++] compareObject:lhs withObject:rhs];
return result;
}
@implementation CPArray (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
File diff suppressed because it is too large. Load diff
+7
View File
@@ -160,6 +160,13 @@
return YES;
}
- (BOOL)isEqual:(id)anObject
{
return self === anObject ||
[anObject isKindOfClass:[self class]] &&
[self isEqualToIndexSet:anObject];
}
/*!
Returns \c YES if the index set contains the specified index.
@param anIndex the index to check for in the set
+9
View File
@@ -44,6 +44,7 @@ var CPNullSharedNull = nil;
return [super alloc];
}*/
/*!
Returns the singleton instance of the CPNull
object. While CPNull and \c nil should
@@ -57,6 +58,14 @@ var CPNullSharedNull = nil;
return CPNullSharedNull;
}
- (BOOL)isEqual:(id)anObject
{
if (self === anObject)
return YES;
return [anObject isKindOfClass:[CPNull class]];
}
/*!
Returns CPNull null.
@param aCoder the coder from which to do nothing
@@ -0,0 +1,605 @@
@import "CPArray.j"
@import "CPNull.j"
@import "CPString.j"
@import "CPEnumerator.j"
@import "CPPredicate.j"
@import "CPExpression.j"
@import "CPExpression_operator.j"
/*!
A predicate to compare directly the left and right hand sides.
@global
@class CPComparisonPredicate
*/
CPDirectPredicateModifier = 0;
/*!
A predicate to compare all entries in the destination of a to-many relationship.
The left hand side must be a collection. The corresponding predicate compares each value in the left hand side with the right hand side, and returns NO when it finds the first mismatch—or YES if all match.
@global
@class CPComparisonPredicate
*/
CPAllPredicateModifier = 1;
/*!
A predicate to match with any entry in the destination of a to-many relationship.
The left hand side must be a collection. The corresponding predicate compares each value in the left hand side against the right hand side and returns YES when it finds the first match—or NO if no match is found.
@global
@class CPComparisonPredicate
*/
CPAnyPredicateModifier = 2;
/*!
A case-insensitive predicate.
@global
@class CPComparisonPredicate
*/
CPCaseInsensitivePredicateOption = 1;
/*!
A diacritic-insensitive predicate.
@global
@class CPComparisonPredicate
*/
CPDiacriticInsensitivePredicateOption = 2;
CPDiacriticInsensitiveSearch = 128;
/*!
A less-than predicate.
@global
@class CPComparisonPredicate
*/
CPLessThanPredicateOperatorType = 0;
/*!
A less-than-or-equal-to predicate.
@global
@class CPComparisonPredicate
*/
CPLessThanOrEqualToPredicateOperatorType = 1;
/*!
A greater-than predicate.
@global
@class CPComparisonPredicate
*/
CPGreaterThanPredicateOperatorType = 2;
/*!
A greater-than-or-equal-to predicate.
@global
@class CPComparisonPredicate
*/
CPGreaterThanOrEqualToPredicateOperatorType = 3;
/*!
An equal-to predicate.
@global
@class CPComparisonPredicate
*/
CPEqualToPredicateOperatorType = 4;
/*!
A not-equal-to predicate.
@global
@class CPComparisonPredicate
*/
CPNotEqualToPredicateOperatorType = 5;
/*!
A full regular expression matching predicate.
@global
@class CPComparisonPredicate
*/
CPMatchesPredicateOperatorType = 6;
/*!
A simple subset of the matches predicate, similar in behavior to SQL LIKE.
@global
@class CPComparisonPredicate
*/
CPLikePredicateOperatorType = 7;
/*!
A begins-with predicate.
@global
@class CPComparisonPredicate
*/
CPBeginsWithPredicateOperatorType = 8;
/*!
An ends-with predicate.
@global
@class CPComparisonPredicate
*/
CPEndsWithPredicateOperatorType = 9;
/*!
A predicate to determine if the left hand side is in the right hand side.
For strings, returns YES if the left hand side is a substring of the right hand side . For collections, returns YES if the left hand side is in the right hand side.
@global
@class CPComparisonPredicate
*/
CPInPredicateOperatorType = 10;
/*!
Predicate that uses a custom selector that takes a single argument and returns a BOOL value.
The selector is invoked on the left hand side with the right hand side.
@global
@class CPComparisonPredicate
*/
CPCustomSelectorPredicateOperatorType = 11;
/*!
A predicate to determine if the left hand side contains the right hand side.
Returns YES if [lhs contains rhs]; the left hand side must be a CPExpression object that evaluates to a collection
@global
@class CPComparisonPredicate
*/
CPContainsPredicateOperatorType = 99;
/*!
A predicate to determine if the right hand side lies between bounds specified by the left hand side.
Returns YES if [lhs between rhs]; the right hand side must be an array in which the first element sets the lower bound and the second element the upper, inclusive. Comparison is performed using compare: or the class-appropriate equivalent.
@global
@class CPComparisonPredicate
*/
CPBetweenPredicateOperatorType = 100;
var CPComparisonPredicateModifier;
var CPPredicateOperatorType;
/*!
@ingroup foundation
@class CPComparisonPredicate
@brief CPComparisonPredicate is a subclass of CPPredicate used to compare expressions.
Comparison predicates are predicates used to compare the results of two expressions. Comparison predicates take an operator, a left expression, and a right expression, and return as a BOOL the result of invoking the operator with the results of evaluating the expressions. Expressions are represented by instances of the CPExpression class.
*/
@implementation CPComparisonPredicate : CPPredicate
{
CPExpression _left;
CPExpression _right;
CPComparisonPredicateModifier _modifier;
CPPredicateOperatorType _type;
unsigned int _options;
SEL _customSelector;
}
// Constructors
/*!
Returns a new predicate formed by combining the left and right expressions using a given selector.
@param left The left hand side expression.
@param right The right hand side expression.
@param selector The selector to use for comparison. The method defined by the selector must take a single argument and return a BOOL value.
@return A new predicate formed by combining the left and right expressions using selector.
*/
+ (CPPredicate)predicateWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right customSelector:(SEL)selector
{
return [[self alloc] initWithLeftExpression:left rightExpression:right customSelector:selector];
}
/*!
Creates and returns a predicate of a given type formed by combining given left and right expressions using a given modifier and options.
@param left The left hand expression.
@param right The right hand expression.
@param modifier The modifier to apply.
@param type The predicate operator type.
@param options The options to apply (see CPComparisonPredicate Options).
@return A new predicate of type type formed by combining the given left and right expressions using the modifier and options.
*/
+ (CPPredicate)predicateWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right modifier:(CPComparisonPredicateModifier)modifier type:(int)type options:(unsigned)options
{
return [[self alloc] initWithLeftExpression:left rightExpression:right modifier:modifier type:type options:options];
}
/*!
Initializes a predicate formed by combining given left and right expressions using a given selector.
@param left The left hand side expression.
@param right The right hand side expression.
@param selector The selector to use for comparison. The method defined by the selector must take a single argument and return a BOOL value.
@return The receiver, initialized by combining the left and right expressions using selector.
*/
- (id)initWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right customSelector:(SEL)selector
{
_left = left;
_right = right;
_modifier = CPDirectPredicateModifier;
_type = CPCustomSelectorPredicateOperatorType;
_options = 0;
_customSelector = selector;
return self;
}
/*!
Initializes a predicate to a given type formed by combining given left and right expressions using a given modifier and options.
@param left The left hand expression.
@param right The right hand expression.
@param modifier The modifier to apply.
@param type The predicate operator type.
@param options The options to apply (see CPComparisonPredicate Options).
@return The receiver, initialized to a predicate of type type formed by combining the left and right expressions using the modifier and options.
*/
- (id)initWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right modifier:(CPComparisonPredicateModifier)modifier type:(CPPredicateOperatorType)type options:(unsigned)options
{
_left = left;
_right = right;
_modifier = modifier;
_type = type;
_options = (type != CPMatchesPredicateOperatorType &&
type != CPLikePredicateOperatorType &&
type != CPBeginsWithPredicateOperatorType &&
type != CPEndsWithPredicateOperatorType &&
type != CPInPredicateOperatorType &&
type != CPContainsPredicateOperatorType) ? 0 : options;
_customSelector = NULL;
return self;
}
// Getting Information About a Comparison Predicate
/*!
Returns the comparison predicate modifier for the receiver.
@return The comparison predicate modifier for the receiver.
*/
- (CPComparisonPredicateModifier)comparisonPredicateModifier
{
return _modifier;
}
/*!
Returns the selector for the receiver.
@return The selector for the receiver, or NULL if there is none.
*/
- (SEL)customSelector
{
return _customSelector;
}
/*!
Returns the left expression for the receiver.
@return The left expression for the receiver, or nil if there is none.
*/
- (CPExpression)leftExpression
{
return _left;
}
/*!
Returns the options that are set for the receiver.
@return The options that are set for the receiver.
*/
- (unsigned)options
{
return _options;
}
/*!
Returns the predicate type for the receiver.
@return Returns the predicate type for the receiver.
*/
- (CPPredicateOperatorType)predicateOperatorType
{
return _type;
}
/*!
Returns the right expression for the receiver.
@return The right expression for the receiver, or nil if there is none.
*/
- (CPExpression)rightExpression
{
return _right;
}
- (CPString)predicateFormat
{
var modifier;
switch (_modifier)
{
case CPDirectPredicateModifier:
modifier = "";
break;
case CPAllPredicateModifier:
modifier = "ALL ";
break;
case CPAnyPredicateModifier:
modifier = "ANY ";
break;
default:
modifier = "";
break;
}
var options;
switch (_options)
{
case CPCaseInsensitivePredicateOption:
options = "[c]";
break;
case CPDiacriticInsensitivePredicateOption:
options = "[d]";
break;
case CPCaseInsensitivePredicateOption | CPDiacriticInsensitivePredicateOption:
options = "[cd]";
break;
default:
options = "";
break;
}
var operator;
switch (_type)
{
case CPLessThanPredicateOperatorType:
operator = "<";
break;
case CPLessThanOrEqualToPredicateOperatorType:
operator = "<=";
break;
case CPGreaterThanPredicateOperatorType:
operator = ">";
break;
case CPGreaterThanOrEqualToPredicateOperatorType:
operator = ">=";
break;
case CPEqualToPredicateOperatorType:
operator = "==";
break;
case CPNotEqualToPredicateOperatorType:
operator = "!=";
break;
case CPMatchesPredicateOperatorType:
operator = "MATCHES";
break;
case CPLikePredicateOperatorType:
operator = "LIKE";
break;
case CPBeginsWithPredicateOperatorType:
operator = "BEGINSWITH";
break;
case CPEndsWithPredicateOperatorType:
operator = "ENDSWITH";
break;
case CPInPredicateOperatorType:
operator = "IN";
break;
case CPContainsPredicateOperatorType:
operator = "CONTAINS";
break;
case CPCustomSelectorPredicateOperatorType:
operator = CPStringFromSelector(_customSelector);
break;
}
return [CPString stringWithFormat:@"%s%s %s%s %s",modifier,[_left description],operator,options,[_right description]];
}
- (CPPredicate)predicateWithSubstitutionVariables:(CPDictionary)variables
{
var left = [_left _expressionWithSubstitutionVariables:variables],
right = [_right _expressionWithSubstitutionVariables:variables];
if (_type != CPCustomSelectorPredicateOperatorType)
return [CPComparisonPredicate predicateWithLeftExpression:left rightExpression:right modifier:_modifier type:_type options:_options];
else
return [CPComparisonPredicate predicateWithLeftExpression:left rightExpression:right customSelector:_customSelector];
}
- (BOOL)_evaluateValue:lhs rightValue:rhs
{
var leftIsNil = (lhs == nil || [lhs isEqual:[CPNull null]]),
rightIsNil = (rhs == nil || [rhs isEqual:[CPNull null]]);
if ((leftIsNil || rightIsNil) && _type != CPCustomSelectorPredicateOperatorType)
return (leftIsNil == rightIsNil &&
(_type == CPEqualToPredicateOperatorType ||
_type == CPLessThanOrEqualToPredicateOperatorType ||
_type == CPGreaterThanOrEqualToPredicateOperatorType));
var string_compare_options = 0;
// left and right should be casted first [CAST()] following 10.5 rules.
switch (_type)
{
case CPLessThanPredicateOperatorType:
return ([lhs compare:rhs] == CPOrderedAscending);
case CPLessThanOrEqualToPredicateOperatorType:
return ([lhs compare:rhs] != CPOrderedDescending);
case CPGreaterThanPredicateOperatorType:
return ([lhs compare:rhs] == CPOrderedDescending);
case CPGreaterThanOrEqualToPredicateOperatorType:
return ([lhs compare:rhs] != CPOrderedAscending);
case CPEqualToPredicateOperatorType:
return [lhs isEqual:rhs];
case CPNotEqualToPredicateOperatorType:
return (![lhs isEqual:rhs]);
case CPMatchesPredicateOperatorType:
var commut = (_options & CPCaseInsensitivePredicateOption) ? "gi":"g";
if (_options & CPDiacriticInsensitivePredicateOption)
{
lhs = lhs.stripDiacritics();
rhs = rhs.stripDiacritics();
}
return (new RegExp(rhs,commut)).test(lhs);
case CPLikePredicateOperatorType:
if (_options & CPDiacriticInsensitivePredicateOption)
{
lhs = lhs.stripDiacritics();
rhs = rhs.stripDiacritics();
}
var commut = (_options & CPCaseInsensitivePredicateOption) ? "gi":"g";
var reg = new RegExp(rhs.escapeForRegExp(),commut);
return reg.test(lhs);
case CPBeginsWithPredicateOperatorType:
var range = CPMakeRange(0,[rhs length]);
if (_options & CPCaseInsensitivePredicateOption) string_compare_options |= CPCaseInsensitiveSearch;
if (_options & CPDiacriticInsensitivePredicateOption) string_compare_options |= CPDiacriticInsensitiveSearch;
return ([lhs compare:rhs options:string_compare_options range:range] == CPOrderedSame);
case CPEndsWithPredicateOperatorType:
var range = CPMakeRange([lhs length] - [rhs length],[rhs length]);
if (_options & CPCaseInsensitivePredicateOption) string_compare_options |= CPCaseInsensitiveSearch;
if (_options & CPDiacriticInsensitivePredicateOption) string_compare_options |= CPDiacriticInsensitiveSearch;
return ([lhs compare:rhs options:string_compare_options range:range] == CPOrderedSame);
case CPInPredicateOperatorType:
// Handle special case where rhs is a collection and lhs an element of it.
if (![rhs isKindOfClass: [CPString class]])
{
if (![rhs respondsToSelector: @selector(objectEnumerator)])
[CPException raise:CPInvalidArgumentException reason:@"The right hand side for an IN operator must be a collection"];
var e = [rhs objectEnumerator],
value;
while (value = [e nextObject])
if ([value isEqual:lhs])
return YES;
return NO;
}
if (_options & CPCaseInsensitivePredicateOption)
string_compare_options |= CPCaseInsensitiveSearch;
if (_options & CPDiacriticInsensitivePredicateOption)
string_compare_options |= CPDiacriticInsensitiveSearch;
return ([rhs rangeOfString:lhs options:string_compare_options].location != CPNotFound);
case CPCustomSelectorPredicateOperatorType:
return [lhs performSelector:_customSelector withObject:rhs];
case CPContainsPredicateOperatorType:
if (![lhs isKindOfClass: [CPString class]])
{
if (![lhs respondsToSelector: @selector(objectEnumerator)])
[CPException raise:CPInvalidArgumentException reason:@"The left hand side for a CONTAINS operator must be a collection or a string"];
var e = [lhs objectEnumerator],
value;
while (value = [e nextObject])
if ([value isEqual:rhs])
return YES;
return NO;
}
if (_options & CPCaseInsensitivePredicateOption)
string_compare_options |= CPCaseInsensitiveSearch;
if (_options & CPDiacriticInsensitivePredicateOption)
string_compare_options |= CPDiacriticInsensitiveSearch;
return ([lhs rangeOfString:rhs options:string_compare_options].location != CPNotFound);
case CPBetweenPredicateOperatorType:
if ([lhs count] < 2)
[CPException raise:CPInvalidArgumentException reason:@"The right hand side for a BETWEEN operator must contain 2 objects"];
var lower = [rhs objectAtIndex:0],
upper = [rhs objectAtIndex:1];
return ([lhs compare:lower] == CPOrderedDescending && [lhs compare:upper] == CPOrderedAscending);
default:
return NO;
}
}
- (BOOL)evaluateWithObject:(id)object
{
return [self evaluateWithObject:object substitutionVariables:nil];
}
- (BOOL)evaluateWithObject:(id)object substitutionVariables:(CPDictionary)variables
{
var left = _left,
right = _right;
if (variables != nil)
{
left = [left _expressionWithSubstitutionVariables:variables];
right = [right _expressionWithSubstitutionVariables:variables];
}
var leftValue = [left expressionValueWithObject:object context:nil],
rightValue = [right expressionValueWithObject:object context:nil];
if (_modifier == CPDirectPredicateModifier)
return [self _evaluateValue:leftValue rightValue:rightValue];
else
{
if (![leftValue respondsToSelector:@selector(objectEnumerator)])
[CPException raise:CPInvalidArgumentException reason:@"The left hand side for an ALL or ANY operator must be either a CPArray or a CPSet"];
var e = [leftValue objectEnumerator],
result = (_modifier == CPAllPredicateModifier),
value;
while (value = [e nextObject])
{
var eval = [self _evaluateValue:value rightValue:rightValue];
if (eval != result)
return eval;
}
return result;
}
}
@end
@implementation CPComparisonPredicate (CPCoding)
- (id)initWithCoder:(CPCoder)coder
{
self = [super init];
if (self != nil)
{
_left = [coder decodeObjectForKey:@"CPComparisonPredicateLeftExpression"];
_right = [coder decodeObjectForKey:@"CPComparisonPredicateRightExpression"];
_modifier = [coder decodeIntForKey:@"CPComparisonPredicateModifier"];
_type = [coder decodeIntForKey:@"CPComparisonPredicateType"];
_options = [coder decodeIntForKey:@"CPComparisonPredicateOptions"];
_customSelector = [coder decodeObjectForKey:@"CPComparisonPredicateCustomSelector"];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:_left forKey:@"CPComparisonPredicateLeftExpression"];
[coder encodeObject:_right forKey:@"CPComparisonPredicateRightExpression"];
[coder encodeInt:_modifier forKey:@"CPComparisonPredicateModifier"];
[coder encodeInt:_type forKey:@"CPComparisonPredicateType"];
[coder encodeInt:_options forKey:@"CPComparisonPredicateOptions"];
[coder encodeObject:_customSelector forKey:@"CPComparisonPredicateCustomSelector"];
}
@end
var source = ['*','?','(',')','{','}','.','+','|','/','$','^'];
var dest = ['.*','.?','\\(','\\)','\\{','\\}','\\.','\\+','\\|','\\/','\\$','\\^'];
String.prototype.escapeForRegExp = function()
{
var foundChar = false;
for (var i = 0; i < source.length; ++i)
{
if (this.indexOf(source[i]) !== -1)
{
foundChar = true;
break;
}
}
if (!foundChar)
return this;
var result = "",
sourceIndex;
for (var i = 0; i < this.length; ++i)
{
var sourceIndex = source.indexOf(this.charAt(i));
if (sourceIndex !== -1)
result += dest[sourceIndex];
else
result += this.charAt(i);
}
return result;
}
@@ -0,0 +1,227 @@
@import "CPPredicate.j"
@import <Foundation/CPArray.j>
@import <Foundation/CPString.j>
/*!
A predicate to compare directly the left and right hand sides.
@global
@class CPCompoundPredicate
*/
CPNotPredicateType = 0;
/*!
A predicate to compare directly the left and right hand sides.
@global
@class CPCompoundPredicate
*/
CPAndPredicateType = 1;
/*!
A predicate to compare directly the left and right hand sides.
@global
@class CPCompoundPredicate
*/
CPOrPredicateType = 2;
var CPCompoundPredicateType;
/*!
@class CPCompoundPredicate
@ingroup foundation
@brief CPCompoundPredicate is a subclass of CPPredicate used to represent logical “gate” operations (AND/OR/NOT) and comparison operations.
Comparison operations are based on two expressions, as represented by instances of the CPExpression class. Expressions are created for constant values, key paths, and so on.
A compound predicate with 0 elements evaluates to TRUE, and a compound predicate with a single sub-predicate evaluates to the truth of its sole subpredicate.
*/
@implementation CPCompoundPredicate : CPPredicate
{
CPCompoundPredicateType _type;
CPArray _predicates;
}
// Constructors
/*!
Returns the receiver initialized to a given type using predicates from a given array.
@param type The type of the new predicate.
@return The receiver initialized with its type set to type and subpredicates array to subpredicates.
*/
- (id)initWithType:(CPCompoundPredicateType)type subpredicates:(CPArray)predicates
{
_type = type;
_predicates = predicates;
return self;
}
/*!
Returns a new predicate formed by NOT-ing the predicates in a given array.
@param subpredicates An array of CPPredicate objects.
@return A new predicate formed by NOT-ing the predicates specified by subpredicates.
*/
+ (CPPredicate)notPredicateWithSubpredicate:(CPPredicate)predicate
{
return [[self alloc] initWithType:CPNotPredicateType subpredicates:[CPArray arrayWithObject:predicate]];
}
/*!
Returns a new predicate formed by AND-ing the predicates in a given array.
@param subpredicates An array of CPPredicate objects.
@return A new predicate formed by AND-ing the predicates specified by subpredicates.
*/
+ (CPPredicate)andPredicateWithSubpredicates:(CPArray)subpredicates
{
return [[self alloc] initWithType:CPAndPredicateType subpredicates:subpredicates];
}
/*!
Returns a new predicate formed by OR-ing the predicates in a given array.
@param subpredicates An array of CPPredicate objects.
@return A new predicate formed by OR-ing the predicates specified by subpredicates.
*/
+ (CPPredicate)orPredicateWithSubpredicates:(CPArray)predicates
{
return [[self alloc] initWithType:CPOrPredicateType subpredicates:predicates];
}
// Getting Information About a Compound Predicate
/*!
Returns the predicate type for the receiver.
@return The predicate type for the receiver.
*/
- (CPCompoundPredicateType)compoundPredicateType
{
return _type;
}
/*!
Returns the array of the receiver’s subpredicates.
@return The array of the receiver’s subpredicates.
*/
- (CPArray)subpredicates
{
return _predicates;
}
- (CPPredicate)predicateWithSubstitutionVariables:(CPDictionary)variables
{
var subp = [CPArray array],
count = [subp count];
i;
for (i = 0; i < count; i++)
{
var p = [subp objectAtIndex:i],
sp = [p predicateWithSubstitutionVariables:variables];
[subp addObject:sp];
}
return [[CPCompoundPredicate alloc] initWithType:_type subpredicates:subp];
}
- (CPString)predicateFormat
{
var result = "",
args = [CPArray array],
count = [_predicates count],
i;
if (count == 0)
return @"TRUPREDICATE";
for (i = 0; i < count; i++)
{
var subpredicate = [_predicates objectAtIndex:i],
precedence = [subpredicate predicateFormat];
if ([subpredicate isKindOfClass:[CPCompoundPredicate class]] && [[subpredicate subpredicates] count]> 1 && [subpredicate compoundPredicateType] != _type)
precedence = [CPString stringWithFormat:@"(%s)",precedence];
if (precedence != nil)
[args addObject:precedence];
}
switch (_type)
{
case CPNotPredicateType:
result += "NOT %s" + [args objectAtIndex:0];
break;
case CPAndPredicateType:
result += [args objectAtIndex:0];
var count = [args count];
for (var j = 1; j < count; j++)
result += " AND " + [args objectAtIndex:j];
break;
case CPOrPredicateType:
result += [args objectAtIndex:0];
var count = [args count];
for (var j = 1; j < count; j++)
result += " OR " + [args objectAtIndex:j];
break;
}
return result;
}
- (BOOL)evaluateWithObject:(id)object
{
return [self evaluateWithObject:object substitutionVariables:nil];
}
- (BOOL)evaluateWithObject:(id)object substitutionVariables:(CPDictionary)variables
{
var result = NO,
count = [_predicates count],
i;
if (count == 0)
return YES;
for (i = 0; i < count; i++)
{
var predicate = [_predicates objectAtIndex:i];
switch (_type)
{
case CPNotPredicateType:
return ![predicate evaluateWithObject:object substitutionVariables:variables];
case CPAndPredicateType:
if (i == 0)
result = [predicate evaluateWithObject:object substitutionVariables:variables];
else
result = result && [predicate evaluateWithObject:object substitutionVariables:variables];
if (!result)
return NO;
break;
case CPOrPredicateType:
if ([predicate evaluateWithObject:object substitutionVariables:variables])
return YES;
break;
}
}
return result;
}
@end
@implementation CPCompoundPredicate (CPCoding)
- (id)initWithCoder:(CPCoder)coder
{
self = [super init];
if (self != nil)
{
_predicates = [coder decodeObjectForKey:@"CPCompoundPredicateSubpredicates"];
_type = [coder decodeIntForKey:@"CPCompoundPredicateType"];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:_predicates forKey:@"CPCompoundPredicateSubpredicates"];
[coder encodeInt:_type forKey:@"CPCompoundPredicateType"];
}
@end
+324
View File
@@ -0,0 +1,324 @@
@import <Foundation/CPString.j>
@import <Foundation/CPArray.j>
@import <Foundation/CPKeyValueCoding.j>
@import <Foundation/CPDictionary.j>
@import <Foundation/CPCoder.j>
/*!
An expression that always returns the same value.
*/
CPConstantValueExpressionType = 0;
/*!
An expression that always returns the parameter object itself.
*/
CPEvaluatedObjectExpressionType = 1;
/*!
An expression that always returns whatever value is associated with the key specified by ‘variable’ in the bindings dictionary.
*/
CPVariableExpressionType = 2;
/*!
An expression that returns something that can be used as a key path.
*/
CPKeyPathExpressionType = 3;
/*!
An expression that returns the result of evaluating a function.
*/
CPFunctionExpressionType = 4;
/*!
An expression that defines an aggregate of NSExpression objects.
*/
CPAggregateExpressionType = 5;
/*!
An expression that filters a collection using a subpredicate.
*/
CPSubqueryExpressionType = 6;
/*!
An expression that creates a union of the results of two nested expressions.
*/
CPUnionSetExpressionType = 7;
/*!
An expression that creates an intersection of the results of two nested expressions.
*/
CPIntersectSetExpressionType = 8;
/*!
An expression that combines two nested expression results by set subtraction.
*/
CPMinusSetExpressionType = 9;
/*!
@ingroup foundation
@class CPExpression
@brief CPExpression is used to represent expressions in a predicate.
Comparison operations in an CPPredicate are based on two expressions, as represented by instances of the CPExpression class. Expressions are created for constant values, key paths, and so on.
Generally, anywhere in the CPExpression class hierarchy where there is composite API and subtypes that may only reasonably respond to a subset of that API, invoking a method that does not make sense for that subtype will cause an exception to be thrown.
*/
@implementation CPExpression : CPObject
{
int _type;
}
// Initializing an Expression
/*!
Initializes the receiver with the specified expression type.
@param type The type of the new expression, as defined by CPExpressionType.
@return An initialized CPExpression object of the type type.
*/
- (id)initWithExpressionType:(int)type
{
_type = type;
return self;
}
//Creating an Expression for a Value
/*!
Returns a new expression that represents a given constant value.
@param value The constant value the new expression is to represent.
@return A new expression that represents the constant value.
*/
+ (CPExpression)expressionForConstantValue:(id)value
{
return [[CPExpression_constant alloc] initWithValue:value];
}
/*!
Returns a new expression that represents the object being evaluated.
@return A new expression that represents the object being evaluated.
*/
+ (CPExpression)expressionForEvaluatedObject
{
return [[CPExpression_self alloc] init];
}
/*!
Returns a new expression that extracts a value from the variable bindings dictionary for a given key.
@param string The key for the variable to extract from the variable bindings dictionary.
@return A new expression that extracts from the variable bindings dictionary the value for the key string.
*/
+ (CPExpression)expressionForVariable:(CPString)string
{
return [[CPExpression_variable alloc] initWithVariable:string];
}
/*!
Returns a new expression that invokes valueForKeyPath: with a given key path.
@param keyPath The key path that the new expression should evaluate.
@return A new expression that invokes valueForKeyPath: with keyPath.
*/
+ (CPExpression)expressionForKeyPath:(CPString)keyPath
{
return [[CPExpression_keypath alloc] initWithKeyPath:keyPath];
}
//Creating a Collection Expression
/*!
Returns a new aggregate expression for a given collection.
@param collection A collection object (an instance of CPArray, CPSet, or CPDictionary) that contains further expressions.
@return A new expression that contains the expressions in collection.
*/
+ (CPExpression)expressionForAggregate:(CPArray)collection
{
return [[CPExpression_aggregate alloc] initWithAggregate:collection];
}
/*!
Returns a new CPExpression object that represent the union of a given set and collection.
@param left An expression that evaluates to an CPSet object.
@param right An expression that evaluates to a collection object (an instance of CPArray, CPSet, or CPDictionary).
@return A new CPExpression object that represents the union of left and right.
*/
+ (CPExpression)expressionForUnionSet:(CPExpression)left with:(CPExpression)right
{
return [[CPExpression_unionset alloc] initWithLeft:left right:right];
}
/*!
Returns a new CPExpression object that represent the intersection of a given set and collection.
@param left An expression that evaluates to an CPSet object.
@param right An expression that evaluates to a collection object (an instance of CPArray, CPSet, or CPDictionary).
@return A new CPExpression object that represents the intersection of left and right.
*/
+ (CPExpression)expressionForIntersectSet:(CPExpression)left with:(CPExpression)right
{
return [[CPExpression_intersectset alloc] initWithLeft:left right:right];
}
/*!
Returns a new CPExpression object that represent the subtraction of a given collection from a given set.
@param left An expression that evaluates to an CPSet object.
@param left An expression that evaluates to a collection object (an instance of CPArray, CPSet, or CPDictionary).
@return A new CPExpression object that represents the subtraction of right from left.
*/
+ (CPExpression)expressionForMinusSet:(CPExpression)left with:(CPExpression)right
{
return [[CPExpression_minusset alloc] initWithLeft:left right:right];
}
// Creating an Expression for a Function
/*!
Returns a new expression that will invoke one of the predefined functions.
@param function_name The name of the function to invoke.
@param parameters An array containing NSExpression objects that will be used as parameters during the invocation of selector.
For a selector taking no parameters, the array should be empty. For a selector taking one or more parameters, the array should contain one NSExpression object which will evaluate to an instance of the appropriate type for each parameter.
If there is a mismatch between the number of parameters expected and the number you provide during evaluation, an exception may be raised or missing parameters may simply be replaced by nil (which occurs depends on how many parameters are provided, and whether you have over- or underflow).
@return A new expression that invokes the function name using the parameters in parameters.
The name parameter can be one of the following predefined functions:
@verbatim
name parameter array contents returns
-------------------------------------------------------------------------------------------------------------------------------------
sum: CPExpression instances representing numbers CPNumber
count: CPExpression instances representing numbers CPNumber
min: CPExpression instances representing numbers CPNumber
max: CPExpression instances representing numbers CPNumber
average: CPExpression instances representing numbers CPNumber
median: CPExpression instances representing numbers CPNumber
mode: CPExpression instances representing numbers CPArray (returned array will contain all occurrences of the mode)
stddev: CPExpression instances representing numbers CPNumber
add:to: CPExpression instances representing numbers CPNumber
from:subtract: two CPExpression instances representing numbers CPNumber
multiply:by: two CPExpression instances representing numbers CPNumber
divide:by: two CPExpression instances representing numbers CPNumber
modulus:by: two CPExpression instances representing numbers CPNumber
sqrt: one CPExpression instance representing numbers CPNumber
log: one CPExpression instance representing a number CPNumber
ln: one CPExpression instance representing a number CPNumber
raise:toPower: one CPExpression instance representing a number CPNumber
exp: one CPExpression instance representing a number CPNumber
floor: one CPExpression instance representing a number CPNumber
ceiling: one CPExpression instance representing a number CPNumber
abs: one CPExpression instance representing a number CPNumber
trunc: one CPExpression instance representing a number CPNumber
uppercase: one CPExpression instance representing a string CPString
lowercase: one CPExpression instance representing a string CPString
random none CPNumber (integer)
random: one CPExpression instance representing a number CPNumber (integer) such that 0 <= rand < param
now none [CPDate now]
bitwiseAnd:with: two CPExpression instances representing numbers CPNumber (numbers will be treated as CPInteger)
bitwiseOr:with: two CPExpression instances representing numbers CPNumber (numbers will be treated as CPInteger)
bitwiseXor:with: two CPExpression instances representing numbers CPNumber (numbers will be treated as CPInteger)
leftshift:by: two CPExpression instances representing numbers CPNumber (numbers will be treated as CPInteger)
rightshift:by: two CPExpression instances representing numbers CPNumber (numbers will be treated as CPInteger)
onesComplement: one CPExpression instance representing a numbers CPNumber (numbers will be treated as CPInteger)
@endverbatim
This method raises an exception immediately if the selector is invalid; it raises an exception at runtime if the parameters are incorrect.
*/
+ (CPExpression)expressionForFunction:(CPString)function_name arguments:(CPArray)parameters
{
return [[CPExpression_function alloc] initWithSelector:CPSelectorFromString(function_name) arguments:parameters];
}
/*!
Returns an expression which will return the result of invoking on a given target a selector with a given name using given arguments.
@param target A CPExpression object which will evaluate an object on which the selector identified by name may be invoked.
@param function_name The name of the method to be invoked.
@param parameters An array containing CPExpression objects which can be evaluated to provide parameters for the method specified by name.
@return An expression which will return the result of invoking the selector named name on the result of evaluating the target expression with the parameters specified by evaluating the elements of parameters.
See the description of expressionForFunction:arguments: for examples of how to construct the parameter array.
*/
+ (CPExpression)expressionForFunction:(CPExpression)target selectorName:(CPString)function_name arguments:(CPArray)parameters
{
return [[CPExpression_function alloc] initWithTarget:target selector:CPSelectorFromString(function_name) arguments:parameters];
}
+ (CPExpression)expressionForSubquery:(CPExpression)expression usingIteratorVariable:(CPString)variable predicate:(id)predicate
{
return nil; // UNIMPLEMENTED
}
// Getting Information About an Expression
/*!
Returns the expression type for the receiver.
@return The expression type for the receiver.
This method raises an exception if it is not applicable to the receiver.
*/
- (int)expressionType
{
return _type;
}
/*!
Returns the constant value of the receiver.
@return The constant value of the receiver.
This method raises an exception if it is not applicable to the receiver.
*/
- (id)constantValue
{
[CPException raise:CPInvalidArgumentException reason:@"self is not of CPConstantValueExpressionType"];
return nil;
}
/*!
Returns the variable for the receiver.
@return The variable for the receiver.
This method raises an exception if it is not applicable to the receiver.
*/
- (CPString)variable
{
[CPException raise:CPInvalidArgumentException reason:@"self is not of CPVariableExpressionType"];
return nil;
}
/*!
Returns the key path for the receiver.
@return The key path for the receiver.
This method raises an exception if it is not applicable to the receiver.
*/
- (CPString)keyPath
{
[CPException raise:CPInvalidArgumentException reason:@"self is not of CPKeyPathExpressionType"];
return nil;
}
/*!
Returns the function for the receiver.
@return The function for the receiver.
This method raises an exception if it is not applicable to the receiver.
*/
- (CPString)function
{
[CPException raise:CPInvalidArgumentException reason:@"self is not of CPFunctionExpressionType"];
return nil;
}
/*!
Returns the arguments for the receiver.
@return The arguments for the receiver—that is, the array of expressions that will be passed as parameters during invocation of the selector on the operand of a function expression.
This method raises an exception if it is not applicable to the receiver.
*/
- (CPArray)arguments
{
[CPException raise:CPInvalidArgumentException reason:@"self is not of CPFunctionExpressionType"];
return nil;
}
/*!
Returns the collection of expressions in an aggregate expression, or the collection element of a subquery expression.
@return Returns the collection of expressions in an aggregate expression, or the collection element of a subquery expression.
This method raises an exception if it is not applicable to the receiver.
*/
- (id)collection
{
[CPException raise:CPInvalidArgumentException reason:@"self is not of CPAggregateExpressionType"];
return nil;
}
@end
@import "CPExpression_constant.j"
@import "CPExpression_self.j"
@import "CPExpression_variable.j"
@import "CPExpression_keypath.j"
@import "CPExpression_function.j"
@import "CPExpression_aggregate.j"
@import "CPExpression_unionset.j"
@import "CPExpression_intersectset.j"
@import "CPExpression_minusset.j"
@@ -0,0 +1,107 @@
@import "CPExpression.j"
@import <Foundation/CPArray.j>
@import <Foundation/CPString.j>
@implementation CPExpression_aggregate : CPExpression
{
CPArray _aggregate;
}
- (id)initWithAggregate:(CPArray)collection
{
[super initWithExpressionType:CPAggregateExpressionType];
_aggregate = collection;
return self;
}
+ (CPExpression)expressionForAggregate:(CPArray)collection
{
return [[self alloc] initWithAggregate:collection];
}
- (id)initWithCoder:(CPCoder)coder
{
var aggregate = [coder decodeObjectForKey:@"CPExpressionAggregate"];
return [self initWithAggregate:aggregate];
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:_aggregate forKey:@"CPExpressionAggregate"]; // subexpressions must be CPCoding compliant.
}
- (BOOL)isEqual:(id)object
{
if (self == object)
return YES;
if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object collection] isEqual:[self collection]])
return NO;
return YES;
}
- (id)collection
{
return _aggregate;
}
- (CPExpression)rightExpression
{
if ([_aggregate count] > 0)
return [_aggregate lastObject];
return nil;
}
- (CPExpression)leftExpression
{
if ([_aggregate count] > 0)
return [_aggregate objectAtIndex:0];
return nil;
}
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
{
var eval_array = [CPArray array],
collection = [_aggregate objectEnumerator],
exp;
while (exp = [collection nextObject])
{
var eval = [exp expressionValueWithObject:object context:context];
if (eval != nil)[eval_array addObject:eval];
}
return eval_array;
}
- (CPString)description
{
var i,
count = [_aggregate count],
result = "{";
for (i = 0; i < count; i++)
result = result + [CPString stringWithFormat:@"%s%s", [[_aggregate objectAtIndex:i] description], (i + 1 < count) ? @", " : @""];
result = result + "}";
return result;
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
{
var subst_array = [CPArray array],
count = [_aggregate count],
i;
for (i = 0; i < count; i++)
[subst_array addObject:[[_aggregate objectAtIndex:i] _expressionWithSubstitutionVariables:variables]];
return [CPExpression expressionForAggregate:subst_array];
}
@end
@@ -0,0 +1,92 @@
@import "CPExpression.j"
@import "CPExpression_variable.j"
@import <Foundation/CPString.j>
@implementation CPExpression_assignment: CPExpression
{
CPExpression_variable _assignmentVariable;
CPExpression _subexpression;
}
- (id)initWithAssignmentVariable:(CPString)variable expression:(CPExpression)expression
{
_assignmentVariable = [CPExpression expressionForVariable:variable];
_subexpression = expression;
return self;
}
- (id)initWithAssignmentExpression:(CPExpression)variableExpression expression:(CPExpression)expression
{
_assignmentVariable = variableExpression;
_subexpression = expression;
return self;
}
- (id)initWithCoder:(CPCoder)coder
{
var variable = [coder decodeObjectForKey:@"CPExpressionAssignmentVariable"];
var expression = [coder decodeObjectForKey:@"CPExpressionAssignmentExpression"];
return [self initWithAssignmentVariable:variable expression:expression];
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:_assignmentVariable forKey:@"CPExpressionAssignmentVariable"];
[coder encodeObject:_subexpression forKey:@"CPExpressionAssignmentExpression"];
}
- (BOOL)isEqual:(id)object
{
if (self == object)
return YES;
if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object subexpression] isEqual:[self subexpression]] || ![[object variable] isEqualToString:[self variable]])
return NO;
return YES;
}
- (CPExpression)assignmentVariable
{
return _assignmentVariable;
}
- (CPExpression)subexpression
{
return _subexpression;
}
- (CPString)variable
{
return [_assignmentVariable variable];
}
- (CPString)description
{
var pretty = [_expression description];
if ([_subexpression isKindOfClass:[CPExpression_operator class]])
pretty = [CPString stringWithFormat:@"(%@)", pretty];
return [CPString stringWithFormat:@"%@ := %@", [self variable], pretty];
}
- (id)expressionValueWithObject:(id)object context:(id)context
{
// UNIMPLEMENTED
return nil;
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
{
// UNIMPLEMENTED
return nil;
}
@end
@@ -0,0 +1,65 @@
@import "CPExpression.j"
@import <Foundation/CPDictionary.j>
@implementation CPExpression_constant : CPExpression
{
id _value;
}
- (id)initWithValue:(id)value
{
[super initWithExpressionType:CPConstantValueExpressionType];
_value = value;
return self;
}
- (id)initWithCoder:(CPCoder)coder
{
var value = [coder decodeObjectForKey:@"CPExpressionConstantValue"];
return [self initWithValue:value];
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:_value forKey:@"CPExpressionConstantValue"];
}
- (BOOL)isEqual:(id)object
{
if (self == object)
return YES;
if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object constantValue] isEqual:[self constantValue]])
return NO;
return YES;
}
- (id)constantValue
{
return _value;
}
- (id)expressionValueWithObject:object context:(CPDictionary)context
{
return _value;
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
{
return self;
}
- (CPString)description
{
if ([_value isKindOfClass:[CPString class]])
return @"\"" + _value + @"\"";
return [_value description];
}
@end
@@ -0,0 +1,373 @@
@import "CPExpression.j"
@import <Foundation/CPString.j>
@import <Foundation/CPArray.j>
@import <Foundation/CPDictionary.j>
@implementation CPExpression_function : CPExpression
{
CPExpression _operand;
SEL _selector;
CPArray _arguments;
int _argc;
}
- (id)initWithSelector:(SEL)aselector arguments:(CPArray)parameters
{
[super initWithExpressionType:CPFunctionExpressionType];
if (![self respondsToSelector:aselector])
[CPException raise: CPInvalidArgumentException reason:@"Unknown function implementation: " + aselector];
_selector = aselector;
_operand = nil;
_arguments = parameters;
_argc = [parameters count];
return self;
}
- (id)initWithTarget:(CPExpression)targetExpression selector:(SEL)aselector arguments:(CPArray)parameters
{
[super initWithExpressionType:CPFunctionExpressionType];
var target = [targetExpression expressionValueWithObject:object context:context];
if (![target respondsToSelector:aselector])
[CPException raise: CPInvalidArgumentException reason:@"Unknown function implementation: " + aselector];
_selector = aselector;
_operand = targetExpression;
_arguments = parameters;
_argc = [parameters count];
return self;
}
- (id)initWithCoder:(CPCoder)coder
{
var selector = CPSelectorFromString([coder decodeObjectForKey:@"CPExpressionFunctionName"]);
var arguments = [coder decodeObjectForKey:@"CPExpressionFunctionArguments"];
return [self initWithSelector:selector arguments:arguments];
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:[self _function] forKey:@"CPExpressionFunctionName"];
[coder encodeObject:_arguments forKey:@"CPExpressionArguments"];
}
- (BOOL)isEqual:(id)object
{
if (self == object)
return YES;
if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object _function] isEqualToString:[self _function]] || ![[object operand] isEqual:[self operand]] || ![[object arguments] isEqualToArray:[self arguments]])
return NO;
return YES;
}
- (CPString)_function // OBJJ preprocessor does not like function as a method
{
return CPStringFromSelector(_selector);
}
- (CPString)function
{
return [self _function];
}
- (CPArray)arguments
{
return _arguments;
}
- (CPExpression)operand
{
return _operand;
}
- (id)expressionValueWithObject:(id)object context:(CPDictionary)context
{
var eval_args = [CPArray array],
i;
for (i = 0; i < _argc; i++)
{
var arg = [[_arguments objectAtIndex:i] expressionValueWithObject:object context:context];
if (arg != nil)
[eval_args addObject:arg];
}
var target = (_operand == nil) ? self : [_operand expressionValueWithObject:object context:context];
return [target performSelector:_selector withObject:eval_args];
}
- (CPString)description
{
var result = [CPString stringWithFormat:@"%@ %s(", [_operand description], [self _function]],
i;
for (i = 0; i < _argc; i++)
result = result + [_arguments objectAtIndex:i] + (i+1<_argc) ? ", " : "";
result = result + ")";
return result ;
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
{
var array = [CPArray array],
i;
for (i = 0; i < _argc; i++)
[array addObject:[[_arguments objectAtIndex:i] _expressionWithSubstitutionVariables:variables]];
return [CPExpression expressionForFunction:[self operand] selectorName:[self _function] arguments:array];
}
- (CPNumber)sum:(CPArray)parameters
{
if (_argc < 1)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var i,
sum = 0.0;
for (i = 0; i < _argc; i++)
sum += [[parameters objectAtIndex:i] doubleValue];
return [CPNumber numberWithDouble: sum];
}
- (CPNumber)count:(CPArray)parameters
{
if (_argc < 1)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
return [CPNumber numberWithUnsignedInt: [[parameters objectAtIndex:0] count]];
}
- (CPNumber)min:(CPArray)parameters
{
if (_argc < 1)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
return MIN([parameters objectAtIndex:0],[parameters objectAtIndex:1]);
}
- (CPNumber)max:(CPArray)parameters
{
if (_argc < 1)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
return MAX([parameters objectAtIndex:0],[parameters objectAtIndex:1]);
}
- (CPNumber)average:(CPArray)parameters
{
if (_argc < 1)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var i,
sum = 0.0;
for (i = 0; i < _argc; i++)
sum += [[parameters objectAtIndex:i] doubleValue];
return [CPNumber numberWithDouble: sum / _argc];
}
- (CPNumber)add:to:(CPArray)parameters
{
if (_argc != 2)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var left = [parameters objectAtIndex:0],
right = [parameters objectAtIndex:1];
return [CPNumber numberWithDouble: [left doubleValue] + [right doubleValue]];
}
- (CPNumber)from:subtract:(CPArray)parameters
{
if (_argc != 2)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var left = [parameters objectAtIndex:0],
right = [parameters objectAtIndex:1];
return [CPNumber numberWithDouble: [left doubleValue] - [right doubleValue]];
}
- (CPNumber)multiply:by:(CPArray)parameters
{
if (_argc != 2)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var left = [parameters objectAtIndex:0],
right = [parameters objectAtIndex:1];
return [CPNumber numberWithDouble: [left doubleValue] * [right doubleValue]];
}
- (CPNumber)divide:by:(CPArray)parameters
{
if (_argc != 2)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var left = [parameters objectAtIndex:0],
right = [parameters objectAtIndex:1];
return [CPNumber numberWithDouble: [left doubleValue] / [right doubleValue]];
}
- (CPNumber)sqrt:(CPArray)parameters
{
if (_argc != 1)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var num = [[parameters objectAtIndex:0] doubleValue];
return [CPNumber numberWithDouble: SQRT(num)];
}
- (CPNumber)raise:to:(CPArray)parameters
{
if (_argc < 2)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var num = [[parameters objectAtIndex:0] doubleValue],
power = [[parameters objectAtIndex:1] doubleValue];
return [CPNumber numberWithDouble: POW(num,power)];
}
- (CPNumber)abs:(CPArray)parameters
{
if (_argc != 1)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var num = [[parameters objectAtIndex:0] doubleValue];
return [CPNumber numberWithDouble:ABS(num)];
}
- (CPDate)now
{
return [CPDate date];
}
- (CPNumber)ln:(CPArray)parameters
{
if (_argc != 1)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var num = [[parameters objectAtIndex:0] doubleValue];
return [CPNumber numberWithDouble:Math.log(num)];
}
- (CPNumber)exp:(CPArray)parameters
{
if (_argc != 1)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var num = [[parameters objectAtIndex:0] doubleValue];
return [CPNumber numberWithDouble:EXP(num)];
}
- (CPNumber)ceiling:(CPArray)parameters
{
if (_argc != 1)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var num = [[parameters objectAtIndex:0] doubleValue];
return [CPNumber numberWithDouble:CEIL(num)];
}
- (CPNumber)random
{
return [CPNumber numberWithDouble:RAND()];
}
- (CPNumber)modulus:by:(CPArray)parameters
{
if (_argc != 2)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var left = [parameters objectAtIndex:0],
right = [parameters objectAtIndex:1];
return [CPNumber numberWithInt:([left intValue] % [right intValue])];
}
- (id)first:(CPArray)parameters
{
if (_argc == 0)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
return [[parameters objectAtIndex:0] objectAtIndex:0];
}
- (id)last:(CPArray)parameters
{
if (_argc == 0)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
return [[parameters objectAtIndex:0] lastObject];
}
- (CPNumber)chs:(CPArray)parameters
{
if (_argc == 0)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
return [CPNumber numberWithInt: - [[parameters objectAtIndex:0] intValue]];
}
- (id)index:(CPArray)parameters
{
if (_argc < 2)
[CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"];
var left = [parameters objectAtIndex:0],
right = [parameters objectAtIndex:1];
if ([left isKindOfClass: [CPDictionary class]])
return [left objectForKey:right];
else
return [left objectAtIndex: [right intValue]];
}
/*
- (CPNumber)median:(CPArray)parameters
{
}
- (CPNumber)mode:(CPArray)parameters
{
}
- (CPNumber)stddev:(CPArray)parameters
{
}
- (CPNumber)log:(CPArray)parameters
{
}
- (CPNumber)raise:to:(CPArray)parameters
{
}
- (CPNumber)trunc:(CPArray)parameters
{
}
// These functions are used when parsing
*/
@end
@@ -0,0 +1,86 @@
@import "CPExpression.j"
@implementation CPExpression_intersectset : CPExpression
{
CPExpression _left;
CPExpression _right;
}
- (id)initWithLeft:(CPExpression)left right:(CPExpression)right
{
[super initWithExpressionType:CPIntersectSetExpressionType];
_left = left ;
_right = right;
return self;
}
- (id)initWithCoder:(CPCoder)coder
{
var left = [coder decodeObjectForKey:@"CPExpressionUnionSetLeftExpression"];
var right = [coder decodeObjectForKey:@"CPExpressionUnionSetRightExpression"];
return [self initWithLeft:left right:right];
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:_left forKey:@"CPExpressionUnionSetLeftExpression"];
[coder encodeObject:_right forKey:@"CPExpressionUnionSetRightExpression"];
}
- (BOOL)isEqual:(id)object
{
if (self == object)
return YES;
if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object leftExpression] isEqual:[self leftExpression]] || ![[object rightExpression] isEqual:[self rightExpression]])
return NO;
return YES;
}
- (id)expressionValueWithObject:object context:(CPDictionary)context
{
var right = [_right expressionValueWithObject:object context:context];
if (![right respondsToSelector: @selector(objectEnumerator)])
[CPException raise:CPInvalidArgumentException reason:@"The right expression for a CPIntersectSetExpressionType expression must be either a CPArray, CPDictionary or CPSet"];
var left = [_left expressionValueWithObject:object context:context];
if (![left isKindOfClass:[CPSet set]])
[CPException raise:CPInvalidArgumentException reason:@"The left expression for a CPIntersectSetExpressionType expression must a CPSet"];
var set = [CPSet setWithSet:left],
e = [right objectEnumerator],
item;
while (item = [e nextObject])
if ([left containsObject:item])
[set addObject:item];
return [CPExpression expressionForConstantValue:set];
}
- (CPExpression )_expressionWithSubstitutionVariables:(CPDictionary )variables
{
return self;
}
- (CPExpression)leftExpression
{
return _left;
}
- (CPExpression)rightExpression
{
return _right;
}
- (CPString )description
{
return [_left description] +" INTERSECT "+ [_right description];
}
@end
@@ -0,0 +1,63 @@
@import "CPExpression.j"
@import <Foundation/CPString.j>
@import <Foundation/CPKeyValueCoding.j>
@implementation CPExpression_keypath : CPExpression
{
CPString _keyPath;
}
- (id)initWithKeyPath:(CPString)keyPath
{
[super initWithExpressionType:CPKeyPathExpressionType];
_keyPath = keyPath ;
return self;
}
- (id)initWithCoder:(CPCoder)coder
{
var keyPath = [coder decodeObjectForKey:@"CPExpressionKeyPath"];
return [self initWithKeyPath:keyPath];
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:_keyPath forKey:@"CPExpressionKeyPath"];
}
- (BOOL)isEqual:(id)object
{
if (self == object)
return YES;
if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object keyPath] isEqualToString:[self keyPath]])
return NO;
return YES;
}
- (CPString)keyPath
{
return _keyPath;
}
- (id)expressionValueWithObject:object context:(CPDictionary)context
{
return [object valueForKeyPath:_keyPath];
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
{
return self;
}
- (CPString)description
{
return _keyPath;
}
@end
@@ -0,0 +1,81 @@
@import "CPExpression.j"
@implementation CPExpression_minusset : CPExpression
- (id)initWithLeft:(CPExpression)left right:(CPExpression)right
{
[super initWithExpressionType:CPMinusSetExpressionType];
_left = left ;
_right = right;
return self;
}
- (id)initWithCoder:(CPCoder)coder
{
var left = [coder decodeObjectForKey:@"CPExpressionMinusSetLeftExpression"];
var right = [coder decodeObjectForKey:@"CPExpressionMinusSetRightExpression"];
return [self initWithLeft:left right:right];
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:_left forKey:@"CPExpressionMinusSetLeftExpression"];
[coder encodeObject:_right forKey:@"CPExpressionMinusSetRightExpression"];
}
- (BOOL)isEqual:(id)object
{
if (self == object)
return YES;
if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object leftExpression] isEqual:[self leftExpression]] || ![[object rightExpression] isEqual:[self rightExpression]])
return NO;
return YES;
}
- (id)expressionValueWithObject:object context:(CPDictionary)context
{
var right = [_right expressionValueWithObject:object context:context];
if (![right respondsToSelector: @selector(objectEnumerator)])
[CPException raise:CPInvalidArgumentException reason:@"The right expression for a CPIntersectSetExpressionType expression must be either a CPArray, CPDictionary or CPSet"];
var left = [_left expressionValueWithObject:object context:context];
if (![left isKindOfClass:[CPSet set]])
[CPException raise:CPInvalidArgumentException reason:@"The left expression for a CPIntersectSetExpressionType expression must a CPSet"];
var set = [CPSet setWithSet:left],
e = [right objectEnumerator],
item;
while (item = [e nextObject])
[set removeObject:item];
return [CPExpression expressionForConstantValue:set];
}
- (CPExpression )_expressionWithSubstitutionVariables:(CPDictionary )variables
{
return self;
}
- (CPExpression)leftExpression
{
return _left;
}
- (CPExpression)rightExpression
{
return _right;
}
- (CPString )description
{
return [_left description] +" MINUS "+ [_right description];
}
@end
@@ -0,0 +1,117 @@
@import "CPExpression.j"
@import <Foundation/CPArray.j>
@import <Foundation/CPString.j>
@import <Foundation/CPDictionary.j>
var CPExpressionOperatorNegate = "CPExpressionOperatorNegate";
var CPExpressionOperatorAdd = "CPExpressionOperatorAdd";
var CPExpressionOperatorSubtract = "CPExpressionOperatorSubtract";
var CPExpressionOperatorMultiply = "CPExpressionOperatorMultiply";
var CPExpressionOperatorDivide = "CPExpressionOperatorDivide";
var CPExpressionOperatorExp = "CPExpressionOperatorExp";
var CPExpressionOperatorAssign = "CPExpressionOperatorAssign";
var CPExpressionOperatorKeypath = "CPExpressionOperatorKeypath";
var CPExpressionOperatorIndex = "CPExpressionOperatorIndex";
var CPExpressionOperatorIndexFirst = "CPExpressionOperatorIndexFirst";
var CPExpressionOperatorIndexLast = "CPExpressionOperatorIndexLast";
var CPExpressionOperatorIndexSize = "CPExpressionOperatorIndexSize";
@implementation CPExpression_operator : CPExpression
{
int _operator;
CPArray _arguments;
}
- (id)initWithOperator:(int)operator arguments:(CPArray)arguments
{
_operator = operator;
_arguments = arguments;
return self;
}
+ (CPExpression)expressionForOperator:(CPExpressionOperator)operator arguments:(CPArray)arguments
{
return [self initWithOperator:operator arguments:arguments];
}
- (CPArray)arguments
{
return _arguments;
}
- (CPString)description
{
var result = [CPString string],
args = [CPArray array],
count = [_arguments count],
i;
for (i = 0; i < count; i++)
{
var check = [_arguments objectAtIndex:i],
precedence = [check description];
if ([check isKindOfClass:[CPExpression_operator class]])
precedence = [CPString stringWithFormat:@"(%@)", precedence];
[args addObject:precedence];
}
switch (_operator)
{
case CPExpressionOperatorNegate :
result = result + [CPString stringWithFormat:@"-%@", [args objectAtIndex:0]];
break;
case CPExpressionOperatorAdd :
result = result + [CPString stringWithFormat:@"%@ + %@", [args objectAtIndex:0], [args objectAtIndex:1]];
break;
case CPExpressionOperatorSubtract :
result = result + [CPString stringWithFormat:@"%@ - %@", [args objectAtIndex:0], [args objectAtIndex:1]];
break;
case CPExpressionOperatorMultiply :
result = result + [CPString stringWithFormat:@"%@ * %@", [args objectAtIndex:0], [args objectAtIndex:1]];
break;
case CPExpressionOperatorDivide :
result = result + [CPString stringWithFormat:@"%@ / %@", [args objectAtIndex:0], [args objectAtIndex:1]];
break;
case CPExpressionOperatorExp :
result = result + [CPString stringWithFormat:@"%@ ** %@", [args objectAtIndex:0], [args objectAtIndex:1]];
break;
case CPExpressionOperatorAssign :
result = result + [CPString stringWithFormat:@"%@ := %@", [args objectAtIndex:0], [args objectAtIndex:1]];
break;
case CPExpressionOperatorKeypath :
result = result + [CPString stringWithFormat:@"%@.%@", [args objectAtIndex:0], [args objectAtIndex:1]];
break;
case CPExpressionOperatorIndex :
result = result + [CPString stringWithFormat:@"%@[%@]", [args objectAtIndex:0], [args objectAtIndex:1]];
break;
case CPExpressionOperatorIndexFirst :
result = result + [CPString stringWithFormat:@"%@[FIRST]", [args objectAtIndex:0]];
break;
case CPExpressionOperatorIndexLast :
result = result + [CPString stringWithFormat:@"%@[LAST]", [args objectAtIndex:0]];
break;
case CPExpressionOperatorIndexSize :
result = result + [CPString stringWithFormat:@"%@[SIZE]", [args objectAtIndex:0]];
break;
}
return result;
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
{
var array = [CPArray array],
count = [_arguments count],
i;
for (i = 0; i < count; i++)
[array addObject:[[_arguments objectAtIndex:i] _expressionWithSubstitutionVariables:variables]];
return [CPExpression_operator expressionForOperator:_operator arguments:array];
}
@end
@@ -0,0 +1,46 @@
@import "CPExpression.j"
@import <Foundation/CPString.j>
@import <Foundation/CPDictionary.j>
@import <Foundation/CPCoder.j>
@implementation CPExpression_self : CPExpression{}
- (id)init
{
[super initWithExpressionType:CPEvaluatedObjectExpressionType];
return self;
}
- (id)initWithCoder:(CPCoder)coder
{
return [self init];
}
- (void)encodeWithCoder:(CPCoder)coder
{
}
- (BOOL)isEqual:(id)object
{
return (object == self);
}
- (id)expressionValueWithObject:object context:(CPDictionary)context
{
return object;
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
{
return self;
}
- (CPString)description
{
return @"SELF";
}
@end
@@ -0,0 +1,84 @@
@import "CPExpression.j"
@implementation CPExpression_unionset : CPExpression
- (id)initWithLeft:(CPExpression)left right:(CPExpression)right
{
[super initWithExpressionType:CPUnionSetExpressionType];
_left = left;
_right = right;
return self;
}
- (id)initWithCoder:(CPCoder)coder
{
var left = [coder decodeObjectForKey:@"CPExpressionUnionSetLeftExpression"],
right = [coder decodeObjectForKey:@"CPExpressionUnionSetRightExpression"];
return [self initWithLeft:left right:right];
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:_left forKey:@"CPExpressionUnionSetLeftExpression"];
[coder encodeObject:_right forKey:@"CPExpressionUnionSetRightExpression"];
}
- (BOOL)isEqual:(id)object
{
if (self == object)
return YES;
if (object.isa != self.isa
|| [object expressionType] != [self expressionType]
|| ![[object leftExpression] isEqual:[self leftExpression]]
|| ![[object rightExpression] isEqual:[self rightExpression]])
return NO;
return YES;
}
- (id)expressionValueWithObject:object context:(CPDictionary )context
{
var right = [_right expressionValueWithObject:object context:context];
if (![right respondsToSelector: @selector(objectEnumerator)])
[CPException raise:CPInvalidArgumentException reason:@"The right expression for a CPIntersectSetExpressionType expression must be either a CPArray, CPDictionary or CPSet"];
var left = [_left expressionValueWithObject:object context:context];
if (![left isKindOfClass:[CPSet set]])
[CPException raise:CPInvalidArgumentException reason:@"The left expression for a CPIntersectSetExpressionType expression must a CPSet"];
var unionset = [CPSet setWithSet:left],
e = [right objectEnumerator],
item;
while (item = [e nextObject])
[unionset addObject:item];
return [CPExpression expressionForConstantValue:unionset];
}
- (CPExpression )_expressionWithSubstitutionVariables:(CPDictionary )variables
{
return self;
}
- (CPExpression)leftExpression
{
return _left;
}
- (CPExpression)rightExpression
{
return _right;
}
- (CPString )description
{
return [_left description] +" UNION "+ [_right description];
}
@end
@@ -0,0 +1,68 @@
@import "CPExpression.j"
@import <Foundation/CPString.j>
@import <Foundation/CPDictionary.j>
@implementation CPExpression_variable : CPExpression
{
CPString _variable;
}
- (id)initWithVariable:(CPString)variable
{
[super initWithExpressionType:CPVariableExpressionType];
_variable = [variable copy];
return self;
}
- (id)initWithCoder:(CPCoder)coder
{
var variable = [coder decodeObjectForKey:@"CPExpressionVariable"];
return [self initWithVariable:variable];
}
- (void)encodeWithCoder:(CPCoder)coder
{
[coder encodeObject:_variable forKey:@"CPExpressionVariable"];
}
- (BOOL)isEqual:(id)object
{
if (self == object)
return YES;
if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object variable] isEqualToString:[self variable]])
return NO;
return YES;
}
- (CPString)variable
{
return _variable;
}
- (id)expressionValueWithObject:object context:(CPDictionary)context
{
return [context objectForKey:_variable];
}
- (CPString)description
{
return [CPString stringWithFormat:@"$%s", _variable];
}
- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables
{
var aconstant = [variables objectForKey:_variable];
if (aconstant != nil)
return [CPExpression expressionForConstantValue:aconstant];
return self;
}
@end
+908
View File
@@ -0,0 +1,908 @@
@import <Foundation/CPValue.j>
@import <Foundation/CPArray.j>
@import <Foundation/CPSet.j>
@import <Foundation/CPNull.j>
@import <Foundation/CPScanner.j>
/*!
@ingroup foundation
@class CPPredicate
@brief The CPPredicate class is used to define logical conditions used to constrain a search either for a fetch or for in-memory filtering.
You use predicates to represent logical conditions, used for describing objects in persistent stores and in-memory filtering of objects. Although it is common to create predicates directly from instances of CPComparisonPredicate, CPCompoundPredicate, and CPExpression, you often create predicates from a format string which is parsed by the class methods on CPPredicate. Examples of predicate format strings include:
Simple comparisons, such as grade == "7" or firstName like "Shaffiq"\n
Case/diacritic insensitive lookups, such as name contains[cd] "itroen"\n
Logical operations, such as (firstName like "Mark") OR (lastName like "Adderley")\n
“Between” predicates such as date between {$YESTERDAY, $TOMORROW}.\n
You can create predicates for relationships, such as:
group.name like "work*"\n
ALL children.age > 12\n
ANY children.age > 12\n
You can create predicates for operations, such as @sum.items.price < 1000.
You can also create predicates that include variables, so that the predicate can be pre-defined before substituting concrete values at runtime with evaluateWithObject:substitutionVariables: method.
*/
@implementation CPPredicate : CPObject
{
}
/*!
Creates and returns a new predicate formed by creating a new string with a given format and parsing the result.
@param format The format string for the new predicate.
@param … A comma-separated list of arguments to substitute into format.
@return A new predicate formed by creating a new string with format and parsing the result.
*/
+ (CPPredicate)predicateWithFormat:(CPString) format, ...
{
if (!format)
[CPException raise:CPInvalidArgumentException reason:_cmd + " the format can't be 'nil'"];
var args = Array.prototype.slice.call(arguments, 3);
return [self predicateWithFormat:arguments[2] argumentArray:args];
}
/*!
Creates and returns a new predicate by substituting the values in a given array into a format string and parsing the result.
@param format The format string for the new predicate.
@param arguments The arguments to substitute into predicateFormat. Values are substituted into predicateFormat in the order they appear in the array.
@return A new predicate by substituting the values in arguments into predicateFormat, and parsing the result.
*/
+ (CPPredicate)predicateWithFormat:(CPString)format argumentArray:(CPArray)arguments
{
if (!format)
[CPException raise:CPInvalidArgumentException reason:_cmd + " the format can't be 'nil'"];
var s = [[CPPredicateScanner alloc] initWithString:format args:arguments],
p = [s parse];
return p;
}
/*!
Creates and returns a new predicate by substituting the values in an argument list into a format string and parsing the result.
@param format The format string for the new predicate.
@param argList The arguments to substitute into predicateFormat. Values are substituted into predicateFormat in the order they appear in the argument list.
@return A new predicate by substituting the values in argList into predicateFormat and parsing the result.
*/
+ (CPPredicate)predicateWithFormat:(CPString)format arguments:(va_list)argList
{
// UNIMPLEMENTED
return nil;
}
/*!
Returns a copy of the receiver with the receiver’s variables substituted by values specified in a given substitution variables dictionary.
@param variables The substitution variables dictionary. The dictionary must contain key-value pairs for all variables in the receiver.
@return A copy of the receiver with the receiver’s variables substituted by values specified in variables.
*/
- (CPPredicate)predicateWithSubstitutionVariables:(CPDictionary)variables
{
// IMPLEMENTED BY SUBCLASSES
}
/*!
Creates and returns a predicate that always evaluates to a given value.
@param value The value to which the new predicate should evaluate.
@return A predicate that always evaluates to value.
*/
+ (CPPredicate)predicateWithValue:(BOOL)value
{
return [[CPPredicate_BOOL alloc] initWithBool:value];
}
// Evaluating a Predicate
/*!
Returns a Boolean value that indicates whether a given object matches the conditions specified by the receiver.
@param object The object against which to evaluate the receiver.
@return YES if object matches the conditions specified by the receiver, otherwise NO.
*/
- (BOOL)evaluateWithObject:(id)object
{
// IMPLEMENTED BY SUBCLASSES
}
/*!
Returns a Boolean value that indicates whether a given object matches the conditions specified by the receiver after substituting in the values in a given variables dictionary.
@param object The object against which to evaluate the receiver.
@param variables The substitution variables dictionary. The dictionary must contain key-value pairs for all variables in the receiver.
@return YES if object matches the conditions specified by the receiver after substituting in the values in variables for any replacement tokens, otherwise NO.
*/
- (BOOL)evaluateWithObject:(id)object substitutionVariables:(CPDictionary)variables
{
// IMPLEMENTED BY SUBCLASSES
}
// Getting Format Information
/*!
Returns the receiver’s format string.
@return The receiver’s format string.
*/
- (CPString)predicateFormat
{
// IMPLEMENTED BY SUBCLASSES
}
- (CPString)description
{
return [self predicateFormat];
}
@end
@implementation CPPredicate_BOOL : CPPredicate
{
BOOL _value;
}
- (id)initWithBool:(BOOL)value
{
_value = value;
return self;
}
- (BOOL)evaluateObject:(id)object
{
return _value;
}
- (CPString)predicateFormat
{
return (_value) ? @"TRUEPREDICATE" : @"FALSEPREDICATE";
}
@end
@implementation CPArray (CPPredicate)
- (CPArray)filteredArrayUsingPredicate:(CPPredicate)predicate
{
var count = [self count],
result = [CPArray array],
i;
for (i = 0; i < count; i++)
{
var object = self[i];
if ([predicate evaluateWithObject:object])
result.push(object);
}
return result;
}
- (void)filterUsingPredicate:(CPPredicate)predicate
{
var count = [self count];
while (count--)
{
if (![predicate evaluateWithObject:self[count]])
splice(count, 1);
}
}
@end
@implementation CPSet (CPPredicate)
- (CPSet)filteredSetUsingPredicate:(CPPredicate)predicate
{
var count = [self count],
result = [CPSet set],
i;
for (i = 0; i < count; i++)
{
var object = [self objectAtIndex:i];
if ([predicate evaluateWithObject:object])
[result addObject:object];
}
return result;
}
- (void)filterUsingPredicate:(CPPredicate)predicate
{
var count = [self count];
while (--count >= 0)
{
var object = [self objectAtIndex:count];
if (![predicate evaluateWithObject:object])
[self removeObjectAtIndex:count];
}
}
@end
#define REFERENCE(variable) \
function(newValue)\
{\
var oldValue = variable;\
if (typeof newValue != 'undefined')\
variable = newValue;\
return oldValue;\
}
@implementation CPPredicateScanner : CPScanner
{
CPEnumerator _args;
unsigned _retrieved;
}
- (id) initWithString:(CPString)format args:(CPArray)args
{
self = [super initWithString:format];
if (self != nil)
{
_args = [args objectEnumerator];
}
return self;
}
- (id) nextArg
{
return [_args nextObject];
}
- (BOOL)scanPredicateKeyword:(CPString)key
{
var loc = [self scanLocation];
var c;
[self setCaseSensitive:NO];
if (![self scanString:key intoString:NULL])
return NO;
if ([self isAtEnd])
return YES;
c = [[self string] characterAtIndex:[self scanLocation]];
if (![[CPCharacterSet alphanumericCharacterSet] characterIsMember:c])
return YES;
[self setScanLocation:loc];
return NO;
}
- (CPPredicate) parse
{
var r = nil;
try
{
[self setCharactersToBeSkipped:[CPCharacterSet whitespaceCharacterSet]];
r = [self parsePredicate];
}
catch(error)
{
CPLogConsole(@"Parsing failed for "+[self string]+" with " + error);
}
finally
{
if (![self isAtEnd])
CPLogConsole(@"Format string contains extra characters: \""+[self string]+"\"");
}
return r;
}
- (CPPredicate) parsePredicate
{
return [self parseAnd];
}
- (CPPredicate) parseAnd
{
var l = [self parseOr];
while ([self scanPredicateKeyword:@"AND"] || [self scanPredicateKeyword:@"&&"])
{
var r = [self parseOr];
if ([r isKindOfClass:[CPCompoundPredicate class]] && [r compoundPredicateType] == CPAndPredicateType)
{
if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPAndPredicateType)
{
[[l subpredicates] addObjectsFromArray:[r subpredicates]];
}
else
{
[[r subpredicates] insertObject:l atIndex:0];
l = r;
}
}
else if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPAndPredicateType)
{
[[l subpredicates] addObject:r];
}
else
{
l = [CPCompoundPredicate andPredicateWithSubpredicates:[CPArray arrayWithObjects:l, r]];
}
}
return l;
}
- (CPPredicate) parseNot
{
if ([self scanString:@"(" intoString:NULL])
{
var r = [self parsePredicate];
if (![self scanString:@")" intoString:NULL])
[CPException raise:CPInvalidArgumentException reason:@"Missing ) in compound predicate"];
return r;
}
if ([self scanPredicateKeyword:@"NOT"] || [self scanPredicateKeyword:@"!"])
{
return [CPCompoundPredicate notPredicateWithSubpredicate:[self parseNot]];
}
if ([self scanPredicateKeyword:@"TRUEPREDICATE"])
{
return [CPPredicate predicateWithValue:YES];
}
if ([self scanPredicateKeyword:@"FALSEPREDICATE"])
{
return [CPPredicate predicateWithValue:NO];
}
return [self parseComparison];
}
- (CPPredicate) parseOr
{
var l = [self parseNot];
while ([self scanPredicateKeyword:@"OR"] || [self scanPredicateKeyword:@"||"])
{
var r = [self parseNot];
if ([r isKindOfClass:[CPCompoundPredicate class]] && [r compoundPredicateType] == CPOrPredicateType)
{
if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPOrPredicateType)
{
[[l subpredicates] addObjectsFromArray:[r subpredicates]];
}
else
{
[[r subpredicates] insertObject:l atIndex:0];
l = r;
}
}
else if ([l isKindOfClass:[CPCompoundPredicate class]] && [l compoundPredicateType] == CPOrPredicateType)
{
[[l subpredicates] addObject:r];
}
else
{
l = [CPCompoundPredicate orPredicateWithSubpredicates:[CPArray arrayWithObjects:l, r]];
}
}
return l;
}
- (CPPredicate) parseComparison
{
var modifier = CPDirectPredicateModifier,
type = 0,
opts = 0,
left,
right,
p,
negate = NO,
swap = NO;
if ([self scanPredicateKeyword:@"ANY"])
{
modifier = CPAnyPredicateModifier;
}
else if ([self scanPredicateKeyword:@"ALL"])
{
modifier = CPAllPredicateModifier;
}
else if ([self scanPredicateKeyword:@"NONE"])
{
modifier = CPAnyPredicateModifier;
negate = YES;
}
else if ([self scanPredicateKeyword:@"SOME"])
{
modifier = CPAllPredicateModifier;
negate = YES;
}
left = [self parseExpression];
if ([self scanString:@"!=" intoString:NULL] || [self scanString:@"<>" intoString:NULL])
{
type = CPNotEqualToPredicateOperatorType;
}
else if ([self scanString:@"<=" intoString:NULL] || [self scanString:@"=<" intoString:NULL])
{
type = CPLessThanOrEqualToPredicateOperatorType;
}
else if ([self scanString:@">=" intoString:NULL] || [self scanString:@"=>" intoString:NULL])
{
type = CPGreaterThanOrEqualToPredicateOperatorType;
}
else if ([self scanString:@"<" intoString:NULL])
{
type = CPLessThanPredicateOperatorType;
}
else if ([self scanString:@">" intoString:NULL])
{
type = CPGreaterThanPredicateOperatorType;
}
else if ([self scanString:@"==" intoString:NULL] || [self scanString:@"=" intoString:NULL])
{
type = CPEqualToPredicateOperatorType;
}
else if ([self scanPredicateKeyword:@"MATCHES"])
{
type = CPMatchesPredicateOperatorType;
}
else if ([self scanPredicateKeyword:@"LIKE"])
{
type = CPLikePredicateOperatorType;
}
else if ([self scanPredicateKeyword:@"BEGINSWITH"])
{
type = CPBeginsWithPredicateOperatorType;
}
else if ([self scanPredicateKeyword:@"ENDSWITH"])
{
type = CPEndsWithPredicateOperatorType;
}
else if ([self scanPredicateKeyword:@"IN"])
{
type = CPInPredicateOperatorType;
}
else if ([self scanPredicateKeyword:@"CONTAINS"])
{
type = CPInPredicateOperatorType;
swap = YES;
}
else if ([self scanPredicateKeyword:@"BETWEEN"])
{
var exp = [self parseSimpleExpression],
a = [exp constantValue],
lower, upper,
lexp, uexp,
lp, up;
if (![a isKindOfClass:[CPArray class]])
[CPException raise:CPInvalidArgumentException reason:@"BETWEEN operator requires array argument"];
lower = [a objectAtIndex:0];
upper = [a objectAtIndex:1];
lexp = [CPExpression expressionForConstantValue:lower];
uexp = [CPExpression expressionForConstantValue:upper];
lp = [CPComparisonPredicate predicateWithLeftExpression:left
rightExpression:lexp
modifier:modifier
type:CPGreaterThanPredicateOperatorType
options:opts];
up = [CPComparisonPredicate predicateWithLeftExpression:left
rightExpression:uexp
modifier:modifier
type:CPLessThanPredicateOperatorType
options:opts];
return [CPCompoundPredicate andPredicateWithSubpredicates:
[CPArray arrayWithObjects:lp, up]];
}
else
[CPException raise:CPInvalidArgumentException reason:@"Invalid comparison predicate: "+ [[self string] substringFromIndex: [self scanLocation]]];
if ([self scanString:@"[cd]" intoString:NULL])
{
opts = CPCaseInsensitivePredicateOption | CPDiacriticInsensitivePredicateOption;
}
else if ([self scanString:@"[c]" intoString:NULL])
{
opts = CPCaseInsensitivePredicateOption;
}
else if ([self scanString:@"[d]" intoString:NULL])
{
opts = CPDiacriticInsensitivePredicateOption;
}
right = [self parseExpression];
if (swap == YES)
{
var tmp = left;
left = right;
right = tmp;
}
p = [CPComparisonPredicate predicateWithLeftExpression:left
rightExpression:right
modifier:modifier
type:type
options:opts];
return negate ? [CPCompoundPredicate notPredicateWithSubpredicate:p]:p;
}
- (CPExpression) parseExpression
{
return [self parseBinaryExpression];
}
- (CPExpression) parseSimpleExpression
{
var identifier,
location,
ident,
dbl;
if ([self scanDouble:REFERENCE(dbl)])
return [CPExpression expressionForConstantValue:[CPNumber numberWithDouble:dbl]];
// FIXME: handle integer, hex constants, 0x 0o 0b
if ([self scanString:@"-" intoString:NULL])
return [CPExpression expressionForFunction:@"chs" arguments:[CPArray arrayWithObject:[self parseExpression]]];
if ([self scanString:@"(" intoString:NULL])
{
var arg = [self parseExpression];
if (![self scanString:@")" intoString:NULL])
[CPException raise:CPInvalidArgumentException reason:@"Missing ) in expression"];
return arg;
}
if ([self scanString:@"{" intoString:NULL])
{
var a = [CPMutableArray arrayWithCapacity:10];
if ([self scanString:@"}" intoString:NULL])
return [CPExpression expressionForConstantValue:a];
[a addObject:[self parseExpression]];
while ([self scanString:@"," intoString:NULL])
[a addObject:[self parseExpression]];
if (![self scanString:@"}" intoString:NULL])
[CPException raise:CPInvalidArgumentException reason:@"Missing } in aggregate"];
return [CPExpression expressionForConstantValue:a];
}
if ([self scanPredicateKeyword:@"NULL"] || [self scanPredicateKeyword:@"NIL"])
{
return [CPExpression expressionForConstantValue:[CPNull null]];
}
if ([self scanPredicateKeyword:@"TRUE"] || [self scanPredicateKeyword:@"YES"])
{
return [CPExpression expressionForConstantValue:[CPNumber numberWithBool:YES]];
}
if ([self scanPredicateKeyword:@"FALSE"] || [self scanPredicateKeyword:@"NO"])
{
return [CPExpression expressionForConstantValue:[CPNumber numberWithBool:NO]];
}
if ([self scanPredicateKeyword:@"SELF"])
{
return [CPExpression expressionForEvaluatedObject];
}
if ([self scanString:@"$" intoString:NULL])
{
var variable = [self parseExpression];
if (![variable keyPath])
[CPException raise:CPInvalidArgumentException reason:@"Invalid variable identifier: " + variable];
return [CPExpression expressionForVariable:[variable keyPath]];
}
location = [self scanLocation];
if ([self scanString:@"%" intoString:NULL])
{
if ([self isAtEnd] == NO)
{
var c = [[self string] characterAtIndex:[self scanLocation]];
switch (c)
{
case '%':// '%%' is treated as '%'
location = [self scanLocation];
break;
case 'K':
[self setScanLocation:[self scanLocation] + 1];
return [CPExpression expressionForKeyPath:[self nextArg]];
case '@':
case 'c':
case 'C':
case 'd':
case 'D':
case 'i':
case 'o':
case 'O':
case 'u':
case 'U':
case 'x':
case 'X':
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
[self setScanLocation:[self scanLocation] + 1];
return [CPExpression expressionForConstantValue:[self nextArg]];
case 'h':
[self scanString:@"h" intoString:NULL];
if ([self isAtEnd] == NO)
{
c = [[self string] characterAtIndex:[self scanLocation]];
if (c == 'i' || c == 'u')
{
[self setScanLocation:[self scanLocation] + 1];
return [CPExpression expressionForConstantValue:[self nextArg]];
}
}
break;
case 'q':
[self scanString:@"q" intoString:NULL];
if ([self isAtEnd] == NO)
{
c = [[self string] characterAtIndex:[self scanLocation]];
if (c == 'i' || c == 'u' || c == 'x' || c == 'X')
{
[self setScanLocation:[self scanLocation] + 1];
return [CPExpression expressionForConstantValue:[self nextArg]];
}
}
break;
}
}
[self setScanLocation:location];
}
if ([self scanString:@"\"" intoString:NULL])
{
var skip = [self charactersToBeSkipped],
str;
[self setCharactersToBeSkipped:nil];
if ([self scanUpToString:@"\"" intoString:REFERENCE(str)] == NO)
{
[self setCharactersToBeSkipped:skip];
[CPException raise:CPInvalidArgumentException reason:@"Invalid double quoted literal at "+location];
}
[self scanString:@"\"" intoString:NULL];
[self setCharactersToBeSkipped:skip];
return [CPExpression expressionForConstantValue:str];
}
if ([self scanString:@"'" intoString:NULL])
{
var skip = [self charactersToBeSkipped],
str;
[self setCharactersToBeSkipped:nil];
if ([self scanUpToString:@"'" intoString:REFERENCE(str)] == NO)
{
[self setCharactersToBeSkipped:skip];
[CPException raise:CPInvalidArgumentException reason:@"Invalid single quoted literal at "+location];
}
[self scanString:@"'" intoString:NULL];
[self setCharactersToBeSkipped:skip];
return [CPExpression expressionForConstantValue:str];
}
if ([self scanString:@"@" intoString:NULL])
{
var e = [self parseExpression];
if (![e keyPath])
[CPException raise:CPInvalidArgumentException reason:@"Invalid keypath identifier: "+e];
return [CPExpression expressionForKeyPath:[e keyPath]+"@"];
}
[self scanString:@"#" intoString:NULL];
if (!identifier)
identifier = [CPCharacterSet characterSetWithCharactersInString:@"_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"];
if (![self scanCharactersFromSet:identifier intoString:REFERENCE(ident)])
[CPException raise:CPInvalidArgumentException reason:@"Missing identifier: "+[[self string] substringFromIndex:[self scanLocation]]];
return [CPExpression expressionForKeyPath:ident];
}
- (CPExpression)parseFunctionalExpression
{
var left = [self parseSimpleExpression];
while (YES)
{
if ([self scanString:@"(" intoString:NULL])
{
// function - this parser allows for (max)(a, b, c) to be properly
// recognized and even (%K)(a, b, c) if %K evaluates to "max"
var args = [CPMutableArray arrayWithCapacity:5];
if (![left keyPath])
[CPException raise:CPInvalidArgumentException reason:@"Invalid function identifier: " + left];
if (![self scanString:@")" intoString:NULL])
{
// any arguments
// first argument
[args addObject:[self parseExpression]];
while ([self scanString:@"," intoString:NULL])
{
// more arguments
[args addObject:[self parseExpression]];
}
if (![self scanString:@")" intoString:NULL])
[CPException raise:CPInvalidArgumentException reason:@"Missing ) in function arguments"];
}
left = [CPExpression expressionForFunction:[left keyPath] arguments:args];
}
else if ([self scanString:@"[" intoString:NULL])
{
// index expression
if ([self scanPredicateKeyword:@"FIRST"])
{
left = [CPExpression expressionForFunction:@"first" arguments:[CPArray arrayWithObject:[self parseExpression]]];
}
else if ([self scanPredicateKeyword:@"LAST"])
{
left = [CPExpression expressionForFunction:@"last" arguments:[CPArray arrayWithObject:[self parseExpression]]];
}
else if ([self scanPredicateKeyword:@"SIZE"])
{
left = [CPExpression expressionForFunction:@"count" arguments:[CPArray arrayWithObject:[self parseExpression]]];
}
else
{
left = [CPExpression expressionForFunction:@"index" arguments:[CPArray arrayWithObjects:left, [self parseExpression]]];
}
if (![self scanString:@"]" intoString:NULL])
[CPException raise:CPInvalidArgumentException reason:@"Missing ] in index argument"];
}
else if ([self scanString:@"." intoString:NULL])
{
// keypath - this parser allows for (a).(b.c)
// to be properly recognized
// and even %K.((%K)) if the first %K evaluates to "a" and the
// second %K to "b.c"
if (![left keyPath])
[CPException raise:CPInvalidArgumentException reason:@"Invalid left keypath:" + left];
var right = [self parseExpression];
if (![right keyPath])
[CPException raise:CPInvalidArgumentException reason:@"Invalid right keypath:" + right];
// concatenate
left = [CPExpression expressionForKeyPath:[left keyPath]+ "." + [right keyPath]];
}
else
{
// done with suffixes
return left;
}
}
}
- (CPExpression)parsePowerExpression
{
var left = [self parseFunctionalExpression];
while (YES)
{
var right;
if ([self scanString:@"**" intoString:NULL])
{
right = [self parseFunctionalExpression];
left = [CPExpression expressionForFunction:@"pow" arguments:[CPArray arrayWithObjects:left, right]];
}
else
{
return left;
}
}
}
- (CPExpression)parseMultiplicationExpression
{
var left = [self parsePowerExpression];
while (YES)
{
var right;
if ([self scanString:@"*" intoString:NULL])
{
right = [self parsePowerExpression];
left = [CPExpression expressionForFunction:@"_mul" arguments:[CPArray arrayWithObjects:left, right]];
}
else if ([self scanString:@"/" intoString:NULL])
{
right = [self parsePowerExpression];
left = [CPExpression expressionForFunction:@"_div" arguments:[CPArray arrayWithObjects:left, right]];
}
else
{
return left;
}
}
}
- (CPExpression)parseAdditionExpression
{
var left = [self parseMultiplicationExpression];
while (YES)
{
var right;
if ([self scanString:@"+" intoString:NULL])
{
right = [self parseMultiplicationExpression];
left = [CPExpression expressionForFunction:@"_add" arguments:[CPArray arrayWithObjects:left, right]];
}
else if ([self scanString:@"-" intoString:NULL])
{
right = [self parseMultiplicationExpression];
left = [CPExpression expressionForFunction:@"_sub" arguments:[CPArray arrayWithObjects:left, right]];
}
else
{
return left;
}
}
}
- (CPExpression)parseBinaryExpression
{
var left = [self parseAdditionExpression];
while (YES)
{
var right;
if ([self scanString:@":=" intoString:NULL]) // assignment
{
// check left to be a variable?
right = [self parseAdditionExpression];
// FIXME
}
else
{
return left;
}
}
}
@end
@import "CPCompoundPredicate.j"
@import "CPComparisonPredicate.j"
@import "CPExpression.j"
@import "CPExpression_operator.j"
@import "CPExpression_aggregate.j"
@import "CPExpression_assignment.j"
+392
View File
@@ -0,0 +1,392 @@
// CPScanner.j
// © Emanuele Vulcano, 2008.
//
// Licensed under the terms of Cappuccino's license
// (the GNU Lesser General Public License, version 2.1).
// Please see Cappuccino's LICENSE file for details.
@import <Foundation/CPCharacterSet.j>
@implementation CPScanner : CPObject
{
CPString _string;
CPDictionary _locale;
int _scanLocation;
BOOL _caseSensitive;
CPCharacterSet _charactersToBeSkipped;
}
// TODO Not all methods of NSScanner are available!
/*
- (BOOL)scanLongLong:(long long *)longLongValue
{
}
- (BOOL)scanDecimal:(NSDecimal *)decimalValue
{
}
- (BOOL)scanHexDouble:(double *)result
{
}
- (BOOL)scanHexFloat:(float *)result
{
}
- (BOOL)scanHexInt:(unsigned *)intValue
{
}
- (BOOL)scanHexLongLong:(unsigned long long *)result
{
}
- (BOOL)scanInteger:(NSInteger *)value
{
}
+ (id)localizedScannerWithString:(CPString)string
{
var scanner = [self scannerWithString:string];
[scanner setLocale:[CPLocale currentLocale]];
return scanner;
}
*/
+ (id)scannerWithString:(CPString)aString
{
return [[self alloc] initWithString:aString];
}
- (id)initWithString:(CPString)aString
{
if (self = [super init])
{
_string = [aString copy];
_scanLocation = 0;
_charactersToBeSkipped = [CPCharacterSet whitespaceCharacterSet];
_caseSensitive = NO;
}
return self;
}
- (id)copy
{
var copy = [[CPScanner alloc] initWithString:[self string]];
[copy setCharactersToBeSkipped:[self charactersToBeSkipped]];
[copy setCaseSensitive:[self caseSensitive]];
[copy setLocale:[self locale]];
[copy setScanLocation:[self scanLocation]];
return copy;
}
- (CPDictionary)locale
{
return _locale;
}
- (void)setLocale:(CPDictionary)aLocale
{
_locale = aLocale;
}
- (void)setCaseSensitive:(BOOL)flag
{
_caseSensitive = flag;
}
- (BOOL)caseSensitive
{
return _caseSensitive;
}
- (CPString)string
{
return _string;
}
- (CPCharacterSet)charactersToBeSkipped
{
return _charactersToBeSkipped;
}
- (void)setCharactersToBeSkipped:(CPCharacterSet)c
{
_charactersToBeSkipped = c;
}
- (BOOL)isAtEnd
{
return _scanLocation == _string.length;
}
- (int)scanLocation
{
return _scanLocation;
}
- (void)setScanLocation:(int)aLocation
{
if (aLocation > _string.length)
aLocation = _string.length; // clamp to just after the last character
else if (aLocation < 0)
aLocation = 0; // clamp to the first
_scanLocation = aLocation;
}
// Method body for all methods that return their value by reference.
- (BOOL)_performScanWithSelector:(SEL)s withObject:(id)arg into:(id)ref
{
var ret = [self performSelector:s withObject:arg];
if (ref != nil)
ref(ret);
return ret != NULL;
}
/* ================================ */
/* = Scanning with CPCharacterSet = */
/* ================================ */
- (BOOL)scanCharactersFromSet:(CPCharacterSet)scanSet intoString:(id)ref
{
return [self _performScanWithSelector:@selector(scanCharactersFromSet:) withObject:scanSet into:ref];
}
- (CPString)scanCharactersFromSet:(CPCharacterSet)scanSet
{
return [self _scanWithSet:scanSet breakFlag:NO];
}
- (BOOL)scanUpToCharactersFromSet:(CPCharacterSet)scanSet intoString:(id)ref
{
return [self _performScanWithSelector:@selector(scanUpToCharactersFromSet:) withObject:scanSet into:ref];
}
- (CPString)scanUpToCharactersFromSet:(CPCharacterSet)scanSet
{
return [self _scanWithSet:scanSet breakFlag:YES];
}
// If stop == YES, it will stop when it sees a character from
// the set (scanUpToCharactersFromSet:); if stop == NO, it will
// stop when it sees a character NOT from the set
// (scanCharactersFromSet:).
- (CPString)_scanWithSet:(CPCharacterSet)scanSet breakFlag:(BOOL)stop
{
if ([self isAtEnd])
return nil;
var current = [self scanLocation];
var str = nil;
while (current < _string.length)
{
var c = (_string.charAt(current));
if ([scanSet characterIsMember:c] == stop)
break;
if (![_charactersToBeSkipped characterIsMember:c])
{
if (!str)
str = '';
str += c;
}
current++;
}
if (str)
[self setScanLocation:current];
return str;
}
/* ==================== */
/* = Scanning strings = */
/* ==================== */
- (void)_movePastCharactersToBeSkipped
{
var current = [self scanLocation];
var string = [self string];
var toSkip = [self charactersToBeSkipped];
while (current < string.length)
{
if (![toSkip characterIsMember:string.charAt(current)])
break;
current++;
}
[self setScanLocation:current];
}
- (BOOL)scanString:(CPString)aString intoString:(id)ref
{
return [self _performScanWithSelector:@selector(scanString:) withObject:aString into:ref];
}
- (CPString)scanString:(CPString)s
{
[self _movePastCharactersToBeSkipped];
if ([self isAtEnd])
return nil;
var currentStr = [self string].substr([self scanLocation], s.length);
if ((_caseSensitive && currentStr != s) || (!_caseSensitive && (currentStr.toLowerCase() != s.toLowerCase())))
{
return nil;
}
else
{
[self setScanLocation:[self scanLocation] + s.length];
return s;
}
}
- (BOOL)scanUpToString:(CPString)aString intoString:(id)ref
{
return [self _performScanWithSelector:@selector(scanUpToString:) withObject:aString into:ref];
}
- (CPString)scanUpToString:(CPString)s
{
var current = [self scanLocation], str = [self string];
var captured = nil;
while (current < str.length)
{
var currentStr = str.substr(current, s.length);
if (currentStr == s || (!_caseSensitive && currentStr.toLowerCase() == s.toLowerCase()))
break;
if (!captured)
captured = '';
captured += str.charAt(current);
current++;
}
if (captured)
[self setScanLocation:current];
// evil private method use!
// this method is defined in the category on CPString
// in CPCharacterSet.j
if ([self charactersToBeSkipped])
captured = [captured _stringByTrimmingCharactersInSet:[self charactersToBeSkipped] options:_CPCharacterSetTrimAtBeginning];
return captured;
}
/* ==================== */
/* = Scanning numbers = */
/* ==================== */
- (float)scanFloat
{
[self _movePastCharactersToBeSkipped];
var str = [self string], current = [self scanLocation];
if ([self isAtEnd])
return 0;
var s = str.substring(current, str.length);
var f = parseFloat(s); // wont work with non . decimal separator !!
if (f)
{
var pos, foundDash = NO;
/*
var decimalSeparatorString;
if(_locale != nil)
decimalSeparatorString = [_locale objectForKey:CPLocaleDecimalSeparator];
else
decimalSeparatorString = [[CPLocale systemLocale] objectForKey:CPLocaleDecimalSeparator];
var separatorCode = (decimalSeparatorString.length >0) decimalSeparatorString.charCodeAt(0) : 45;
*/
var separatorCode = 45;
for(pos = current; pos < current + str.length; pos++)
{
var charCode = str.charCodeAt(pos);
if (charCode == separatorCode)
{
if (foundDash == YES)
break; // We already found a decimal separator so this one is an extra char
foundDash = YES;
}
else if (charCode < 48 || charCode > 57 || (charCode == 45 && pos != current)) // not a digit or a "-" but not prefix
break;
}
[self setScanLocation:pos];
return f;
}
return nil;
}
- (int)scanInt
{
[self _movePastCharactersToBeSkipped];
var str = [self string], current = [self scanLocation];
if ([self isAtEnd])
return 0;
var s = str.substring(current, str.length);
var i = parseInt(s);
if (i)
{
var pos, foundDash = NO;
for (pos = current; pos < current + str.length; pos++)
{
var charCode = str.charCodeAt(pos);
if (charCode == 46)
{
if (foundDash == YES)
break;
foundDash = YES;
}
else if (charCode < 48 || charCode > 57 || (charCode == 45 && pos != current))
break;
}
[self setScanLocation:pos];
return i;
}
return nil;
}
- (BOOL)scanInt:(int)intoInt
{
return [self _performScanWithSelector:@selector(scanInt) withObject:nil into:intoInt];
}
- (BOOL)scanFloat:(float)intoFloat
{
return [self _performScanWithSelector:@selector(scanFloat) withObject:nil into:intoFloat];
}
- (BOOL)scanDouble:(float)intoDouble
{
return [self scanFloat:intoDouble];
}
/* ========= */
/* = Debug = */
/* ========= */
- (void) description
{
return [super description] + " {" + CPStringFromClass([self class]) + ", state = '" + ([self string].substr(0, _scanLocation) + "{{ SCAN LOCATION ->}}" + [self string].substr(_scanLocation)) + "'; }";
}
@end
+39
View File
@@ -55,6 +55,12 @@ CPAnchoredSearch = 8;
@class CPString
*/
CPNumericSearch = 64;
/*!
Search ignores diacritic marks.
@global
@class CPString
*/
CPDiacriticInsensitiveSearch = 128;
var CPStringUIDs = new CFMutableDictionary();
@@ -469,6 +475,12 @@ var CPStringRegexSpecialCharacters = [
rhs = rhs.toLowerCase();
}
if(aMask & CPDiacriticInsensitiveSearch)
{
lhs = lhs.stripDiacritics();
rhs = rhs.stripDiacritics();
}
if (lhs < rhs)
return CPOrderedAscending;
else if (lhs > rhs)
@@ -783,4 +795,31 @@ var CPStringRegexSpecialCharacters = [
@end
var diacritics = [[192,198],[224,230],[231,231],[232,235],[236,239],[242,246],[249,252]]; // Basic Latin ; Latin-1 Supplement.
var normalized = [65,97,99,101,105,111,117];
String.prototype.stripDiacritics = function ()
{
var output = "";
for (var indexSource = 0; indexSource < this.length; indexSource++)
{
var code = this.charCodeAt(indexSource);
for (var i = 0; i < diacritics.length; i++)
{
var drange = diacritics[i];
if (code >= drange[0] && code <= drange[drange.length-1])
{
code = normalized[i];
break;
}
}
output += String.fromCharCode(code);
}
return output;
}
String.prototype.isa = CPString;
+6
View File
@@ -22,13 +22,17 @@
@import "CPArray.j"
@import "CPBundle.j"
@import "CPCharacterSet.j"
@import "CPCoder.j"
@import "CPComparisonPredicate.j"
@import "CPCompoundPredicate.j"
@import "CPData.j"
@import "CPDate.j"
@import "CPDictionary.j"
@import "CPEnumerator.j"
@import "CPException.j"
@import "CPFormatter.j"
@import "CPExpression.j"
@import "CPIndexSet.j"
@import "CPInvocation.j"
@import "CPJSONPConnection.j"
@@ -44,9 +48,11 @@
@import "CPObjJRuntime.j"
@import "CPOperation.j"
@import "CPOperationQueue.j"
@import "CPPredicate.j"
@import "CPPropertyListSerialization.j"
@import "CPRange.j"
@import "CPRunLoop.j"
@import "CPScanner.j"
@import "CPSet.j"
@import "CPSortDescriptor.j"
@import "CPString.j"
@@ -0,0 +1,86 @@
@import <Foundation/CPSortDescriptor.j>
@import <Foundation/CPPredicate.j>
@import <AppKit/CPArrayController.j>
@implementation CPArrayControllerPerformance : OJTestCase
- (void)testRearrangeObjects
{
var ELEMENTS = 200,
REPEATS = 25,
ac = [CPArrayController new],
array = [];
for (var i = 0; i < ELEMENTS; i++)
{
var s = [Sortable new];
[s setA:i];
[s setB:i % 3];
array.push(s);
}
var descriptors = [
[CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO],
];
[ac setContent:array];
[ac setFilterPredicate:[CPPredicate predicateWithFormat:@"(b != %@)", 0]];
// Filter alone
var start = (new Date).getTime();
for (var i = 0; i < REPEATS; i++)
{
[ac rearrangeObjects];
var sorted = [ac arrangedObjects],
last = ELEMENTS;
// Verify that all is well.
for (var j = 0, count = [sorted count]; j < count; j++)
{
if (sorted[j].b == 0)
[self fail:"b == 0 should be filtered out (position: "+j+")"];
last = sorted[j];
}
}
var end = (new Date).getTime();
CPLog.warn("testRearrangeObjects, filter: "+(end-start)+"ms");
[ac setSortDescriptors:descriptors];
// Filter and sort.
start = (new Date).getTime();
for (var i = 0; i < REPEATS; i++)
{
[ac rearrangeObjects];
var sorted = [ac arrangedObjects],
last = ELEMENTS;
// Verify that all is well.
for (var j = 0, count = [sorted count]; j < count; j++)
{
if (sorted[j].b == 0)
[self fail:"b == 0 should be filtered out (position: "+j+")"];
if (sorted[j].a >= last)
[self fail:"array values should be descending (position: "+j+")"];
last = sorted[j];
}
}
end = (new Date).getTime();
CPLog.warn("testRearrangeObjects, filter and sort: "+(end-start)+"ms");
}
@end
@implementation Sortable : CPObject
{
int a @accessors;
int b @accessors;
}
@end
+15 -1
View File
@@ -252,12 +252,26 @@ function descriptionWithoutEntity(aString)
var set1 = [CPIndexSet indexSetWithIndex:7];
var set2 = [CPIndexSet indexSetWithIndex:7];
var set3 = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(7, 2)];
[self assertFalse:[set1 isEqualToIndexSet:nil]];
[self assertTrue:[set1 isEqualToIndexSet:set2]];
[self assertTrue:[set1 isEqualToIndexSet:set1]];
[self assertFalse:[set1 isEqualToIndexSet:set3]];
}
- (void)testIsEqual
{
var set1 = [CPIndexSet indexSetWithIndex:7];
var set2 = [CPIndexSet indexSetWithIndex:7];
var set3 = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(7, 2)];
[self assertFalse:[set1 isEqual:nil]];
[self assertFalse:[set1 isEqual:7]];
[self assertTrue:[set1 isEqual:set2]];
[self assertTrue:[set1 isEqual:set1]];
[self assertFalse:[set1 isEqual:set3]];
}
- (void)testCount
{
var set = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(7, 2)];
+6
View File
@@ -4,6 +4,12 @@
@implementation CPNullTest : OJTestCase
- (void)testEquals
{
[self assert:[CPNull null] equals:[CPNull null] message:"CPNull null should equal itself."];
[self assert:[CPNull null] equals:[CPNull new] message:"CPNull null should equal another CPNull."];
}
- (void)testArchiving
{
[self assert:[CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:[CPNull null]]] equals:[CPNull null]];
+225
View File
@@ -0,0 +1,225 @@
@import <Foundation/Foundation.j>
@implementation CPPredicateTest : OJTestCase
{
CPDictionary dict;
}
- (id)init
{
self = [super init];
if (self != nil)
{
var d,
objects;
dict = [[CPDictionary alloc] init];
[dict setObject: @"A Title" forKey:@"title"];
var keys = [CPArray arrayWithObjects:@"Name",@"Age",@"Children"];
objects = [CPArray arrayWithObjects:@"John",[CPNumber numberWithInt:34],[CPArray arrayWithObjects:@"Kid1", @"Kid2"]];
d = [CPDictionary dictionaryWithObjects:objects forKeys:keys];
[dict setObject:d forKey:@"Record1"];
objects = [CPArray arrayWithObjects:@"Mary",[CPNumber numberWithInt:30],[CPArray arrayWithObjects:@"Kid1", @"Girl1"]];
d = [CPDictionary dictionaryWithObjects:objects forKeys:keys];
[dict setObject:d forKey:@"Record2"];
}
return self;
}
- (void)testExpressionsInit
{
var expression_keypath = [CPExpression expressionForKeyPath:@"name"];
[self assertNotNull:expression_keypath message:"KeyPath Expression should not be nil"];
var expression_str = [CPExpression expressionForConstantValue:@"j[a-z]an"];
[self assertNotNull:expression_str message:"ConstantValue Expression should not be nil"];
var expression_num = [CPExpression expressionForConstantValue:[CPNumber numberWithInt:1]];
[self assertNotNull:expression_num message:"ConstantValue Expression should not be nil"];
var expression_collection = [CPExpression expressionForConstantValue:[CPArray arrayWithObjects:@"a",@"b",@"d"]];
[self assertNotNull:expression_collection message:"ConstantValue Expression should not be nil"];
var expression_var = [CPExpression expressionForVariable:@"variable"];
[self assertNotNull:expression_var message:"Variable Expression should not be nil"];
var expression_function = [CPExpression expressionForFunction:@"sum:" arguments:[CPArray arrayWithObjects:expression_num,expression_num]];
[self assertNotNull:expression_function message:"Function Expression should not be nil"];
var expression_self = [CPExpression expressionForEvaluatedObject];
[self assertNotNull:expression_self message:"Function Expression should not be nil"];
var expression_aggregate = [CPExpression expressionForAggregate:[CPArray arrayWithObjects:expression_str,expression_num,expression_function]];
[self assertNotNull:expression_aggregate message:"Aggregate Expression should not be nil"];
var expression_subquery = [CPExpression expressionForSubquery:expression_collection usingIteratorVariable:@"self" predicate:[CPPredicate predicateWithValue:YES]];
// [self assertNotNull:expression_subquery message:"Subquery Expression should not be nil"];
var set = [CPSet setWithObjects:@"a",@"b",@"d"];
var array = [CPArray arrayWithObjects:@"a",@"b",@"d"];
var expression_intersect = [CPExpression expressionForIntersectSet:set with:array];
[self assertNotNull:expression_intersect message:"IntersectSet Expression should not be nil"];
var expression_unionset = [CPExpression expressionForUnionSet:set with:array];
[self assertNotNull:expression_unionset message:"UnionSet Expression should not be nil"];
var expression_minusset = [CPExpression expressionForMinusSet:set with:array];
[self assertNotNull:expression_minusset message:"MinusSet Expression should not be nil"];
}
- (void)testFunctionExpression
{
var function_exp = [CPExpression expressionForFunction:"sum:" arguments:[CPArray arrayWithObjects:[CPExpression expressionForConstantValue:1],[CPExpression expressionForConstantValue:2],[CPExpression expressionForConstantValue:3]]];
var pred = [[CPComparisonPredicate alloc] initWithLeftExpression:function_exp rightExpression:[CPExpression expressionForConstantValue:3] modifier:CPDirectPredicateModifier type:CPGreaterThanPredicateOperatorType options:0];
[self assertTrue:[pred evaluateWithObject:nil] message:[pred description] + " should be true"];
}
- (void)testVariableExpression
{
var variable_exp = [CPExpression expressionForVariable:@"variable"];
var pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForKeyPath:@"Record1.Age"] rightExpression:variable_exp modifier:CPDirectPredicateModifier type:CPGreaterThanPredicateOperatorType options:0];
var variables = [CPDictionary dictionaryWithObject:20 forKey:@"variable"];
[self assertTrue:[pred evaluateWithObject:dict substitutionVariables:variables] message:"'"+ [pred description] + "' should be true"];
}
- (void)testOptions
{
var pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForConstantValue:@"àa"] rightExpression:[CPExpression expressionForConstantValue:@"aà"] modifier:CPDirectPredicateModifier type:CPLikePredicateOperatorType options:3];
[self assertTrue:[pred evaluateWithObject:nil] message:"/"+ [pred description] + "/ should be true"];
pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForConstantValue:@"aB"] rightExpression:[CPExpression expressionForConstantValue:@"Ab"] modifier:CPDirectPredicateModifier type:CPLikePredicateOperatorType options:1];
[self assertTrue:[pred evaluateWithObject:nil] message:"/"+ [pred description] + "/ should be true"];
}
- (void)testModifier
{
var pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForKeyPath:@"Record2.Children"] rightExpression:[CPExpression expressionForConstantValue:@"Gi"] modifier:CPAnyPredicateModifier type:CPBeginsWithPredicateOperatorType options:0];
[self assertTrue:[pred evaluateWithObject:dict] message:"'"+ [pred description] + "' should be true"];
pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForKeyPath:@"Record1.Children"] rightExpression:[CPExpression expressionForConstantValue:@"Kid"] modifier:CPAllPredicateModifier type:CPBeginsWithPredicateOperatorType options:0];
[self assertTrue:[pred evaluateWithObject:dict] message:"'"+ [pred description] + "' should be true"];
}
- (void)testOperators
{
var pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForConstantValue:"Farenight 451"] rightExpression:[CPExpression expressionForConstantValue:"(F|g)\\w+\\s\\d{3}"] modifier:CPDirectPredicateModifier type:CPMatchesPredicateOperatorType options:2];
[self assertTrue:[pred evaluateWithObject:nil] message:"'"+ [pred description] + "' should be true"];
pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForConstantValue:@"b"] rightExpression:[CPExpression expressionForConstantValue:@"[a-c]"] modifier:CPDirectPredicateModifier type:CPLikePredicateOperatorType options:1];
[self assertTrue:[pred evaluateWithObject:nil] message:"'"+ [pred description] + "' should be true"];
pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForConstantValue:@"Aa"] rightExpression:[CPExpression expressionForConstantValue:@"ab"] modifier:CPDirectPredicateModifier type:CPLessThanPredicateOperatorType options:0];
[self assertTrue:[pred evaluateWithObject:nil] message:"'"+ [pred description] + "' should be true"];
pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForConstantValue:@"Ac"] rightExpression:[CPExpression expressionForConstantValue:@"ab"] modifier:CPDirectPredicateModifier type:CPLessThanPredicateOperatorType options:2];
[self assertTrue:[pred evaluateWithObject:nil] message:"'"+ [pred description] + "' should be true"];
}
- (void)testCompoundPredicate
{
var predOne = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForKeyPath:@"Record1.Name"] rightExpression:[CPExpression expressionForConstantValue:@"J"] modifier:CPDirectPredicateModifier type:CPBeginsWithPredicateOperatorType options:0];
var predTwo = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForKeyPath:@"Record1.Age"] rightExpression:[CPExpression expressionForConstantValue:[CPNumber numberWithInt:40]] modifier:CPDirectPredicateModifier type:CPLessThanPredicateOperatorType options:0];
var pred = [[CPCompoundPredicate alloc] initWithType:CPAndPredicateType subpredicates:[CPArray arrayWithObjects:predOne,predTwo]];
[self assertTrue:[pred evaluateWithObject:dict] message:"'"+ [pred description] + "' should be true"];
}
- (void)testNilComparisons
{
// Custom Selector Predicate
var pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForKeyPath:@"Record1.Name"] rightExpression:[CPExpression expressionForConstantValue:nil] customSelector:@selector(yes:)];
[self assertTrue:[pred evaluateWithObject:dict] message:"'"+ [pred description] + "' should be true"];
pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForConstantValue:nil] rightExpression:[CPExpression expressionForConstantValue:nil] customSelector:@selector(yes:)];
[self assertFalse:[pred evaluateWithObject:dict] message:"'"+ [pred description] + "' should be false"];
pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForKeyPath:@"Record1.Name"] rightExpression:[CPExpression expressionForConstantValue:nil] modifier:CPDirectPredicateModifier type:CPBeginsWithPredicateOperatorType options:0];
[self assertFalse:[pred evaluateWithObject:dict] message:"'"+ [pred description] + "' should be false"];
// Predicates with operators
pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForKeyPath:@"Record1.Age"] rightExpression:[CPExpression expressionForConstantValue:nil] modifier:CPDirectPredicateModifier type:CPGreaterThanPredicateOperatorType options:0];
[self assertFalse:[pred evaluateWithObject:dict] message:"'"+ [pred description] + "' should be false"];
pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForConstantValue:nil] rightExpression:[CPExpression expressionForConstantValue:nil] modifier:CPDirectPredicateModifier type:CPGreaterThanOrEqualToPredicateOperatorType options:0];
[self assertTrue:[pred evaluateWithObject:dict] message:"'"+ [pred description] + "' should be true"];
pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForConstantValue:nil] rightExpression:[CPExpression expressionForConstantValue:nil] modifier:CPDirectPredicateModifier type:CPBeginsWithPredicateOperatorType options:0];
[self assertFalse:[pred evaluateWithObject:dict] message:"'"+ [pred description] + "' should be false"];
}
- (void)testPredicateParsing
{
var predicate;
// TEST String
predicate = [CPPredicate predicateWithFormat: @"%K == %@", @"Record1.Name", @"John"];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
predicate = [CPPredicate predicateWithFormat: @"%K MATCHES[c] %@", @"Record1.Name", @"john"];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
predicate = [CPPredicate predicateWithFormat: @"%K BEGINSWITH %@", @"Record1.Name", @"Jo"];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
predicate = [CPPredicate predicateWithFormat: @"(%K == %@) AND (%K == %@)", @"Record1.Name", @"John", @"Record2.Name", @"Mary"];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
// TEST integer
predicate = [CPPredicate predicateWithFormat: @"%K == %d", @"Record1.Age", 34];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
predicate = [CPPredicate predicateWithFormat: @"%K < %d", @"Record1.Age", 40];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
predicate = [CPPredicate predicateWithFormat: @"%K BETWEEN %@", @"Record1.Age", [CPArray arrayWithObjects:[CPNumber numberWithInt: 20], [CPNumber numberWithInt:40]]];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
predicate = [CPPredicate predicateWithFormat: @"Record1.Age BETWEEN {%f,%f}", 20, 40];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
predicate = [CPPredicate predicateWithFormat: @"(%K == %d) OR (%K == %d)", @"Record1.Age", 34, @"Record2.Age", 34];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
// TEST float
predicate = [CPPredicate predicateWithFormat: @"%K < %f", @"Record1.Age", 40.5];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
predicate = [CPPredicate predicateWithFormat: @"%f > %K", 40.5, @"Record1.Age"];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
// TEST Aggregate
predicate = [CPPredicate predicateWithFormat: @"%@ IN %K", @"Kid1", @"Record1.Children"];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
predicate = [CPPredicate predicateWithFormat: @"ANY %K == %@", @"Record2.Children", @"Girl1"];
[self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"];
}
@end
@implementation CPObject (PredicateTesting)
- (BOOL)yes:(id)object
{
return YES;
}
@end
File renamed without changes.
+48
View File
@@ -0,0 +1,48 @@
/*
* AppController.j
* CPTextField
*
* Created by Alexander Ljungberg on August 2, 2010.
* Copyright 2010, WireLoad, LLC All rights reserved.
*/
@import <Foundation/CPObject.j>
@implementation AppController : CPObject
{
}
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
contentView = [theWindow contentView];
var textField = [CPTextField textFieldWithStringValue:"" placeholder:"Edit me!" width:200],
label = [[CPTextField alloc] initWithFrame:CGRectMake(15, 15, 400, 24)];
[label setStringValue:"Edit and hit enter: editing should end."];
[contentView addSubview:label];
[textField setFrameOrigin:CGPointMake(15, 35)];
[textField setEditable:YES];
[textField setPlaceholderString:"Edit me!"];
[textField setTarget:self];
[textField setAction:@selector(textAction:)];
[contentView addSubview:textField];
[theWindow orderFront:self];
// Uncomment the following line to turn on the standard menu bar.
//[CPMenu setMenuBarVisible:YES];
}
- (void)textAction:(id)sender
{
[sender setEditable:NO];
}
@end
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CPApplicationDelegateClass</key>
<string>AppController</string>
<key>CPBundleName</key>
<string>CPTextField</string>
<key>CPPrincipalClass</key>
<string>CPApplication</string>
</dict>
</plist>
+93
View File
@@ -0,0 +1,93 @@
/*
* Jakefile
* CPTextField
*
* Created by Alexander Ljungberg on August 2, 2010.
* Copyright 2010, WireLoad, LLC All rights reserved.
*/
var ENV = require("system").env,
FILE = require("file"),
JAKE = require("jake"),
task = JAKE.task,
FileList = JAKE.FileList,
app = require("cappuccino/jake").app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug",
OS = require("os");
app ("CPTextField", function(task)
{
task.setBuildIntermediatesPath(FILE.join("Build", "CPTextField.build", configuration));
task.setBuildPath(FILE.join("Build", configuration));
task.setProductName("CPTextField");
task.setIdentifier("com.yourcompany.CPTextField");
task.setVersion("1.0");
task.setAuthor("WireLoad, LLC");
task.setEmail("feedback @nospam@ yourcompany.com");
task.setSummary("CPTextField");
task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**")));
task.setResources(new FileList("Resources/**"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
if (configuration === "Debug")
task.setCompilerFlags("-DDEBUG -g");
else
task.setCompilerFlags("-O");
});
task ("default", ["CPTextField"], function()
{
printResults(configuration);
});
task ("build", ["default"]);
task ("debug", function()
{
ENV["CONFIGURATION"] = "Debug";
JAKE.subjake(["."], "build", ENV);
});
task ("release", function()
{
ENV["CONFIGURATION"] = "Release";
JAKE.subjake(["."], "build", ENV);
});
task ("run", ["debug"], function()
{
OS.system(["open", FILE.join("Build", "Debug", "CPTextField", "index.html")]);
});
task ("run-release", ["release"], function()
{
OS.system(["open", FILE.join("Build", "Release", "CPTextField", "index.html")]);
});
task ("deploy", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Deployment", "CPTextField"));
OS.system(["press", "-f", FILE.join("Build", "Release", "CPTextField"), FILE.join("Build", "Deployment", "CPTextField")]);
printResults("Deployment")
});
task ("desktop", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Desktop", "CPTextField"));
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "CPTextField"), FILE.join("Build", "Desktop", "CPTextField", "CPTextField.app"));
printResults("Desktop")
});
task ("run-desktop", ["desktop"], function()
{
OS.system([FILE.join("Build", "Desktop", "CPTextField", "CPTextField.app", "Contents", "MacOS", "NativeHost"), "-i"]);
});
function printResults(configuration)
{
print("----------------------------");
print(configuration+" app built at path: "+FILE.join("Build", configuration, "CPTextField"));
print("----------------------------");
}
+104
View File
@@ -0,0 +1,104 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!--
index-debug.html
CPTextField
Created by Alexander Ljungberg on August 2, 2010.
Copyright 2010, WireLoad, LLC All rights reserved.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="Resources/icon.png" />
<link rel="apple-touch-startup-image" href="Resources/default.png" />
<title>CPTextField</title>
<script type="text/javascript">
OBJJ_MAIN_FILE = "main.j";
OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks", "SomethingElse"];
</script>
<script src="Frameworks/Debug/Objective-J/Objective-J.js" type="text/javascript" charset="UTF-8"></script>
<script type="text/javascript">
objj_msgSend_reset();
// DEBUG OPTIONS:
// Uncomment to enable printing of backtraces on exceptions:
//objj_msgSend_decorate(objj_backtrace_decorator);
// Uncomment to supress exceptions that take place inside a message
//objj_msgSend_decorate(objj_supress_exceptions_decorator)
// Uncomment to enable runtime type checking:
//objj_msgSend_decorate(objj_typecheck_decorator);
// Uncomment (along with both above) to print backtraces on type check errors:
//objj_typecheck_prints_backtrace = true;
// Uncomment to disable the default logger (CPLogConsole if window.console exists, CPLogPopup otherwise):
//CPLogUnregister(CPLogDefault);
// Uncomment to enable a specific logger:
//CPLogRegister(CPLogConsole);
//CPLogRegister(CPLogPopup);
</script>
<style type="text/css">
body{margin:0; padding:0;}
#container {position: absolute; top:50%; left:50%;}
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
</style>
<!--[if lt IE 7]>
<STYLE type="text/css">
#container { position: relative; top: 50%; }
#content { position: relative;}
</STYLE>
<![endif]-->
</head>
<body style="">
<div id="cappuccino-body">
<div id="loadingcontainer" style="background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
<script type="text/javascript">
document.write("<div id='container'><p id='content'>" +
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
"Loading CPTextField...</p></div>");
</script>
<noscript>
<div id="container">
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
</ul>
</div>
</div>
</noscript>
</div>
</div>
</body>
</html>
+79
View File
@@ -0,0 +1,79 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!--
index.html
CPTextField
Created by Alexander Ljungberg on August 2, 2010.
Copyright 2010, WireLoad, LLC All rights reserved.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="Resources/icon.png" />
<link rel="apple-touch-startup-image" href="Resources/default.png" />
<title>CPTextField</title>
<script type="text/javascript">
OBJJ_MAIN_FILE = "main.j";
</script>
<script type="text/javascript" src="Frameworks/Objective-J/Objective-J.js" charset="UTF-8"></script>
<style type="text/css">
body{margin:0; padding:0;}
#container {position: absolute; top:50%; left:50%;}
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
</style>
<!--[if lt IE 7]>
<STYLE type="text/css">
#container { position: relative; top: 50%; }
#content { position: relative;}
</STYLE>
<![endif]-->
</head>
<body style="">
<div id="cappuccino-body">
<div id="loadingcontainer" style="background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
<script type="text/javascript">
document.write("<div id='container'><p id='content'>" +
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
"Loading CPTextField...</p></div>");
</script>
<noscript>
<div id="container">
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
</ul>
</div>
</div>
</noscript>
</div>
</div>
</body>
</html>
+18
View File
@@ -0,0 +1,18 @@
/*
* AppController.j
* CPTextField
*
* Created by Alexander Ljungberg on August 2, 2010.
* Copyright 2010, WireLoad, LLC All rights reserved.
*/
@import <Foundation/Foundation.j>
@import <AppKit/AppKit.j>
@import "AppController.j"
function main(args, namedArgs)
{
CPApplicationMain(args, namedArgs);
}
File renamed without changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

File renamed without changes.
File renamed without changes.
@@ -0,0 +1,75 @@
/*
* AppController.j
* TestTemplate
*
* Created by You on August 10, 2010.
* Copyright 2010, Your Company All rights reserved.
*/
@import <Foundation/CPObject.j>
@implementation AppController : CPObject
{
CPTableView table;
CPTableColumn columnA;
CPTableColumn columnB;
CPTableColumn columnC;
CPTableColumn columnD;
}
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
contentView = [theWindow contentView];
var scroll = [[CPScrollView alloc] initWithFrame:CGRectMake(100,100,700,400)];
table = [[CPTableView alloc] initWithFrame:CGRectMakeZero()];
[table setDataSource:self];
columnA = [[CPTableColumn alloc] initWithIdentifier:"A"];
[table addTableColumn:columnA];
[[columnA headerView] setStringValue:"A"];
[columnA setWidth:175];
columnB = [[CPTableColumn alloc] initWithIdentifier:"B"];
[table addTableColumn:columnB];
[[columnB headerView] setStringValue:"B"];
[columnB setWidth:175]
columnC = [[CPTableColumn alloc] initWithIdentifier:"C"];
[table addTableColumn:columnC];
[[columnC headerView] setStringValue:"C"];
[columnC setWidth:175];
columnD = [[CPTableColumn alloc] initWithIdentifier:"D"];
[table addTableColumn:columnD];
[[columnD headerView] setStringValue:"D"];
[columnD setWidth:175];
columnE = [[CPTableColumn alloc] initWithIdentifier:"E"];
[table addTableColumn:columnE];
[[columnE headerView] setStringValue:"E"];
[columnE setWidth:175];
[scroll setDocumentView:table];
[contentView addSubview:scroll];
[theWindow orderFront:self];
}
- (int)numberOfRowsInTableView:(id)tableView
{
return 10000;
}
- (id)tableView:(id)tableView objectValueForTableColumn:(CPTableColumn)aColumn row:(int)aRow
{
return "Column " + [aColumn identifier] + " Row " + aRow;
}
@end
+2
View File
@@ -60,6 +60,8 @@
_resizingMask = [aCoder decodeBoolForKey:@"NSIsResizeable"] ? CPTableColumnUserResizingMask : CPTableColumnAutoresizingMask;
_isHidden = [aCoder decodeBoolForKey:@"NSHidden"];
_isEditable = [aCoder decodeBoolForKey:@"NSIsEditable"];
_sortDescriptorPrototype = [aCoder decodeObjectForKey:@"NSSortDescriptorPrototype"];
}
+3 -6
View File
@@ -52,12 +52,9 @@
[aCoder decodeFloatForKey:@"NSIntercellSpacingHeight"]);
var gridColor = [aCoder decodeObjectForKey:@"NSGridColor"];
// Convert xib default to cib default
if ([gridColor isEqual:[CPColor colorWithRed:127.0 / 255.0 green:127.0 / 255.0 blue:127.0 / 255.0 alpha:1.0]])
gridColor = [CPColor colorWithRed:229.0 / 255.0 green:229.0 / 255.0 blue:229.0 / 255.0 alpha:1.0];
[self setValue:gridColor forThemeAttribute:@"grid-color"];
if (![gridColor isEqual:[CPColor colorWithRed:127.0 / 255.0 green:127.0 / 255.0 blue:127.0 / 255.0 alpha:1.0]])
[self setValue:gridColor forThemeAttribute:@"grid-color"];
_gridStyleMask = [aCoder decodeIntForKey:@"NSGridStyleMask"];
+10 -2
View File
@@ -64,8 +64,16 @@
var frame = [self frame];
[self setFrameOrigin:CGPointMake(frame.origin.x - 4.0, frame.origin.y - 4.0)];
[self setFrameSize:CGSizeMake(frame.size.width + 8.0, frame.size.height + 8.0)];
[self setFrameOrigin:CGPointMake(frame.origin.x, frame.origin.y)];
[self setFrameSize:CGSizeMake(frame.size.width, frame.size.height)];
// Only adjust the origin and size if this is a bezeled textfield
// this makes sure that labels positioned in IB are properly positioned after nibcib
if ([self isBezeled])
{
[self setFrameOrigin:CGPointMake(frame.origin.x - 4.0, frame.origin.y - 4.0)];
[self setFrameSize:CGSizeMake(frame.size.width + 8.0, frame.size.height + 8.0)];
}
CPLog.debug([self stringValue] + " => isBordered=" + [self isBordered] + ", isBezeled=" + [self isBezeled] + ", bezelStyle=" + [self bezelStyle] + "("+[cell stringValue]+", " + [cell placeholderString] + ")");
}
+2
View File
@@ -29,6 +29,7 @@ var NSBorderlessWindowMask = 0x00,
NSMiniaturizableWindowMask = 0x04,
NSResizableWindowMask = 0x08,
NSUtilityWindowMask = 0x10,
NSDocModalWindowMask = 0x40,
NSTexturedBackgroundWindowMask = 0x100,
NSHUDBackgroundWindowMask = 0x2000;
@@ -70,6 +71,7 @@ var NSBorderlessWindowMask = 0x00,
(_windowStyleMask & NSMiniaturizableWindowMask ? CPMiniaturizableWindowMask : 0) |
(_windowStyleMask & NSResizableWindowMask ? CPResizableWindowMask : 0) |
(_windowStyleMask & NSTexturedBackgroundWindowMask ? NSTexturedBackgroundWindowMask : 0) |
(_windowStyleMask & NSDocModalWindowMask ? CPDocModalWindowMask : 0) |
(_windowStyleMask & NSHUDBackgroundWindowMask ? CPHUDBackgroundWindowMask : 0);
_windowIsFullBridge = [aCoder decodeObjectForKey:"NSFrameAutosaveName"] === "CPBorderlessBridgeWindowMask";