Commits

Tuukka Norri committed 3eb1778

Added classes for creating predicate editor row templates
- Also changed a symbol definition, since we weakly link all of AppKit again.

  • Participants
  • Parent commits 1b40132

Comments (0)

Files changed (9)

File BaseTen.xcodeproj/project.pbxproj

 		539204DA11AECE07000E2BEC /* BXDictionaryFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 539204D811AECE07000E2BEC /* BXDictionaryFunctions.m */; };
 		539204DD11AECEAF000E2BEC /* BXArrayFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 539204DB11AECEAF000E2BEC /* BXArrayFunctions.h */; };
 		539204DE11AECEAF000E2BEC /* BXDictionaryFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 539204DC11AECEAF000E2BEC /* BXDictionaryFunctions.h */; };
-		5392053D11AEDB4F000E2BEC /* BXCollectionFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 5392053C11AEDB4F000E2BEC /* BXCollectionFunctions.h */; };
+		5392053D11AEDB4F000E2BEC /* BXCollectionFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 5392053C11AEDB4F000E2BEC /* BXCollectionFunctions.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		53937B670EFD097700510DBD /* PGTSInvocationRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 53937B650EFD097700510DBD /* PGTSInvocationRecorder.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		53937B680EFD097700510DBD /* PGTSInvocationRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 53937B660EFD097700510DBD /* PGTSInvocationRecorder.m */; };
 		539506F211CFAFBA0049F36C /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5395061611CFAD680049F36C /* AppKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };

File BaseTenAppKit/BaseTenAppKit.xcodeproj/project.pbxproj

 		53174F670F52182700551EBC /* BXConnectionViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 53174F650F52182700551EBC /* BXConnectionViewController.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		53174F680F52182700551EBC /* BXConnectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53174F660F52182700551EBC /* BXConnectionViewController.m */; };
 		532861C30F54DB6D00F294A0 /* MessageView.nib in Resources */ = {isa = PBXBuildFile; fileRef = 532861C10F54DB6D00F294A0 /* MessageView.nib */; };
+		5353EF3311DD3C22000E8E4E /* BXPredicateEditorRowTemplateFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 5353EF3111DD3C22000E8E4E /* BXPredicateEditorRowTemplateFactory.h */; };
+		5353EF3411DD3C22000E8E4E /* BXPredicateEditorRowTemplateFactory.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5353EF3211DD3C22000E8E4E /* BXPredicateEditorRowTemplateFactory.mm */; };
+		5353F04411DE1180000E8E4E /* BXMultipleChoicePredicateEditorRowTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5353F04211DE1180000E8E4E /* BXMultipleChoicePredicateEditorRowTemplate.h */; };
+		5353F04511DE1180000E8E4E /* BXMultipleChoicePredicateEditorRowTemplate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5353F04311DE1180000E8E4E /* BXMultipleChoicePredicateEditorRowTemplate.mm */; };
 		5354445D0D634D9C002A6C47 /* BXSynchronizedArrayController.h in Headers */ = {isa = PBXBuildFile; fileRef = 537E484A0A14B7BA00A22080 /* BXSynchronizedArrayController.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		5354445E0D634D9C002A6C47 /* BaseTenAppKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 537E4A6D0A14C82200A22080 /* BaseTenAppKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		5354445F0D634D9C002A6C47 /* BXObjectStatusToColorTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5364C8C90A222FA600AD8AAF /* BXObjectStatusToColorTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		53438E2A0A384EE600BEDCFF /* NSController+BXAppKitAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSController+BXAppKitAdditions.m"; path = "Sources/NSController+BXAppKitAdditions.m"; sourceTree = "<group>"; };
 		53438E540A384FE500BEDCFF /* BXController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BXController.h; path = Sources/BXController.h; sourceTree = "<group>"; };
 		53438E550A384FE500BEDCFF /* BXController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BXController.m; path = Sources/BXController.m; sourceTree = "<group>"; };
+		5353EF3111DD3C22000E8E4E /* BXPredicateEditorRowTemplateFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BXPredicateEditorRowTemplateFactory.h; path = Sources/BXPredicateEditorRowTemplateFactory.h; sourceTree = "<group>"; };
+		5353EF3211DD3C22000E8E4E /* BXPredicateEditorRowTemplateFactory.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = BXPredicateEditorRowTemplateFactory.mm; path = Sources/BXPredicateEditorRowTemplateFactory.mm; sourceTree = "<group>"; };
+		5353F04211DE1180000E8E4E /* BXMultipleChoicePredicateEditorRowTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BXMultipleChoicePredicateEditorRowTemplate.h; path = Sources/BXMultipleChoicePredicateEditorRowTemplate.h; sourceTree = "<group>"; };
+		5353F04311DE1180000E8E4E /* BXMultipleChoicePredicateEditorRowTemplate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BXMultipleChoicePredicateEditorRowTemplate.mm; path = Sources/BXMultipleChoicePredicateEditorRowTemplate.mm; sourceTree = "<group>"; };
 		535444490D634D63002A6C47 /* debug-gc.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "debug-gc.xcconfig"; path = "xcconfig/debug-gc.xcconfig"; sourceTree = "<group>"; };
 		5354444A0D634D63002A6C47 /* debug-non-gc.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "debug-non-gc.xcconfig"; path = "xcconfig/debug-non-gc.xcconfig"; sourceTree = "<group>"; };
 		5354444B0D634D63002A6C47 /* release-gc.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "release-gc.xcconfig"; path = "xcconfig/release-gc.xcconfig"; sourceTree = "<group>"; };
 				53D0D15A0B6F896200D2D101 /* BXDatabaseContextAdditions.m */,
 				5355C15111A8744700C5DEAF /* BXAppKitSystemEventNotifier.h */,
 				5355C15211A8744700C5DEAF /* BXAppKitSystemEventNotifier.m */,
+				5353F04711DE1187000E8E4E /* Predicate editor */,
 				531060190B905E44009329AD /* NSController Subclasses */,
 				5310601A0B905E59009329AD /* Connection views and panels */,
 				53726DAD0A30E8FD0086BB60 /* Transformers */,
 			name = "Connection views and panels";
 			sourceTree = "<group>";
 		};
