Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Sample Applications/CoreRecipesApp/MainApplication/Sources/Widgets/PredicateEditorView.m
/* |
File: PredicateEditorView.m |
Abstract: The NSView subclass used for editing the predicates for |
SmartGroup objects. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Computer, Inc. ("Apple") in consideration of your agreement to the |
following terms, and your use, installation, modification or |
redistribution of this Apple software constitutes acceptance of these |
terms. If you do not agree with these terms, please do not use, |
install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Computer, |
Inc. may be used to endorse or promote products derived from the Apple |
Software without specific prior written permission from Apple. Except |
as expressly stated in this notice, no other rights or licenses, express |
or implied, are granted by Apple herein, including but not limited to |
any patent rights that may be infringed by your derivative works or by |
other works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright © 2005 Apple Computer, Inc., All Rights Reserved |
*/ |
#import "PredicateEditorView.h" |
#import "PredicateUILayoutHelp.h" |
#import "InferenceRules.h" |
@implementation PredicateEditorView |
- (id)initWithFrame:(NSRect)frame { |
self = [super initWithFrame:frame]; |
if (self) { |
contexts = [[NSMutableArray alloc] init]; |
editors = CFArrayCreateMutable(kCFAllocatorMallocZone, 0, NULL); |
inferenceCore = [[InferenceCore alloc] init]; |
[[InferenceRules self] setup:inferenceCore]; |
[inferenceCore setValue:[[NSApp delegate] managedObjectContext] forKey:@"managedObjectContext"]; |
} |
return self; |
} |
- (void)dealloc { |
[inferenceCore release]; |
[contexts release]; |
[super dealloc]; |
} |
/** |
Called by classes that use the PredicateEditor to clear the UI. Here we |
create a copy of the subviews, iterate through them, and remove them from |
the superview. |
*/ |
- (void)reset { |
NSArray *subviews = [[self subviews] copy]; |
int count = [subviews count]; |
int index = 0; |
for (; index < count; index++) { |
[[subviews objectAtIndex:index] removeFromSuperviewWithoutNeedingDisplay]; |
} |
[subviews release]; |
[contexts release]; |
contexts = [[NSMutableArray alloc] init]; |
} |
/** |
Turning on isFlipped makes it easier to understand when placing subviews |
*/ |
- (BOOL)isFlipped { |
return YES; |
} |
/** |
EntityName accessors. PredicateEditor is designed to only edit using |
attributes in one entity at a time. |
*/ |
- (void)setEntityName:(NSString *)entityName { |
[inferenceCore setValue:entityName forKey:@"entityName"]; |
} |
- (NSString *)entityName { |
return [inferenceCore valueForKey:@"entityName"]; |
} |
- (NSSize)minimumSize { |
NSArray *subviews = [self subviews]; |
int count = [subviews count]; |
float height = (count > 0) ? [[subviews lastObject] frame].size.height : 10; |
NSSize size = NSMakeSize([self frame].size.width, ((count * height) + (count * 2))); |
return size; |
} |
- (void)setFrameSize:(NSSize)newSize { |
if (newSize.height >= [self minimumSize].height) { |
[super setFrameSize:newSize]; |
} |
} |
- (void)updateSize { |
[self setFrameSize:[self minimumSize]]; |
} |
/** |
UI external to the PredicateEditor add/remove predicates to/from the view. |
This way buttons and menu items can be placed in the containg view. |
*/ |
- (void)add:(id)sender { |
NSArray *subviews = [self subviews]; |
NSNib *nib = [[NSNib alloc] initWithNibNamed:@"SimpleNameValueEditor" bundle:[NSBundle bundleForClass:[self class]]]; |
PredicateUILayoutHelp *owner = [[PredicateUILayoutHelp alloc] init]; |
[owner setPredicateView:self]; |
// the inferenceCore key gives the subview nib items something to bind to when accessing entity information (attributeNames) |
InferenceCore *newCore = [inferenceCore copy]; |
[owner setInferenceCore:newCore]; |
[newCore release]; |
BOOL success = [nib instantiateNibWithOwner:owner topLevelObjects:nil]; |
if (success == YES) { |
NSView *lastView = [subviews lastObject]; // use the lastView to compute location of next view |
[self addSubview:[owner view]]; |
if (lastView != nil) { |
float yPosition = [lastView frame].origin.y + [lastView frame].size.height + 2l; |
[[owner view] setFrameOrigin:NSMakePoint(0.0l, yPosition)]; |
} |
NSSize size = [[owner view] frame].size; |
[[owner view] setFrameSize:NSMakeSize([self frame].size.width, size.height)]; |
[self updateSize]; |
// cache the PredicateUILayoutHelp owner so we can get values later when building a predicate |
[contexts addObject:owner]; |
} |
else { |
NSLog(@"An error occurred adding a predicate element"); |
} |
[owner release]; |
} |
- (void)remove:(PredicateUILayoutHelp *)element { |
NSArray *subviews = [[[self subviews] copy] autorelease]; |
int index = [contexts indexOfObjectIdenticalTo:element]; |
if (index != NSNotFound) { |
NSView *view = [subviews objectAtIndex:index]; |
NSPoint newPoint = [view frame].origin; |
float dy = [view frame].size.height + 2l; |
[view removeFromSuperview]; |
[contexts removeObjectAtIndex:index]; |
int count = [subviews count]; |
for (index++; index < count; index++) { |
view = [subviews objectAtIndex:index]; |
[view setFrameOrigin:newPoint]; |
newPoint.y += dy; |
} |
[self updateSize]; |
} |
[self setNeedsDisplay:YES]; |
} |
/** |
NSEditorRegistration |
*/ |
- (void)objectDidBeginEditing:(id)editor { |
if (CFArrayGetFirstIndexOfValue(editors, CFRangeMake(0, CFArrayGetCount(editors)), editor) == -1) { |
CFArrayAppendValue((CFMutableArrayRef)editors, editor); |
} |
} |
- (void)objectDidEndEditing:(id)editor { |
CFIndex index; |
index = CFArrayGetFirstIndexOfValue(editors, CFRangeMake(0, CFArrayGetCount(editors)), editor); |
if (index != -1) { |
CFArrayRemoveValueAtIndex((CFMutableArrayRef)editors, index); |
} |
} |
/** |
Make sure any editing views commit their changes. Prevents us from creating |
bad predicates. |
*/ |
- (BOOL)commitEditing { |
CFIndex i, count, index; |
NSObject *editor; |
count = CFArrayGetCount(editors); |
for (i = 0; i < count; i++) { |
index = count - i - 1; |
editor = (NSObject *)(CFArrayGetValueAtIndex(editors, index)); |
if (![editor commitEditing]) { |
return NO; |
} |
// we should NOT modify this array directly - the editor will be removed from the array in the objectDidEndEditing method |
//CFArrayRemoveValueAtIndex(editors, index); |
} |
// ensure the predicate is valid |
@try { |
[self predicate]; |
} |
@catch ( NSException *e ) { |
// present an alert about the problem |
NSAlert *alert = [[[NSAlert alloc] init] autorelease]; |
[alert addButtonWithTitle:@"OK"]; |
[alert setMessageText:@"Invalid Conditions"]; |
[alert setInformativeText: [NSString stringWithFormat: @"The conditions you have specified for the SmartGroup are invalid: please examine the values entered to ensure they have the proper formatting.\n\n(Error: %@)", [e description]]]; |
[alert setAlertStyle:NSWarningAlertStyle]; |
[alert runModal]; |
// return no |
return NO; |
} |
return YES; |
} |
/** |
When created, the predicate UI is initially empty. Here, we reverse engineer |
the predicate string from the passed in predicate. For best results, only |
pass in predicates created by the PredicateEditor |
*/ |
- (void)setPredicate:(NSPredicate *)newPredicate { |
NSArray *subpredicates; |
// The predicate may be nil, compound or comparison |
if (newPredicate == nil) { |
subpredicates = [NSArray array]; |
} else if ([newPredicate isKindOfClass:[NSCompoundPredicate self]]) { |
subpredicates = [(NSCompoundPredicate *)newPredicate subpredicates]; |
} else if (([newPredicate isEqualTo:[NSPredicate predicateWithValue:YES]]) || ([newPredicate isEqualTo:[NSPredicate predicateWithValue:NO]])) { |
subpredicates = [NSArray array]; |
} else { |
subpredicates = [NSArray arrayWithObject:newPredicate]; |
} |
int count = [subpredicates count]; |
int index = 0; |
for (; index < count; index++) { |
NSComparisonPredicate *term = [subpredicates objectAtIndex:index]; |
int operatorType = [term predicateOperatorType]; |
// The IN operator type corresonds to "foo contains'f'", but the expressions are reversed to look like "'f' IN foo" so we need to special case |
NSString *attributeName = (operatorType == NSInPredicateOperatorType) ? [[term rightExpression] keyPath] : [[term leftExpression] keyPath]; |
NSString *searchValue = (operatorType == NSInPredicateOperatorType) ? [[term leftExpression] constantValue] : [[term rightExpression] constantValue]; |
NSString *operatorName; |
switch (operatorType) { |
case NSBeginsWithPredicateOperatorType : |
operatorName = @"beginswith"; break; |
case NSInPredicateOperatorType : |
operatorName = @"contains"; break; |
case NSEndsWithPredicateOperatorType : |
operatorName = @"endswith"; break; |
case NSLikePredicateOperatorType : |
operatorName = @"like"; break; |
case NSMatchesPredicateOperatorType : |
operatorName = @"matches"; break; |
case NSLessThanPredicateOperatorType : |
operatorName = @"<"; break; |
case NSLessThanOrEqualToPredicateOperatorType : |
operatorName = @"<="; break; |
case NSGreaterThanPredicateOperatorType : |
operatorName = @">"; break; |
case NSGreaterThanOrEqualToPredicateOperatorType : |
operatorName = @">="; break; |
case NSEqualToPredicateOperatorType : |
operatorName = @"=="; break; |
case NSNotEqualToPredicateOperatorType : |
operatorName = @"!="; break; |
default : |
operatorName = @""; |
} |
// add a predicate line to the UI |
[self add:self]; |
// Set the values of the keys the SimpleNameValueEditor nib binds to, pushing the predicate details to the UI |
id context = [contexts lastObject]; // the PredicateUILayoutHelp that was added above |
[context setValue:attributeName forKey:@"attributeName"]; |
[context setValue:operatorName forKey:@"operatorName"]; |
[context setValue:searchValue forKey:@"searchValue"]; |
} |
} |
/** |
Combine the bound values of each PredicateUILayoutHelp object in contexts |
into predicates. We always AND individual predicates together. |
*/ |
- (NSPredicate *)predicate { |
NSPredicate *predicate = nil; |
NSMutableString *format = [NSMutableString string]; |
int count = [contexts count]; |
int index; |
for (index=0; index < count; index++) { |
id context = [contexts objectAtIndex:index]; |
InferenceCore *contextInferenceCore = [(PredicateUILayoutHelp *)context inferenceCore]; |
[format appendString:[contextInferenceCore inferredValueForKeyPath:@"prependedAnyString"]]; |
[format appendString:@" "]; |
[format appendString:[context valueForKey:@"attributeName"]]; |
[format appendString:@" "]; |
[format appendString:[context valueForKey:@"operatorName"]]; |
NSAttributeType type = [[context valueForKey:@"attributeType"] intValue]; |
if ((type == NSInteger16AttributeType) || (type == NSDateAttributeType)) { |
[format appendFormat:@" %@", [context valueForKey:@"searchValue"]]; |
} |
else if (type == NSStringAttributeType) { |
[format appendString:@" \""]; |
[format appendString:[context valueForKey:@"searchValue"]]; |
[format appendString:@"\""]; |
} |
if (index+1 < count) { |
[format appendString:@" AND "]; |
} |
} |
if ([format length] > 0) { |
predicate = [NSPredicate predicateWithFormat:format]; |
} |
return predicate; |
} |
@end |
Copyright © 2005 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2005-06-01