aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax <dmoagx@users.noreply.github.com>2018-05-09 19:12:08 +0200
committerMax <dmoagx@users.noreply.github.com>2018-05-09 19:12:08 +0200
commit1a958facfa883c5e9604ea282c4bb4f809e981fe (patch)
tree821efb5dcbe9c0624df35c0690b9115457b72a73
parent523a40c4a1297c318a9632b1ab31dee2d2de1b00 (diff)
downloadsequelpro-1a958facfa883c5e9604ea282c4bb4f809e981fe.tar.gz
sequelpro-1a958facfa883c5e9604ea282c4bb4f809e981fe.tar.bz2
sequelpro-1a958facfa883c5e9604ea282c4bb4f809e981fe.zip
#63: Move some code around + add documentation
-rw-r--r--Source/SPFillView.h47
-rw-r--r--Source/SPFillView.m61
-rw-r--r--Source/SPTableContent.m6
-rw-r--r--Source/SPTableContentFilterController.h69
-rw-r--r--Source/SPTableContentFilterController.m410
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj6
6 files changed, 372 insertions, 227 deletions
diff --git a/Source/SPFillView.h b/Source/SPFillView.h
new file mode 100644
index 00000000..21ea074b
--- /dev/null
+++ b/Source/SPFillView.h
@@ -0,0 +1,47 @@
+//
+// SPFillView.h
+// sequel-pro
+//
+// Created by Max Lohrmann on 09.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+/**
+ * SPFillView is a very simple NSView that will
+ * fill its whole view rect with a solid color.
+ * The color can be set in Interface Builder.
+ */
+@interface SPFillView : NSView
+{
+ NSColor *currentColor;
+}
+
+/**
+ * This method is invoked when unarchiving the View from the xib.
+ * The value is configured in IB under "User Defined Runtime Attributes"
+ */
+- (void)setSystemColorOfName:(NSString *)name;
+
+@end
diff --git a/Source/SPFillView.m b/Source/SPFillView.m
new file mode 100644
index 00000000..b2fae0fb
--- /dev/null
+++ b/Source/SPFillView.m
@@ -0,0 +1,61 @@
+//
+// SPFillView.m
+// sequel-pro
+//
+// Created by Max Lohrmann on 09.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+#import "SPFillView.h"
+
+@implementation SPFillView
+
+- (void)setSystemColorOfName:(NSString *)name
+{
+ //TODO: xibs after 10.6 support storing colors as user defined attributes so we don't need the detour via strings anymore
+ NSColorList *scl = [NSColorList colorListNamed:@"System"];
+ NSColor *color = [scl colorWithKey:name];
+ if(color) {
+ [color retain];
+ [currentColor release];
+ currentColor = color;
+ [self setNeedsDisplay:YES];
+ }
+}
+
+- (void)drawRect:(NSRect)dirtyRect {
+ if(currentColor) {
+ [currentColor set];
+ NSRectFill(dirtyRect);
+ }
+}
+
+- (void)dealloc
+{
+ [currentColor release];
+ [super dealloc];
+}
+
+@end
diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m
index da4fea05..7a48dccf 100644
--- a/Source/SPTableContent.m
+++ b/Source/SPTableContent.m
@@ -2375,9 +2375,9 @@ static void *TableContentKVOContext = &TableContentKVOContext;
else if(navigateAsHex) filterComparison = @"= (Hex String)";
// Store the filter details to use when loading the target table
- NSDictionary *filterSettings = [filterControllerInstance makeSerializedFilterForColumn:[refDictionary objectForKey:@"column"]
- operator:filterComparison
- values:@[targetFilterValue]];
+ NSDictionary *filterSettings = [SPTableContentFilterController makeSerializedFilterForColumn:[refDictionary objectForKey:@"column"]
+ operator:filterComparison
+ values:@[targetFilterValue]];
// If the link is within the current table, apply filter settings manually
if ([[refDictionary objectForKey:@"table"] isEqualToString:selectedTable]) {
diff --git a/Source/SPTableContentFilterController.h b/Source/SPTableContentFilterController.h
index 24935d2a..3d4029d6 100644
--- a/Source/SPTableContentFilterController.h
+++ b/Source/SPTableContentFilterController.h
@@ -28,13 +28,9 @@
//
// More info at <https://github.com/sequelpro/sequelpro>
-#import <Foundation/Foundation.h>
-
-@class SPSplitView;
@class SPTableData;
@class SPDatabaseDocument;
@class SPTablesList;
-@class SPTableContent;
@class SPContentFilterManager;
NSString * const SPTableContentFilterHeightChangedNotification;
@@ -62,34 +58,91 @@ NSString * const SPTableContentFilterHeightChangedNotification;
/**
* Makes the first NSTextField found in the rule editor the first responder
+ *
+ * MUST BE CALLED ON THE UI THREAD!
*/
- (void)focusFirstInputField;
+/**
+ * Will reconfigure the columns of the rule editor from the given array.
+ * Call with nil to reset the editor to its initial empty state.
+ * Existing rows will be removed in any case!
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
- (void)setColumns:(NSArray *)dataColumns;
-- (void)openContentFilterManagerForFilterType:(NSString *)filterType;
-
+/**
+ * Converts the current filter expression displayed in the UI into an
+ * SQL string suitable for use in a WHERE clause.
+ *
+ * @param isBINARY Indicates that the filter should use the BINARY qualifier for ignoring
+ * collations during search.
+ * @param err Upon return contains and object describing why the SQL conversion failed,
+ * if it failed or nil, if no errors occured.
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
- (NSString *)sqlWhereExpressionWithBinary:(BOOL)isBINARY error:(NSError **)err;
+/**
+ * Returns the current filter configuration in a serialized form that can be exported and
+ * reapplied later.
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
- (NSDictionary *)serializedFilter;
+
+/**
+ * Restores the filter rule configuration from a given dictionary.
+ * The current column configuration must match the schema that was used when generating
+ * the serialized data, otherwise the invalid rules will be ignored.
+ *
+ * @param serialized A dictionary previously generated by calling -serializedFilter.
+ * @return A serialized filter
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
- (void)restoreSerializedFilters:(NSDictionary *)serialized;
-- (NSDictionary *)makeSerializedFilterForColumn:(NSString *)colName operator:(NSString *)opName values:(NSArray *)values;
+/**
+ * Create a serialized filter from a given column, operator and operand.
+ * This is used when navigating foreign key links between tables to create the filter for the target table.
+ *
+ * @param colName Name of the column to filter (left side operand)
+ * @param opName Name of the filter (operator)
+ * @param values The values to filter with (right side operand)
+ * @return A serialized filter
+ *
+ * This method is thread-safe.
+ */
++ (NSDictionary *)makeSerializedFilterForColumn:(NSString *)colName operator:(NSString *)opName values:(NSArray *)values;
+/**
+ * The view height the rule editor needs in order to not have to resort to scrollbars
+ *
+ * SHOULD be called on the UI thread, or results may be inconsistent!
+ */
@property (readonly, assign, nonatomic) CGFloat preferredHeight;
/**
- * Indicates whether the rule editor has no expressions
+ * Indicates whether the rule editor has no filter expressions
+ *
+ * SHOULD be called on the UI thread, or results may be inconsistent!
*/
- (BOOL)isEmpty;
/**
* Adds a new row to the rule editor
+ *
+ * MUST BE CALLED ON THE UI THREAD!
*/
- (void)addFilterExpression;
/**
* Used when the rule editor wants to trigger filtering
+ *
+ * SHOULD be called on the UI thread, or results may be inconsistent!
*/
@property (assign, nonatomic) id target;
@property (assign, nonatomic) SEL action;
diff --git a/Source/SPTableContentFilterController.m b/Source/SPTableContentFilterController.m
index 0f928439..bdddad42 100644
--- a/Source/SPTableContentFilterController.m
+++ b/Source/SPTableContentFilterController.m
@@ -29,7 +29,6 @@
// More info at <https://github.com/sequelpro/sequelpro>
#import "SPTableContentFilterController.h"
-#import "SPTableContent.h"
#import "SPQueryController.h"
#import "SPDatabaseDocument.h"
#import "RegexKitLite.h"
@@ -47,12 +46,31 @@ typedef NS_ENUM(NSInteger, RuleNodeType) {
NSString * const SPTableContentFilterHeightChangedNotification = @"SPTableContentFilterHeightChanged";
+/**
+ * The type of filter rule that the current item represents.
+ */
const NSString * const SerFilterClass = @"filterClass";
+/**
+ * The current rule is a group row (an "AND" or "OR" expression with children)
+ */
const NSString * const SerFilterClassGroup = @"groupNode";
+/**
+ * The current rule is a filter expression
+ */
const NSString * const SerFilterClassExpression = @"expressionNode";
+/**
+ * Group Nodes only:
+ * Indicates whether the group is a conjunction.
+ * If YES, the children will be combined using "AND", otherwise using "OR".
+ */
const NSString * const SerFilterGroupIsConjunction = @"isConjunction";
+/**
+ * Group Nodes only:
+ * An array of child filter rules (which again can be group or expression rules)
+ */
const NSString * const SerFilterGroupChildren = @"children";
/**
+ * Expression Nodes only:
* The name of the column to filter in (left side expression)
*
* Legacy names:
@@ -60,10 +78,12 @@ const NSString * const SerFilterGroupChildren = @"children";
*/
const NSString * const SerFilterExprColumn = @"column";
/**
+ * Expression Nodes only:
* The data type grouping of the column for applicable filters
*/
const NSString * const SerFilterExprType = @"filterType";
/**
+ * Expression Nodes only:
* The title of the filter operator to apply
*
* Legacy names:
@@ -71,7 +91,8 @@ const NSString * const SerFilterExprType = @"filterType";
*/
const NSString * const SerFilterExprComparison = @"filterComparison";
/**
- * The values to apply the filter with
+ * Expression Nodes only:
+ * The values to apply the filter with (an array of 0 or more elements)
*
* Legacy names:
* @"filterValue", argumentField
@@ -79,11 +100,15 @@ const NSString * const SerFilterExprComparison = @"filterComparison";
*/
const NSString * const SerFilterExprValues = @"filterValues";
/**
+ * Expression Nodes only:
* the filter definition dictionary (as in ContentFilters.plist)
+ * for the filter represented by SerFilterExprComparison.
*
* This item is not designed to be serialized to disk
*/
-const NSString * const SerFilterExprDefinition = @"filterDefinition";
+const NSString * const SerFilterExprDefinition = @"_filterDefinition";
+
+#pragma mark -
@interface RuleNode : NSObject {
RuleNodeType type;
@@ -91,25 +116,6 @@ const NSString * const SerFilterExprDefinition = @"filterDefinition";
@property(assign, nonatomic) RuleNodeType type;
@end
-@implementation RuleNode
-
-@synthesize type = type;
-
-- (NSUInteger)hash {
- return type;
-}
-
-- (BOOL)isEqual:(id)other {
- if (other == self) return YES;
- if (other && [[other class] isEqual:[self class]] && [(RuleNode *)other type] == type) return YES;
-
- return NO;
-}
-
-@end
-
-#pragma mark -
-
@interface ColumnNode : RuleNode {
NSString *name;
NSString *typegrouping;
@@ -120,74 +126,12 @@ const NSString * const SerFilterExprDefinition = @"filterDefinition";
@property(retain, nonatomic) NSArray *operatorCache;
@end
-@implementation ColumnNode
-
-@synthesize name = name;
-@synthesize typegrouping = typegrouping;
-@synthesize operatorCache = operatorCache;
-
-- (instancetype)init
-{
- if((self = [super init])) {
- type = RuleNodeTypeColumn;
- }
- return self;
-}
-
-- (NSString *)description
-{
- return [NSString stringWithFormat:@"ColumnNode<%@@%p>",[self name],self];
-}
-
-- (NSUInteger)hash {
- return ([name hash] ^ [typegrouping hash] ^ [super hash]);
-}
-
-- (BOOL)isEqual:(id)other {
- if (other == self) return YES;
- if (other && [[other class] isEqual:[self class]] && [name isEqualToString:[other name]] && [typegrouping isEqualToString:[other typegrouping]]) return YES;
-
- return NO;
-}
-
-@end
-
-#pragma mark -
-
@interface StringNode : RuleNode {
NSString *value;
}
@property(copy, nonatomic) NSString *value;
@end
-@implementation StringNode
-
-@synthesize value = value;
-
-- (instancetype)init
-{
- if((self = [super init])) {
- type = RuleNodeTypeString;
- }
- return self;
-}
-
-- (NSUInteger)hash {
- return ([value hash] ^ [super hash]);
-}
-
-- (BOOL)isEqual:(id)other {
- if (other == self) return YES;
- if (other && [[other class] isEqual:[self class]] && [value isEqualToString:[(StringNode *)other value]]) return YES;
-
- return NO;
-}
-
-
-@end
-
-#pragma mark -
-
@interface OpNode : RuleNode {
// Note: The main purpose of this field is to have @"=" for column A and @"=" for column B to return NO in -isEqual:
// because otherwise NSRuleEditor will get confused and blow up.
@@ -200,42 +144,6 @@ const NSString * const SerFilterExprDefinition = @"filterDefinition";
@property (retain, nonatomic) NSDictionary *filter;
@end
-@implementation OpNode
-
-@synthesize parentColumn = parentColumn;
-@synthesize settings = settings;
-@synthesize filter = filter;
-
-- (instancetype)init
-{
- if((self = [super init])) {
- type = RuleNodeTypeOperator;
- }
- return self;
-}
-
-- (void)dealloc
-{
- [self setFilter:nil];
- [self setSettings:nil];
- [super dealloc];
-}
-
-- (NSUInteger)hash {
- return (([parentColumn hash] << 16) ^ [settings hash] ^ [super hash]);
-}
-
-- (BOOL)isEqual:(id)other {
- if (other == self) return YES;
- if (other && [[other class] isEqual:[self class]] && [settings isEqualToDictionary:[(OpNode *)other settings]] && [parentColumn isEqual:[other parentColumn]]) return YES;
-
- return NO;
-}
-
-@end
-
-#pragma mark -
-
@interface ArgNode : RuleNode {
NSDictionary *filter;
NSUInteger argIndex;
@@ -246,44 +154,6 @@ const NSString * const SerFilterExprDefinition = @"filterDefinition";
@property (assign, nonatomic) NSUInteger argIndex;
@end
-@implementation ArgNode
-
-@synthesize filter = filter;
-@synthesize argIndex = argIndex;
-@synthesize initialValue = initialValue;
-
-- (instancetype)init
-{
- if((self = [super init])) {
- type = RuleNodeTypeArgument;
- }
- return self;
-}
-
-- (void)dealloc
-{
- [self setInitialValue:nil];
- [self setFilter:nil];
- [super dealloc];
-}
-
-- (NSUInteger)hash {
- // initialValue does not count towards hash because two Args are not different if only the initialValue differs
- return ((argIndex << 16) ^ [filter hash] ^ [super hash]);
-}
-
-- (BOOL)isEqual:(id)other {
- // initialValue does not count towards isEqual: because two Args are not different if only the initialValue differs
- if (other == self) return YES;
- if (other && [[other class] isEqual:[self class]] && [filter isEqualToDictionary:[(ArgNode *)other filter]] && argIndex == [(ArgNode *)other argIndex]) return YES;
-
- return NO;
-}
-
-@end
-
-#pragma mark -
-
@interface ConnectorNode : RuleNode {
NSDictionary *filter;
NSUInteger labelIndex;
@@ -292,38 +162,6 @@ const NSString * const SerFilterExprDefinition = @"filterDefinition";
@property (assign, nonatomic) NSUInteger labelIndex;
@end
-@implementation ConnectorNode
-
-@synthesize filter = filter;
-@synthesize labelIndex = labelIndex;
-
-- (instancetype)init
-{
- if((self = [super init])) {
- type = RuleNodeTypeConnector;
- }
- return self;
-}
-
-- (void)dealloc
-{
- [self setFilter:nil];
- [super dealloc];
-}
-
-- (NSUInteger)hash {
- return ((labelIndex << 16) ^ [filter hash] ^ [super hash]);
-}
-
-- (BOOL)isEqual:(id)other {
- if (other == self) return YES;
- if (other && [[other class] isEqual:[self class]] && [filter isEqualToDictionary:[(ConnectorNode *)other filter]] && labelIndex == [(ConnectorNode *)other labelIndex]) return YES;
-
- return NO;
-}
-
-@end
-
#pragma mark -
@interface SPTableContentFilterController () <NSRuleEditorDelegate>
@@ -347,6 +185,7 @@ static void _addIfNotNil(NSMutableArray *array, id toAdd);
- (OpNode *)_operatorNamed:(NSString *)title forColumn:(ColumnNode *)col;
- (BOOL)_focusOnFieldInSubtree:(NSDictionary *)dict;
- (void)_resize;
+- (void)openContentFilterManagerForFilterType:(NSString *)filterType;
@end
@@ -640,7 +479,7 @@ static void _addIfNotNil(NSMutableArray *array, id toAdd);
- (void)_resize
{
// The situation with the sizing is a bit f'ed up:
- // - When this method is invoked the NSRuleEditor has not yet updated its required frame size
+ // - When -ruleEditorRowsDidChange: is invoked the NSRuleEditor has not yet updated its required frame size
// - We can't use KVO on -frame either, because SPTableContent will update the container size which
// ultimately also updates the NSRuleEditor's frame, causing a loop
// - Calling -sizeToFit works, but only when the NSRuleEditor is growing. It won't shrink
@@ -663,7 +502,9 @@ static void _addIfNotNil(NSMutableArray *array, id toAdd);
- (void)ruleEditorRowsDidChange:(NSNotification *)notification
{
- [self performSelector:@selector(_resize) withObject:nil afterDelay:0.2]; //TODO find a better way to trigger resize
+ //TODO find a better way to trigger resize
+ // We can't do this here, because it will cause rows to jump around when removing them (the add case works fine, though)
+ [self performSelector:@selector(_resize) withObject:nil afterDelay:0.2];
//[self _resize];
}
@@ -915,6 +756,8 @@ static void _addIfNotNil(NSMutableArray *array, id toAdd);
return nil;
}
+ if(err) *err = nil;
+
NSString *out = [filterString copy];
[filterString release];
@@ -1131,7 +974,7 @@ fail:
return nil;
}
-- (NSDictionary *)makeSerializedFilterForColumn:(NSString *)colName operator:(NSString *)opName values:(NSArray *)values
++ (NSDictionary *)makeSerializedFilterForColumn:(NSString *)colName operator:(NSString *)opName values:(NSArray *)values
{
return @{
SerFilterClass: SerFilterClassExpression,
@@ -1306,46 +1149,181 @@ BOOL SerIsGroup(NSDictionary *dict)
@end
-//TODO move
-@interface SPFillView : NSView
+#pragma mark -
+
+@implementation RuleNode
+
+@synthesize type = type;
+
+- (NSUInteger)hash {
+ return type;
+}
+
+- (BOOL)isEqual:(id)other {
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [(RuleNode *)other type] == type) return YES;
+
+ return NO;
+}
+
+@end
+
+@implementation ColumnNode
+
+@synthesize name = name;
+@synthesize typegrouping = typegrouping;
+@synthesize operatorCache = operatorCache;
+
+- (instancetype)init
+{
+ if((self = [super init])) {
+ type = RuleNodeTypeColumn;
+ }
+ return self;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"ColumnNode<%@@%p>",[self name],self];
+}
+
+- (NSUInteger)hash {
+ return ([name hash] ^ [typegrouping hash] ^ [super hash]);
+}
+
+- (BOOL)isEqual:(id)other {
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [name isEqualToString:[other name]] && [typegrouping isEqualToString:[other typegrouping]]) return YES;
+
+ return NO;
+}
+
+@end
+
+
+@implementation StringNode
+
+@synthesize value = value;
+
+- (instancetype)init
{
- NSColor *currentColor;
+ if((self = [super init])) {
+ type = RuleNodeTypeString;
+ }
+ return self;
}
-/**
- * This method is invoked when unarchiving the View from the xib.
- * The value is configured in IB under "User Defined Runtime Attributes"
- */
-- (void)setSystemColorOfName:(NSString *)name;
+- (NSUInteger)hash {
+ return ([value hash] ^ [super hash]);
+}
+
+- (BOOL)isEqual:(id)other {
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [value isEqualToString:[(StringNode *)other value]]) return YES;
+
+ return NO;
+}
@end
-@implementation SPFillView
+@implementation OpNode
+
+@synthesize parentColumn = parentColumn;
+@synthesize settings = settings;
+@synthesize filter = filter;
-- (void)setSystemColorOfName:(NSString *)name
+- (instancetype)init
{
- //TODO: xibs after 10.6 support storing colors as user defined attributes
- NSColorList *scl = [NSColorList colorListNamed:@"System"];
- NSColor *color = [scl colorWithKey:name];
- if(color) {
- [color retain];
- [currentColor release];
- currentColor = color;
- [self setNeedsDisplay:YES];
+ if((self = [super init])) {
+ type = RuleNodeTypeOperator;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [self setFilter:nil];
+ [self setSettings:nil];
+ [super dealloc];
+}
+
+- (NSUInteger)hash {
+ return (([parentColumn hash] << 16) ^ [settings hash] ^ [super hash]);
+}
+
+- (BOOL)isEqual:(id)other {
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [settings isEqualToDictionary:[(OpNode *)other settings]] && [parentColumn isEqual:[other parentColumn]]) return YES;
+
+ return NO;
+}
+
+@end
+
+@implementation ArgNode
+
+@synthesize filter = filter;
+@synthesize argIndex = argIndex;
+@synthesize initialValue = initialValue;
+
+- (instancetype)init
+{
+ if((self = [super init])) {
+ type = RuleNodeTypeArgument;
}
+ return self;
+}
+
+- (void)dealloc
+{
+ [self setInitialValue:nil];
+ [self setFilter:nil];
+ [super dealloc];
}
-- (void)drawRect:(NSRect)dirtyRect {
- if(currentColor) {
- [currentColor set];
- NSRectFill(dirtyRect);
+- (NSUInteger)hash {
+ // initialValue does not count towards hash because two Args are not different if only the initialValue differs
+ return ((argIndex << 16) ^ [filter hash] ^ [super hash]);
+}
+
+- (BOOL)isEqual:(id)other {
+ // initialValue does not count towards isEqual: because two Args are not different if only the initialValue differs
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [filter isEqualToDictionary:[(ArgNode *)other filter]] && argIndex == [(ArgNode *)other argIndex]) return YES;
+
+ return NO;
+}
+
+@end
+
+@implementation ConnectorNode
+
+@synthesize filter = filter;
+@synthesize labelIndex = labelIndex;
+
+- (instancetype)init
+{
+ if((self = [super init])) {
+ type = RuleNodeTypeConnector;
}
+ return self;
}
- (void)dealloc
{
- [currentColor release];
+ [self setFilter:nil];
[super dealloc];
}
+- (NSUInteger)hash {
+ return ((labelIndex << 16) ^ [filter hash] ^ [super hash]);
+}
+
+- (BOOL)isEqual:(id)other {
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [filter isEqualToDictionary:[(ConnectorNode *)other filter]] && labelIndex == [(ConnectorNode *)other labelIndex]) return YES;
+
+ return NO;
+}
+
@end
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index 79afe4e7..5b5dc546 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -391,6 +391,7 @@
58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF57D0F3B4E9700518E8E /* SPTableData.m */; };
73F70A961E4E547500636550 /* SPJSONFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 73F70A951E4E547500636550 /* SPJSONFormatter.m */; };
8D15AC340486D014006FF6A4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; };
+ 9BE765EBBDFD2F121C13D274 /* SPFillView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE768F3989033CEDDC2027E /* SPFillView.m */; };
9BE76F2886901784E4FD2321 /* SPFilterTableController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE76A3D5C9830E2F7738770 /* SPFilterTableController.m */; };
B51D6B9E114C310C0074704E /* toolbar-switch-to-table-triggers.png in Resources */ = {isa = PBXBuildFile; fileRef = B51D6B9D114C310C0074704E /* toolbar-switch-to-table-triggers.png */; };
B52460D70F8EF92300171639 /* SPArrayAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = B52460D40F8EF92300171639 /* SPArrayAdditions.m */; };
@@ -1129,7 +1130,9 @@
73F70A941E4E547500636550 /* SPJSONFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPJSONFormatter.h; sourceTree = "<group>"; };
73F70A951E4E547500636550 /* SPJSONFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPJSONFormatter.m; sourceTree = "<group>"; };
8D15AC370486D014006FF6A4 /* Sequel Pro.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Sequel Pro.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 9BE768F3989033CEDDC2027E /* SPFillView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFillView.m; sourceTree = "<group>"; };
9BE76A3D5C9830E2F7738770 /* SPFilterTableController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFilterTableController.m; sourceTree = "<group>"; };
+ 9BE76CC0CCE3A4A74E3E8D5E /* SPFillView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPFillView.h; sourceTree = "<group>"; };
9BE76F9BF9BDA2921CDD05AF /* SPFilterTableController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPFilterTableController.h; sourceTree = "<group>"; };
B51D6B9D114C310C0074704E /* toolbar-switch-to-table-triggers.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-table-triggers.png"; sourceTree = "<group>"; };
B52460D30F8EF92300171639 /* SPArrayAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPArrayAdditions.h; sourceTree = "<group>"; };
@@ -1978,6 +1981,8 @@
17DC8827126B32F300E9AAEC /* Table Views */,
17DF51241163C68600E3F396 /* Outline Views */,
17DC8828126B332F00E9AAEC /* Accessory Views */,
+ 9BE768F3989033CEDDC2027E /* SPFillView.m */,
+ 9BE76CC0CCE3A4A74E3E8D5E /* SPFillView.h */,
);
name = Views;
sourceTree = "<group>";
@@ -3277,6 +3282,7 @@
50E217B618174280009D3580 /* SPFavoriteColorSupport.m in Sources */,
1A564F74237E2E4958CA593A /* SPPillAttachmentCell.m in Sources */,
9BE76F2886901784E4FD2321 /* SPFilterTableController.m in Sources */,
+ 9BE765EBBDFD2F121C13D274 /* SPFillView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};