+		5353F04711DE1187000E8E4E /* Predicate editor */ = {
+			isa = PBXGroup;
+			children = (
+				5353EF3111DD3C22000E8E4E /* BXPredicateEditorRowTemplateFactory.h */,
+				5353EF3211DD3C22000E8E4E /* BXPredicateEditorRowTemplateFactory.mm */,
+				5353F04211DE1180000E8E4E /* BXMultipleChoicePredicateEditorRowTemplate.h */,
+				5353F04311DE1180000E8E4E /* BXMultipleChoicePredicateEditorRowTemplate.mm */,
+			);
+			name = "Predicate editor";
+			sourceTree = "<group>";
+		};
 		53726DAD0A30E8FD0086BB60 /* Transformers */ = {
 			isa = PBXGroup;
 			children = (
 				53174F000F5209E300551EBC /* BXHostPanel.h in Headers */,
 				53174F670F52182700551EBC /* BXConnectionViewController.h in Headers */,
 				5355C15311A8744700C5DEAF /* BXAppKitSystemEventNotifier.h in Headers */,
+				5353EF3311DD3C22000E8E4E /* BXPredicateEditorRowTemplateFactory.h in Headers */,
+				5353F04411DE1180000E8E4E /* BXMultipleChoicePredicateEditorRowTemplate.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 				53174F680F52182700551EBC /* BXConnectionViewController.m in Sources */,
 				5302F39F0F536C4500105ECD /* BXAuthenticationPanel.m in Sources */,
 				5355C15411A8744700C5DEAF /* BXAppKitSystemEventNotifier.m in Sources */,
+				5353EF3411DD3C22000E8E4E /* BXPredicateEditorRowTemplateFactory.mm in Sources */,
+				5353F04511DE1180000E8E4E /* BXMultipleChoicePredicateEditorRowTemplate.mm in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

File BaseTenAppKit/Sources/BXAppKitSystemEventNotifier.h

 #import <BaseTen/BXSystemEventNotifier.h>
 
 
-BX_EXPORT Class BXAppKitSystemEventNotifierClass;
 @interface BXAppKitSystemEventNotifier : BXSystemEventNotifier
 {
 }

File BaseTenAppKit/Sources/BXAppKitSystemEventNotifier.m

 #import <AppKit/AppKit.h>
 
 
-static void __attribute__((constructor))
-Constructor ()
-{
-	BXAppKitSystemEventNotifierClass = [BXAppKitSystemEventNotifier class];
-}
-
-
 
 @implementation BXAppKitSystemEventNotifier
 - (void) _applicationWillTerminate: (NSNotification *) notification

File BaseTenAppKit/Sources/BXMultipleChoicePredicateEditorRowTemplate.h

+//
+// BXMultipleChoicePredicateEditorRowTemplate.h
+// BaseTen
+//
+// Copyright (C) 2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import <Cocoa/Cocoa.h>
+@class BXEntityDescription;
+@class BXDatabaseContext;
+
+
+@interface BXMultipleChoicePredicateEditorRowTemplate : NSPredicateEditorRowTemplate
+{
+	id mOptions;
+	NSArray *mLeftExpressions;
+	NSString *mDisplayName;
+	NSString *mOptionDisplayNameKeyPath;
+}
+- (id) initWithLeftExpressionOptions: (id) options 
+					 rightExpression: (NSExpression *) rightExpression
+			optionDisplayNameKeyPath: (NSString *) optionDisplayNameKeyPath
+		  rightExpressionDisplayName: (NSString *) displayName
+							modifier: (NSComparisonPredicateModifier) modifier;
+@end

File BaseTenAppKit/Sources/BXMultipleChoicePredicateEditorRowTemplate.mm

+//
+// BXMultipleChoicePredicateEditorRowTemplate.mm
+// BaseTen
+//
+// Copyright (C) 2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import "BXMultipleChoicePredicateEditorRowTemplate.h"
+#import <BaseTen/BaseTen.h>
+#import <BaseTen/BXLogger.h>
+#import <BaseTen/BXCollectionFunctions.h>
+#import <BaseTen/BXArraySize.h>
+
+static NSString * const kKVOCtx = @"BXMultipleChoicePredicateEditorRowTemplateKeyValueObservingContext";
+using namespace BaseTen;
+
+
+@interface BXMultipleChoicePredicateEditorRowTemplate ()
+@property (readwrite, copy) NSArray *leftExpressions;
+@property (readwrite, retain, nonatomic) id optionObjects;
+@property (readwrite, copy) NSString *displayName;
+@property (readwrite, copy) NSString *optionDisplayNameKeyPath;
+@end
+
+
+
+@implementation BXMultipleChoicePredicateEditorRowTemplate
+@synthesize leftExpressions = mLeftExpressions;
+@synthesize displayName = mDisplayName;
+@synthesize optionDisplayNameKeyPath = mOptionDisplayNameKeyPath;
+
++ (BOOL) automaticallyNotifiesObserversForKey: (NSString *) key
+{
+	if ([key isEqualToString: @"optionObjects"])
+		return NO;
+	else
+		return [super automaticallyNotifiesObserversForKey: key];
+}
+
+
++ (NSSet *) keyPathsForValuesAffectingLeftExpressions
+{
+	return [NSSet setWithObject: @"optionObjects"];
+}
+
+
++ (NSSet *) keyPathsForValuesAffectingTemplateViews
+{
+	return [NSSet setWithObject: @"leftExpressions"];
+}
+
+
+- (id) initWithLeftExpressionOptions: (id) options 
+					 rightExpression: (NSExpression *) rightExpression
+			optionDisplayNameKeyPath: (NSString *) optionDisplayNameKeyPath
+		  rightExpressionDisplayName: (NSString *) displayName
+							modifier: (NSComparisonPredicateModifier) modifier
+{
+	Expect (options);
+	Expect (rightExpression);
+	Expect (optionDisplayNameKeyPath);
+	Expect (displayName);
+	
+	NSMutableArray *initialLeftExpressions = [NSMutableArray arrayWithCapacity: [options count]];
+	for (BXDatabaseObject *object in options)
+		[initialLeftExpressions addObject: [NSExpression expressionForConstantValue: object]];
+	
+	id operators [] = {
+		ObjectValue <NSPredicateOperatorType> (NSEqualToPredicateOperatorType),
+		ObjectValue <NSPredicateOperatorType> (NSNotEqualToPredicateOperatorType)
+	};
+	
+	if ((self = [super initWithLeftExpressions: initialLeftExpressions 
+							  rightExpressions: [NSArray arrayWithObject: rightExpression]
+									  modifier: modifier
+									 operators: [NSArray arrayWithObjects: operators count: BXArraySize (operators)]
+									   options: 0]))
+	{
+		[self setDisplayName: displayName];
+		[self setOptionDisplayNameKeyPath: optionDisplayNameKeyPath];
+		[self setOptionObjects: options];
+		[self addObserver: self forKeyPath: @"optionObjects" options: 0 context: kKVOCtx];
+	}
+	return self;
+}
+
+
+- (void) dealloc
+{
+	[mOptions release];
+	[mLeftExpressions release];
+	[mDisplayName release];
+	[mOptionDisplayNameKeyPath release];
+	[super dealloc];
+}
+
+
+- (double) matchForPredicate: (NSPredicate *) inPredicate
+{
+	double retval = 0.0;
+	if ([inPredicate isKindOfClass: [NSComparisonPredicate class]])
+	{
+		NSComparisonPredicate *predicate = (id) inPredicate;
+		NSExpression* lhs = [predicate leftExpression];
+		if ([[self leftExpressions] containsObject: lhs])
+			retval = 1.0;
+	}
+	return retval;
+}
+
+
+- (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) object change: (NSDictionary *) change context: (void *) context
+{
+    if (kKVOCtx == context)
+	{
+		if ([keyPath isEqualToString: @"optionObjects"])
+		{
+			NSMutableArray *leftExpressions = [NSMutableArray arrayWithCapacity: [mOptions count]];
+			for (BXDatabaseObject *object in [self optionObjects])
+				[leftExpressions addObject: [NSExpression expressionForConstantValue: object]];
+			
+			[self setLeftExpressions: leftExpressions];
+		}
+	}
+	else
+	{
+		[super observeValueForKeyPath: keyPath ofObject: object change: change context: context];
+	}
+}
+
+
+- (NSArray *) templateViews
+{
+	NSArray *retval = [super templateViews];
+	
+	[[[[retval objectAtIndex: 0] itemArray] objectAtIndex: 0] setTitle: mDisplayName];
+	for (NSMenuItem* item in [[retval objectAtIndex: 2] itemArray])
+	{
+		BXDatabaseObject *optionValue = [[item representedObject] constantValue];
+		[item setTitle: [optionValue valueForKeyPath: mOptionDisplayNameKeyPath]];
+	}
+	
+	return retval;
+}
+
+
+@dynamic optionObjects;
+- (id) optionObjects
+{
+	return mOptions;
+}
+
+- (void) setOptionObjects: (id) options
+{
+	if (mOptions != options)
+	{
+		[self willChangeValueForKey: @"optionObjects"];
+		[mOptions release];
+		mOptions = [options retain];
+		[mOptions setKey: @"optionObjects"];
+		[mOptions setOwner: self];
+		[self didChangeValueForKey: @"optionObjects"];
+	}
+}
+@end

File BaseTenAppKit/Sources/BXPredicateEditorRowTemplateFactory.h

+//
+// BXPredicateEditorRowTemplateFactory.h
+// BaseTen
+//
+// Copyright (C) 2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import <Foundation/Foundation.h>
+@class BXDatabaseContext;
+@class BXEntityDescription;
+@class BXAttributeDescription;
+
+
+@interface BXPredicateEditorRowTemplateFactory : NSObject
+{
+	NSDictionary *mTypeMapping;
+}
+- (NSArray *) templatesWithDisplayNames: (NSArray *) displayNames
+				   forAttributeKeyPaths: (NSArray *) keyPaths
+					inEntityDescription: (BXEntityDescription *) entityDescription;
+- (NSArray *) multipleChoiceTemplatesWithDisplayNames: (NSArray *) displayNames
+						 andOptionDisplayNameKeyPaths: (NSArray *) displayNameKeyPaths
+							  forRelationshipKeyPaths: (NSArray *) keyPaths
+								  inEntityDescription: (BXEntityDescription *) originalEntity
+									  databaseContext: (BXDatabaseContext *) ctx
+												error: (NSError **) error;
+
+- (NSAttributeType) attributeTypeForAttributeDescription: (BXAttributeDescription *) desc;
+- (NSArray *) operatorsForAttributeType: (NSAttributeType) attributeType 
+				   attributeDescription: (BXAttributeDescription *) desc;
+- (NSUInteger) comparisonOptionsForAttributeType: (NSAttributeType) attributeType
+							attributeDescription: (BXAttributeDescription *) desc;
+@end

File BaseTenAppKit/Sources/BXPredicateEditorRowTemplateFactory.mm

+//
+// BXPredicateEditorRowTemplateFactory.mm
+// BaseTen
+//
+// Copyright (C) 2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import "BXPredicateEditorRowTemplateFactory.h"
+#import "BXMultipleChoicePredicateEditorRowTemplate.h"
+#import <BaseTen/BaseTen.h>
+#import <BaseTen/BXCollectionFunctions.h>
+#import <BaseTen/BXArraySize.h>
+#import <BaseTen/BXKeyPathParser.h>
+
+
+using namespace BaseTen;
+
+
+@interface BXAttributeKeyPathSortKey : NSObject <NSCopying>
+{
+	NSArray *mOperators;
+	NSAttributeType mAttributeType;
+	NSComparisonPredicateModifier mModifier;
+	NSUInteger mOptions;
+}
+@property (readonly, copy, nonatomic) NSArray *operators;
+@property (readonly, nonatomic) NSAttributeType attributeType;
+@property (readonly, nonatomic) NSComparisonPredicateModifier modifier;
+@property (readonly, nonatomic) NSUInteger options;
+- (id) initWithOperators: (NSArray *) operators 
+		   attributeType: (NSAttributeType) attributeType 
+				modifier: (NSComparisonPredicateModifier) modifier 
+				 options: (NSUInteger) options;
+@end
+
+
+
+@interface BXRelationshipKeyPathValue : NSObject
+{
+	NSString *mKeyPath;
+	NSComparisonPredicateModifier mModifier;
+}
+@property (readonly, nonatomic) NSString *keyPath;
+@property (readonly, nonatomic) NSComparisonPredicateModifier modifier;
+- (id) initWithKeyPath: (NSString *) keyPath
+			  modifier: (NSComparisonPredicateModifier) modifier;
+@end
+
+
+
+@implementation BXAttributeKeyPathSortKey
+@synthesize operators = mOperators;
+@synthesize attributeType = mAttributeType;
+@synthesize modifier = mModifier;
+@synthesize options = mOptions;
+
+- (id) initWithOperators: (NSArray *) operators 
+		   attributeType: (NSAttributeType) attributeType 
+				modifier: (NSComparisonPredicateModifier) modifier 
+				 options: (NSUInteger) options
+{
+	if ((self = [super init]))
+	{
+		mOperators = [operators copy];
+		mAttributeType = attributeType;
+		mModifier = modifier;
+		mOptions = options;
+	}
+	return self;
+}
+
+
+- (void) dealloc
+{
+	[mOperators release];
+	[super dealloc];
+}
+
+
+- (id) copyWithZone: (NSZone *) zone
+{
+	return [self retain];
+}
+
+
+- (NSUInteger) hash
+{
+	return mAttributeType ^ mModifier ^ mOptions;
+}
+
+
+- (BOOL) isEqual: (id) anObject
+{
+	BOOL retval = NO;
+	if ([anObject isKindOfClass: [self class]])
+	{
+		// Since we are immutable, no synchronization is necessary.
+		BXAttributeKeyPathSortKey *other = anObject;
+		if (mAttributeType == other->mAttributeType && mModifier == other->mModifier &&
+			mOptions == other->mOptions && [mOperators isEqual: other->mOperators])
+		{
+			retval = YES;
+		}
+	}
+	return retval;
+}
+@end
+
+
+
+@implementation BXRelationshipKeyPathValue
+@synthesize keyPath = mKeyPath;
+@synthesize modifier = mModifier;
+
+- (id) initWithKeyPath: (NSString *) keyPath modifier: (NSComparisonPredicateModifier) modifier
+{
+	if ((self = [super init]))
+	{
+		mKeyPath = [keyPath copy];
+		mModifier = modifier;
+	}
+	return self;
+}
+
+
+- (void) dealloc
+{
+	[mKeyPath release];
+	[super dealloc];
+}
+@end
+
+
+
+@implementation BXPredicateEditorRowTemplateFactory
+- (id) init
+{
+	if ((self = [super init]))
+	{
+		mTypeMapping = [[NSDictionary alloc] initWithObjectsAndKeys:
+						ObjectValue <NSAttributeType> (NSBooleanAttributeType),		@"bool",
+						ObjectValue <NSAttributeType> (NSBinaryDataAttributeType),	@"bytea",
+						ObjectValue <NSAttributeType> (NSStringAttributeType),		@"char",
+						ObjectValue <NSAttributeType> (NSFloatAttributeType),		@"float4",
+						ObjectValue <NSAttributeType> (NSDoubleAttributeType),		@"float8",
+						ObjectValue <NSAttributeType> (NSInteger16AttributeType),	@"int2",
+						ObjectValue <NSAttributeType> (NSInteger32AttributeType),	@"int4",
+						ObjectValue <NSAttributeType> (NSInteger64AttributeType),	@"int8",
+						ObjectValue <NSAttributeType> (NSStringAttributeType),		@"name",
+						ObjectValue <NSAttributeType> (NSDecimalAttributeType),		@"numeric",
+						ObjectValue <NSAttributeType> (NSInteger32AttributeType),	@"oid",
+						ObjectValue <NSAttributeType> (NSStringAttributeType),		@"text",
+						ObjectValue <NSAttributeType> (NSDateAttributeType),		@"timestamp",
+						ObjectValue <NSAttributeType> (NSDateAttributeType),		@"timestamptz",
+						ObjectValue <NSAttributeType> (NSStringAttributeType),		@"varchar",
+						nil];
+	}
+	return self;
+}
+
+
+- (NSAttributeType) attributeTypeForAttributeDescription: (BXAttributeDescription *) desc
+{
+	NSAttributeType retval = NSUndefinedAttributeType;
+	NSString *pgType = [desc databaseTypeName];
+	NSValue *attrType = [mTypeMapping objectForKey: pgType];
+	if (attrType)
+	{
+		BaseTen::ValueGetter <NSAttributeType> getter;
+		getter (attrType, &retval);
+	}
+	return retval;
+}
+
+
+- (NSArray *) operatorsForAttributeType: (NSAttributeType) attributeType 
+				   attributeDescription: (BXAttributeDescription *) desc
+{
+	NSArray *retval = nil;
+	switch (attributeType)
+	{
+		case NSStringAttributeType:
+		{
+			id operators [] = {
+				ObjectValue <NSPredicateOperatorType> (NSEqualToPredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSNotEqualToPredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSMatchesPredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSLikePredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSBeginsWithPredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSEndsWithPredicateOperatorType),
+				//ObjectValue <NSPredicateOperatorType> (NSInPredicateOperatorType), //Not applicable since the lhs should be a substring of rhs but that's not quite usable.
+			};
+			retval = [NSArray arrayWithObjects: operators count: BXArraySize (operators)];
+			break;
+		}
+			
+		case NSInteger16AttributeType:
+		case NSInteger32AttributeType:
+		case NSInteger64AttributeType:
+		case NSDecimalAttributeType:
+		case NSDoubleAttributeType:
+		case NSFloatAttributeType:
+		case NSDateAttributeType:
+		{
+			id operators [] = {
+				ObjectValue <NSPredicateOperatorType> (NSLessThanPredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSLessThanOrEqualToPredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSGreaterThanPredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSGreaterThanOrEqualToPredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSEqualToPredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSNotEqualToPredicateOperatorType)
+			};
+			retval = [NSArray arrayWithObjects: operators count: BXArraySize (operators)];
+			break;
+		}
+			
+		case NSBooleanAttributeType:
+		{
+			id operators [] = {
+				ObjectValue <NSPredicateOperatorType> (NSEqualToPredicateOperatorType),
+				ObjectValue <NSPredicateOperatorType> (NSNotEqualToPredicateOperatorType)
+			};
+			retval = [NSArray arrayWithObjects: operators count: BXArraySize (operators)];
+			break;
+		}
+			
+		default: 
+			break;
+	}
+	return retval;
+}
+
+
+- (NSUInteger) comparisonOptionsForAttributeType: (NSAttributeType) attributeType
+							attributeDescription: (BXAttributeDescription *) desc
+{
+	NSUInteger retval = 0;
+	if (NSStringAttributeType == attributeType)
+		retval = NSCaseInsensitivePredicateOption | NSDiacriticInsensitivePredicateOption;
+	return retval;
+}
+
+
+- (NSArray *) templatesWithDisplayNames: (NSArray *) displayNames
+				   forAttributeKeyPaths: (NSArray *) keyPaths
+					inEntityDescription: (BXEntityDescription *) originalEntity;
+{
+	// Sort key paths by attribute type, modifier and the operators.
+	// (The user might want to override -operatorsForAttributeType:attributeDescription: for individual attributes.)
+	// Create the templates and use the display names for display.
+	
+	NSDictionary *displayNamesByKeyPath = [NSDictionary dictionaryWithObjects: displayNames forKeys: keyPaths];
+	NSMutableDictionary *sortedKeyPaths = [NSMutableDictionary dictionary];
+	
+	for (NSString *keyPath in keyPaths)
+	{
+		BXEntityDescription *currentEntity = originalEntity;
+		NSComparisonPredicateModifier modifier = NSDirectPredicateModifier;
+		BOOL inAttribute = NO;
+		NSArray *components = BXKeyPathComponents (keyPath);
+		BXAttributeDescription *attr = nil;
+		
+		for (NSString *component in components)
+		{
+			if (inAttribute)
+			{
+				[NSException raise: NSInvalidArgumentException format:
+				 @"The component '%@' (key path '%@') points to an attribute, even though the end wasn't reached yet.",
+				 component, keyPath];
+			}
+			
+			BXRelationshipDescription *rel = nil;
+			if ((attr = [[currentEntity attributesByName] objectForKey: component]))
+				inAttribute = YES;
+			else if ((rel = [[currentEntity relationshipsByName] objectForKey: component]))
+			{
+				currentEntity = [rel destinationEntity];
+				if ([rel isToMany])
+					modifier = NSAnyPredicateModifier;
+			}
+			else
+			{
+				[NSException raise: NSInvalidArgumentException format: 
+				 @"Didn't find property '%@' (key path '%@') in entity %@.%@.", 
+				 component, keyPath, [currentEntity schemaName], [currentEntity name]];
+			}
+		}
+		
+		if (! inAttribute)
+		{
+			[NSException raise: NSInvalidArgumentException format:
+			 @"The key path '%@' didn't end in an attribute.", keyPath];
+		}
+		
+		NSAttributeType attributeType = [self attributeTypeForAttributeDescription: attr];
+		NSArray *operators = [self operatorsForAttributeType: attributeType attributeDescription: attr];
+		NSUInteger options = [self comparisonOptionsForAttributeType: attributeType attributeDescription: attr];
+		if (operators)
+		{
+			BXAttributeKeyPathSortKey *sortKey = [[BXAttributeKeyPathSortKey alloc] initWithOperators: operators
+																						attributeType: attributeType
+																							 modifier: modifier
+																							  options: options];
+			NSMutableArray *keyPaths = [sortedKeyPaths objectForKey: sortKey];
+			if (! keyPaths)
+			{
+				keyPaths = [[NSMutableArray alloc] init];
+				[sortedKeyPaths setObject: keyPaths forKey: sortKey];
+				[keyPaths release];
+			}
+			[keyPaths addObject: keyPath];
+			[sortKey release];
+		}		
+	}
+	
+	NSMutableArray *templates = [NSMutableArray arrayWithCapacity: [sortedKeyPaths count]];
+	for (BXAttributeKeyPathSortKey *sortKey in sortedKeyPaths)
+	{
+		NSArray *keyPaths = [sortedKeyPaths objectForKey: sortKey];
+		NSMutableArray *expressions = [NSMutableArray arrayWithCapacity: [keyPaths count]];
+		for (NSString *keyPath in keyPaths)
+			[expressions addObject: [NSExpression expressionForKeyPath: keyPath]];
+		
+		NSPredicateEditorRowTemplate *rowTemplate = nil;
+		rowTemplate = [[[NSPredicateEditorRowTemplate alloc] initWithLeftExpressions: expressions
+														rightExpressionAttributeType: [sortKey attributeType]
+																			modifier: [sortKey modifier]
+																		   operators: [sortKey operators]
+																			 options: [sortKey options]] autorelease];
+
+		for (NSMenuItem *item in [[[rowTemplate templateViews] objectAtIndex: 0] itemArray])
+		{
+			NSString *keyPath = [[item representedObject] keyPath];
+			[item setTitle: [displayNamesByKeyPath objectForKey: keyPath]];
+		}
+		
+		[templates addObject: rowTemplate];
+	}
+	
+	return [[templates copy] autorelease];
+}
+
+
+- (NSArray *) multipleChoiceTemplatesWithDisplayNames: (NSArray *) displayNames
+						 andOptionDisplayNameKeyPaths: (NSArray *) displayNameKeyPaths
+							  forRelationshipKeyPaths: (NSArray *) keyPaths
+								  inEntityDescription: (BXEntityDescription *) originalEntity
+									  databaseContext: (BXDatabaseContext *) ctx
+												error: (NSError **) error
+{
+	// Fetch all objects from the entity at the relationship key path end.
+	// For each object just the corresponding display name key path for the display name.
+	// Instead of display names etc., compare the primary key and the foreign key.
+	
+	NSArray *retval = nil;
+	NSDictionary *displayNamesByRelKeyPath = [NSDictionary dictionaryWithObjects: displayNames forKeys: keyPaths];
+	NSDictionary *optionDisplayNameKeyPathsByRelKeyPath = [NSDictionary dictionaryWithObjects: displayNameKeyPaths forKeys: keyPaths];
+	NSMutableDictionary *sortedKeyPaths = [NSMutableDictionary dictionary];
+	
+	for (NSString *keyPath in sortedKeyPaths)
+	{
+		BXEntityDescription *currentEntity = originalEntity;
+		NSComparisonPredicateModifier modifier = NSDirectPredicateModifier;
+		NSArray *components = BXKeyPathComponents (keyPath);
+		BXRelationshipDescription *rel = nil;
+		
+		for (NSString *component in components)
+		{			
+			if ((rel = [[currentEntity relationshipsByName] objectForKey: component]))
+			{
+				currentEntity = [rel destinationEntity];
+				if ([rel isToMany])
+					modifier = NSAnyPredicateModifier;
+			}
+			else if ([[currentEntity attributesByName] objectForKey: component])
+			{
+				[NSException raise: NSInvalidArgumentException format:
+				 @"The component '%@' (key path '%@') points to an attribute, but a relationship was expected.",
+				 component, keyPath];				
+			}
+			else
+			{
+				[NSException raise: NSInvalidArgumentException format: 
+				 @"Didn't find property '%@' (key path '%@') in entity %@.%@.", 
+				 component, keyPath, [currentEntity schemaName], [currentEntity name]];
+			}
+		}
+		
+		NSMutableArray *keyPaths = [sortedKeyPaths objectForKey: currentEntity];
+		if (! keyPaths)
+		{
+			keyPaths = [NSMutableArray array];
+			[sortedKeyPaths setObject: keyPaths forKey: currentEntity];
+		}
+		
+		BXRelationshipKeyPathValue *val = [[BXRelationshipKeyPathValue alloc] initWithKeyPath: keyPath modifier: modifier];
+		[keyPaths addObject: val];
+		[val release];
+	}
+	
+	NSMutableArray *templates = [NSMutableArray array];
+	for (BXEntityDescription *entity in sortedKeyPaths)
+	{
+		BXRelationshipKeyPathValue *val = [sortedKeyPaths objectForKey: entity];
+		NSString *keyPath = [val keyPath];
+		NSComparisonPredicateModifier modifier = [val modifier];
+		NSExpression *rightExpression = [NSExpression expressionForKeyPath: keyPath];
+		NSString *optionDisplayNameKeyPath = [optionDisplayNameKeyPathsByRelKeyPath objectForKey: keyPath];
+		NSString *displayName = [displayNamesByRelKeyPath objectForKey: keyPath];
+		
+		id res = [ctx executeFetchForEntity: entity 
+							  withPredicate: nil 
+							returningFaults: NO 
+						updateAutomatically: YES 
+									  error: error];
+		
+		if (! res)
+			goto bail;
+		
+		BXMultipleChoicePredicateEditorRowTemplate *rowTemplate = nil;
+		rowTemplate = [[BXMultipleChoicePredicateEditorRowTemplate alloc] initWithLeftExpressionOptions: res
+																						rightExpression: rightExpression
+																			   optionDisplayNameKeyPath: optionDisplayNameKeyPath
+																			 rightExpressionDisplayName: displayName
+																							   modifier: modifier];
+		
+		[templates addObject: rowTemplate];
+		[rowTemplate release];
+	}
+	
+	retval = [[templates copy] autorelease];
+	
+bail:
+	return retval;
+}
+@end

File Sources/BXSystemEventNotifier.m

 #import "BXValidationLock.h"
 
 #import "../BaseTenAppKit/Sources/BXAppKitSystemEventNotifier.h"
-Class BXAppKitSystemEventNotifierClass WEAK_IMPORT_ATTRIBUTE;
+Class BXAppKitSystemEventNotifierClass;
 
 
 
 
 
 @implementation BXSystemEventNotifier
++ (void) initialize
+{
+	static BOOL tooLate = NO;
+	if (! tooLate)
+	{
+		tooLate = YES;
+		BXAppKitSystemEventNotifierClass = NSClassFromString (@"BXAppKitSystemEventNotifierClass");
+	}
+}
+
+
 + (id) copyNotifier
 {
 	id retval = nil;