aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstuconnolly <stuart02@gmail.com>2012-08-15 10:47:43 +0000
committerstuconnolly <stuart02@gmail.com>2012-08-15 10:47:43 +0000
commit097676f7aaf6a95229fe5c57715e19716af57221 (patch)
tree707b647e339b84b62c9f264f0b2b0caa8c0b548c
parenta3ccba044ae0f73c4d79ce4ad8c8dd9374a1ad62 (diff)
downloadsequelpro-097676f7aaf6a95229fe5c57715e19716af57221.tar.gz
sequelpro-097676f7aaf6a95229fe5c57715e19716af57221.tar.bz2
sequelpro-097676f7aaf6a95229fe5c57715e19716af57221.zip
Move table content filter logic to it's own file.
-rw-r--r--Source/SPConstants.h3
-rw-r--r--Source/SPConstants.m3
-rw-r--r--Source/SPTableContent.h4
-rw-r--r--Source/SPTableContent.m237
-rw-r--r--Source/SPTableContentDataSource.m11
-rw-r--r--Source/SPTableContentDelegate.m1
-rw-r--r--Source/SPTableContentFilter.h43
-rw-r--r--Source/SPTableContentFilter.m249
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj6
9 files changed, 340 insertions, 217 deletions
diff --git a/Source/SPConstants.h b/Source/SPConstants.h
index 989dfdb4..3978db6f 100644
--- a/Source/SPConstants.h
+++ b/Source/SPConstants.h
@@ -274,6 +274,9 @@ extern NSString *SPThemesSupportFolder;
extern NSString *SPBundleSupportFolder;
extern NSString *SPDataSupportFolder;
+// Table filter
+extern NSString *SPTableContentFilterKey;
+
// Preference key constants
//
// General Prefpane
diff --git a/Source/SPConstants.m b/Source/SPConstants.m
index fa9fce47..b72c0217 100644
--- a/Source/SPConstants.m
+++ b/Source/SPConstants.m
@@ -68,6 +68,9 @@ NSString *SPThemesSupportFolder = @"Themes";
NSString *SPBundleSupportFolder = @"Bundles";
NSString *SPDataSupportFolder = @"Data";
+// Table filter
+NSString *SPTableContentFilterKey = @"filter";
+
// Preference key constants
// General Prefpane
NSString *SPDefaultFavorite = @"DefaultFavorite";
diff --git a/Source/SPTableContent.h b/Source/SPTableContent.h
index 013897f9..c9fa1d7c 100644
--- a/Source/SPTableContent.h
+++ b/Source/SPTableContent.h
@@ -299,11 +299,7 @@
- (NSString *)escapeFilterArgument:(NSString *)argument againstClause:(NSString *)clause;
- (void)openContentFilterManager;
-- (void)makeContentFilterHaveFocus;
- (NSArray*)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSInteger)columnIndex;
-- (void)updateFilterTableClause:(id)currentValue;
-- (NSString*)escapeFilterTableDefaultOperator:(NSString*)anOperator;
-
@end
diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m
index e7f9a5be..8b52958d 100644
--- a/Source/SPTableContent.m
+++ b/Source/SPTableContent.m
@@ -32,6 +32,7 @@
// More info at <http://code.google.com/p/sequel-pro/>
#import "SPTableContent.h"
+#import "SPTableContentFilter.h"
#import "SPDatabaseDocument.h"
#import "SPTableStructure.h"
#import "SPTableInfo.h"
@@ -66,6 +67,8 @@
#import <pthread.h>
#import <SPMySQL/SPMySQL.h>
+static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOperator";
+
@interface SPTableContent ()
- (BOOL)cancelRowEditing;
@@ -571,7 +574,7 @@
[filterTableData setObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
[columnDefinition objectForKey:@"name"], @"name",
[columnDefinition objectForKey:@"typegrouping"], @"typegrouping",
- [NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil], @"filter",
+ [NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil], SPTableContentFilterKey,
nil] forKey:[columnDefinition objectForKey:@"datacolumnindex"]];
#endif
@@ -2524,8 +2527,6 @@
#pragma mark -
-// Additional methods
-
/**
* Sets the connection (received from SPDatabaseDocument) and makes things that have to be done only once
*/
@@ -3328,7 +3329,7 @@
#ifndef SP_REFACTOR
[sheet orderOut:self];
- if([contextInfo isEqualToString:@"setdefaultoperator"]) {
+ if([contextInfo isEqualToString:SPTableFilterSetDefaultOperator]) {
if(returnCode) {
if(filterTableDefaultOperator) [filterTableDefaultOperator release];
NSString *newOperator = [filterTableSetDefaultOperatorValue stringValue];
@@ -3559,11 +3560,13 @@
[filterTableView abortEditing];
- if(filterTableData && [filterTableData count]) {
+ if (filterTableData && [filterTableData count]) {
// Clear filter data
- for(NSNumber *col in [filterTableData allKeys])
- [[filterTableData objectForKey:col] setObject:[NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil] forKey:@"filter"];
+ for (NSNumber *col in [filterTableData allKeys])
+ {
+ [[filterTableData objectForKey:col] setObject:[NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil] forKey:SPTableContentFilterKey];
+ }
[filterTableView reloadData];
[filterTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
@@ -3571,8 +3574,6 @@
// Reload table
[self filterTable:nil];
-
-
}
#endif
}
@@ -3593,6 +3594,7 @@
[filterTableWhereClause setCompletionWasReinvokedAutomatically:NO];
[filterTableWhereClause insertText:@""];
[filterTableWhereClause didChangeText];
+
[[filterTableView window] makeFirstResponder:filterTableView];
#endif
}
@@ -3607,13 +3609,15 @@
if (filterTableNegate) {
[filterTableQueryTitle setStringValue:NSLocalizedString(@"WHERE NOT query", @"Title of filter preview area when the query WHERE is negated")];
- } else {
+ }
+ else {
[filterTableQueryTitle setStringValue:NSLocalizedString(@"WHERE query", @"Title of filter preview area when the query WHERE is normal")];
}
// If live search is set perform filtering
- if([filterTableLiveSearchCheckbox state] == NSOnState)
+ if ([filterTableLiveSearchCheckbox state] == NSOnState) {
[self filterTable:filterTableFilterButton];
+ }
#endif
}
@@ -3629,8 +3633,9 @@
[filterTableDistinctCheckbox setState:(filterTableDistinct) ? NSOnState : NSOffState];
// If live search is set perform filtering
- if([filterTableLiveSearchCheckbox state] == NSOnState)
+ if ([filterTableLiveSearchCheckbox state] == NSOnState) {
[self filterTable:filterTableFilterButton];
+ }
#endif
}
@@ -3645,13 +3650,16 @@
[filterTableWindow makeFirstResponder:filterTableView];
// Load history
- if([prefs objectForKey:SPFilterTableDefaultOperatorLastItems]) {
+ if ([prefs objectForKey:SPFilterTableDefaultOperatorLastItems]) {
NSMutableArray *lastItems = [NSMutableArray array];
- NSString *defaultItem = @"LIKE '%@%'";
- [lastItems addObject:defaultItem];
+
+ [lastItems addObject:@"LIKE '%@%'"];
- for(NSString* item in [prefs objectForKey:SPFilterTableDefaultOperatorLastItems])
+ for (NSString* item in [prefs objectForKey:SPFilterTableDefaultOperatorLastItems])
+ {
[lastItems addObject:item];
+ }
+
[filterTableSetDefaultOperatorValue removeAllItems];
[filterTableSetDefaultOperatorValue addItemsWithObjectValues:lastItems];
}
@@ -3662,7 +3670,7 @@
modalForWindow:filterTableWindow
modalDelegate:self
didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
- contextInfo:@"setdefaultoperator"];
+ contextInfo:SPTableFilterSetDefaultOperator];
#endif
}
@@ -3676,8 +3684,9 @@
#ifndef SP_REFACTOR
// If live search is set perform filtering
- if([filterTableLiveSearchCheckbox state] == NSOnState)
+ if ([filterTableLiveSearchCheckbox state] == NSOnState) {
[self filterTable:filterTableFilterButton];
+ }
#endif
}
@@ -3705,7 +3714,7 @@
/**
* Provide a getter for the table's sort column name
*/
-- (NSString *) sortColumnName
+- (NSString *)sortColumnName
{
if (!sortCol || !dataColumns) return nil;
@@ -3715,7 +3724,7 @@
/**
* Provide a getter for the table current sort order
*/
-- (BOOL) sortColumnIsAscending
+- (BOOL)sortColumnIsAscending
{
return !isDesc;
}
@@ -4234,194 +4243,6 @@
return YES;
}
-/**
- * Escape passed operator for usage as filterTableDefaultOperator
- */
-- (NSString*)escapeFilterTableDefaultOperator:(NSString*)anOperator
-{
-
- if(anOperator == nil) return @"";
-
- NSMutableString *newOp = [[[NSMutableString alloc] initWithCapacity:[anOperator length]] autorelease];
- [newOp setString:anOperator];
- [newOp replaceOccurrencesOfRegex:@"%" withString:@"%%"];
- [newOp replaceOccurrencesOfRegex:@"(?<!`)@(?!=`)" withString:@"%@"];
- return newOp;
-}
-
-/**
- * Update WHERE clause in Filter Table Window
- *
- * @param currentValue If currentValue == nil take the data from filterTableData, if currentValue == filterTableSearchAllFields
- * generate a WHERE clause to search in all given fields, if currentValue == a string take this string as table cell data of the
- * currently edited table cell
- */
-- (void)updateFilterTableClause:(id)currentValue
-{
-#ifndef SP_REFACTOR
- NSMutableString *clause = [NSMutableString string];
- NSInteger numberOfRows = [self numberOfRowsInTableView:filterTableView];
- NSInteger numberOfCols = [[filterTableView tableColumns] count];
- NSInteger numberOfValues = 0;
- NSRange opRange, defopRange;
-
- BOOL lookInAllFields = NO;
-
- NSString *re1 = @"^\\s*(<[=>]?|>=?|!?=|≠|≤|≥)\\s*(.*?)\\s*$";
- NSString *re2 = @"^\\s*(.*)\\s+(.*?)\\s*$";
- NSInteger editedRow = [filterTableView editedRow];
-
-
- if(currentValue == filterTableSearchAllFields) {
- numberOfRows = 1;
- lookInAllFields = YES;
- }
-
- [filterTableWhereClause setString:@""];
-
- for(NSInteger i=0; i<numberOfRows; i++) {
- numberOfValues = 0;
- for(NSInteger anIndex=0; anIndex<numberOfCols; anIndex++) {
- NSString *filterCell;
- NSDictionary *filterCellData = [NSDictionary dictionaryWithDictionary:[filterTableData objectForKey:[NSString stringWithFormat:@"%d",anIndex]]];
-
- // Take filterTableData
- if(currentValue == nil) {
- filterCell = NSArrayObjectAtIndex([filterCellData objectForKey:@"filter"], i);
- // Take last edited value to create the OR clause
- } else if(lookInAllFields) {
- if(lastEditedFilterTableValue && [lastEditedFilterTableValue length]) {
- filterCell = lastEditedFilterTableValue;
- } else {
- [filterTableWhereClause setString:@""];
- [filterTableWhereClause insertText:@""];
- [filterTableWhereClause scrollRangeToVisible:NSMakeRange(0, 0)];
-
- // If live search is set perform filtering
- if([filterTableLiveSearchCheckbox state] == NSOnState)
- [self filterTable:filterTableFilterButton];
-
- }
- // Take value from currently edited table cell
- } else if([currentValue isKindOfClass:[NSString class]]){
- if(i == editedRow && anIndex == [[NSArrayObjectAtIndex([filterTableView tableColumns], [filterTableView editedColumn]) identifier] integerValue])
- filterCell = (NSString*)currentValue;
- else
- filterCell = NSArrayObjectAtIndex([filterCellData objectForKey:@"filter"], i);
- }
-
- if([filterCell length]) {
-
- // Recode special operators
- filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≠" withString:@"!="];
- filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≤" withString:@"<="];
- filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≥" withString:@">="];
-
- if(numberOfValues)
- [clause appendString:(lookInAllFields) ? @" OR " : @" AND "];
-
- NSString *fieldName = [[filterCellData objectForKey:@"name"] backtickQuotedString];
- NSString *filterTableDefaultOperatorWithFieldName = [filterTableDefaultOperator stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName];
- opRange = [filterCell rangeOfString:@"`@`"];
- defopRange = [filterTableDefaultOperator rangeOfString:@"`@`"];
-
- // if cell data begins with ' or " treat it as it is
- // by checking if default operator by itself contains a ' or " - if so
- // remove first and if given the last ' or "
- if([filterCell isMatchedByRegex:@"^\\s*['\"]"]) {
- if([filterTableDefaultOperator isMatchedByRegex:@"['\"]"]) {
- NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:@"^\\s*(['\"])(.*)\\1\\s*$"];
- if([matches count] && [matches = NSArrayObjectAtIndex(matches,0) count] == 3) {
- [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, NSArrayObjectAtIndex(matches, 2)];
- } else {
- matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:@"^\\s*(['\"])(.*)\\s*$"];
- if([matches count] && [matches = NSArrayObjectAtIndex(matches,0) count] == 3)
- [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, NSArrayObjectAtIndex(matches, 2)];
- }
- } else {
- [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, filterCell];
- }
- }
-
- // if cell contains the field name placeholder
- else if(opRange.length || defopRange.length) {
- filterCell = [filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName];
- if(defopRange.length)
- [clause appendFormat:filterTableDefaultOperatorWithFieldName, [filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName]];
- else
- [clause appendString:[filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName]];
- }
-
- // if cell is equal to NULL
- else if([filterCell isMatchedByRegex:@"(?i)^\\s*null\\s*$"]) {
- [clause appendFormat:@"%@ IS NULL", fieldName];
- }
-
- // if cell starts with an operator
- else if([filterCell isMatchedByRegex:re1]) {
- NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:re1];
- if([matches count] && [matches = NSArrayObjectAtIndex(matches,0) count] == 3)
- [clause appendFormat:@"%@ %@ %@", fieldName, NSArrayObjectAtIndex(matches, 1), NSArrayObjectAtIndex(matches, 2)];
- }
-
- // if cell consists of at least two words treat the first as operator and the rest as argument
- else if([filterCell isMatchedByRegex:re2]) {
- NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:re2];
- if([matches count] && [matches = NSArrayObjectAtIndex(matches,0) count] == 3)
- [clause appendFormat:@"%@ %@ %@", fieldName, [NSArrayObjectAtIndex(matches, 1) uppercaseString], NSArrayObjectAtIndex(matches, 2)];
- }
-
- // apply the default operator
- else {
- [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, filterCell];
- }
-
- numberOfValues++;
- }
- }
- if(numberOfValues)
- [clause appendString:@"\nOR\n"];
- }
-
- // Remove last " OR " if any
- if([clause length] > 3)
- [filterTableWhereClause setString:[clause substringToIndex:([clause length]-4)]];
- else
- [filterTableWhereClause setString:@""];
-
- // Update syntax highlighting and uppercasing
- [filterTableWhereClause insertText:@""];
- [filterTableWhereClause scrollRangeToVisible:NSMakeRange(0, 0)];
-
- // If live search is set perform filtering
- if([filterTableLiveSearchCheckbox state] == NSOnState)
- [self filterTable:filterTableFilterButton];
-#endif
-}
-
-/**
- * Makes the content filter field have focus by making it the first responder.
- */
-- (void)makeContentFilterHaveFocus
-{
-
- NSDictionary *filter = [[contentFilters objectForKey:compareType] objectAtIndex:[[compareField selectedItem] tag]];
-
- if([filter objectForKey:@"NumberOfArguments"]) {
- NSUInteger numOfArgs = [[filter objectForKey:@"NumberOfArguments"] integerValue];
- switch(numOfArgs) {
- case 2:
- [[firstBetweenField window] makeFirstResponder:firstBetweenField];
- break;
- case 1:
- [[argumentField window] makeFirstResponder:argumentField];
- break;
- default:
- [[compareField window] makeFirstResponder:compareField];
- }
- }
-}
-
- (void)setFieldEditorSelectedRange:(NSRange)aRange
{
[tableContentView setFieldEditorSelectedRange:aRange];
diff --git a/Source/SPTableContentDataSource.m b/Source/SPTableContentDataSource.m
index 484df453..21743464 100644
--- a/Source/SPTableContentDataSource.m
+++ b/Source/SPTableContentDataSource.m
@@ -31,6 +31,7 @@
// More info at <http://code.google.com/p/sequel-pro/>
#import "SPTableContentDataSource.h"
+#import "SPTableContentFilter.h"
#import "SPDataStorage.h"
#import "SPCopyTable.h"
#import "SPTablesList.h"
@@ -47,7 +48,7 @@
{
#ifndef SP_REFACTOR
if (tableView == filterTableView) {
- return filterTableIsSwapped ? [filterTableData count] : [[[filterTableData objectForKey:@"0"] objectForKey:@"filter"] count];
+ return filterTableIsSwapped ? [filterTableData count] : [[[filterTableData objectForKey:@"0"] objectForKey:SPTableContentFilterKey] count];
}
else
#endif
@@ -69,10 +70,10 @@
return [[[NSTableHeaderCell alloc] initTextCell:[[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:@"name"]] autorelease];
}
else {
- return NSArrayObjectAtIndex([[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:@"filter"], [[tableColumn identifier] integerValue] - 1);
+ return NSArrayObjectAtIndex([[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:SPTableContentFilterKey], [[tableColumn identifier] integerValue] - 1);
}
else {
- return NSArrayObjectAtIndex([[filterTableData objectForKey:[tableColumn identifier]] objectForKey:@"filter"], rowIndex);
+ return NSArrayObjectAtIndex([[filterTableData objectForKey:[tableColumn identifier]] objectForKey:SPTableContentFilterKey], rowIndex);
}
}
else
@@ -124,10 +125,10 @@
#ifndef SP_REFACTOR
if(tableView == filterTableView) {
if (filterTableIsSwapped) {
- [[[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:@"filter"] replaceObjectAtIndex:([[tableColumn identifier] integerValue] - 1) withObject:(NSString *)object];
+ [[[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:SPTableContentFilterKey] replaceObjectAtIndex:([[tableColumn identifier] integerValue] - 1) withObject:(NSString *)object];
}
else {
- [[[filterTableData objectForKey:[tableColumn identifier]] objectForKey:@"filter"] replaceObjectAtIndex:rowIndex withObject:(NSString *)object];
+ [[[filterTableData objectForKey:[tableColumn identifier]] objectForKey:SPTableContentFilterKey] replaceObjectAtIndex:rowIndex withObject:(NSString *)object];
}
[self updateFilterTableClause:nil];
diff --git a/Source/SPTableContentDelegate.m b/Source/SPTableContentDelegate.m
index 3a0be224..9cae98d8 100644
--- a/Source/SPTableContentDelegate.m
+++ b/Source/SPTableContentDelegate.m
@@ -31,6 +31,7 @@
// More info at <http://code.google.com/p/sequel-pro/>
#import "SPTableContentDelegate.h"
+#import "SPTableContentFilter.h"
#ifndef SP_REFACTOR /* headers */
#import "SPAppController.h"
#endif
diff --git a/Source/SPTableContentFilter.h b/Source/SPTableContentFilter.h
new file mode 100644
index 00000000..1efe8039
--- /dev/null
+++ b/Source/SPTableContentFilter.h
@@ -0,0 +1,43 @@
+//
+// $Id$
+//
+// SPTableContentFilter.h
+// sequel-pro
+//
+// Created by Stuart Connolly (stuconnolly.com) on August 14, 2012.
+// Copyright (c) 2012 Stuart Connolly. 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.
+
+#import "SPTableContent.h"
+
+@interface SPTableContent (SPTableContentFilter)
+
+#ifndef SP_REFACTOR
+
+- (void)makeContentFilterHaveFocus;
+- (void)updateFilterTableClause:(id)currentValue;
+- (NSString*)escapeFilterTableDefaultOperator:(NSString*)operator;
+
+#endif
+
+@end
diff --git a/Source/SPTableContentFilter.m b/Source/SPTableContentFilter.m
new file mode 100644
index 00000000..8d2c80ce
--- /dev/null
+++ b/Source/SPTableContentFilter.m
@@ -0,0 +1,249 @@
+//
+// $Id$
+//
+// SPTableContentFilter.m
+// sequel-pro
+//
+// Created by Stuart Connolly (stuconnolly.com) on August 14, 2012.
+// Copyright (c) 2012 Stuart Connolly. 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.
+
+#import "SPTableContentFilter.h"
+#import "RegexKitLite.h"
+#import "SPCopyTable.h"
+
+@implementation SPTableContent (SPTableContentFilter)
+
+#ifndef SP_REFACTOR
+
+/**
+ * Escape passed operator for usage as filterTableDefaultOperator.
+ */
+- (NSString*)escapeFilterTableDefaultOperator:(NSString *)operator
+{
+ if (!operator) return @"";
+
+ NSMutableString *newOp = [[[NSMutableString alloc] initWithCapacity:[operator length]] autorelease];
+
+ [newOp setString:operator];
+ [newOp replaceOccurrencesOfRegex:@"%" withString:@"%%"];
+ [newOp replaceOccurrencesOfRegex:@"(?<!`)@(?!=`)" withString:@"%@"];
+
+ return newOp;
+}
+
+/**
+ * Update WHERE clause in filter table window.
+ *
+ * @param currentValue If currentValue == nil take the data from filterTableData, if currentValue == filterTableSearchAllFields
+ * generate a WHERE clause to search in all given fields, if currentValue == a string take this string as table cell data of the
+ * currently edited table cell
+ */
+- (void)updateFilterTableClause:(id)currentValue
+{
+#ifndef SP_REFACTOR
+ NSMutableString *clause = [NSMutableString string];
+ NSInteger numberOfRows = [self numberOfRowsInTableView:filterTableView];
+ NSInteger numberOfCols = [[filterTableView tableColumns] count];
+ NSInteger numberOfValues = 0;
+ NSRange opRange, defopRange;
+
+ BOOL lookInAllFields = NO;
+
+ NSString *re1 = @"^\\s*(<[=>]?|>=?|!?=|≠|≤|≥)\\s*(.*?)\\s*$";
+ NSString *re2 = @"^\\s*(.*)\\s+(.*?)\\s*$";
+
+ NSInteger editedRow = [filterTableView editedRow];
+
+ if (currentValue == filterTableSearchAllFields) {
+ numberOfRows = 1;
+ lookInAllFields = YES;
+ }
+
+ [filterTableWhereClause setString:@""];
+
+ for (NSInteger i = 0; i < numberOfRows; i++)
+ {
+ numberOfValues = 0;
+
+ for (NSInteger anIndex = 0; anIndex < numberOfCols; anIndex++)
+ {
+ NSString *filterCell;
+ NSDictionary *filterCellData = [NSDictionary dictionaryWithDictionary:[filterTableData objectForKey:[NSString stringWithFormat:@"%d", anIndex]]];
+
+ // Take filterTableData
+ if (!currentValue) {
+ filterCell = NSArrayObjectAtIndex([filterCellData objectForKey:SPTableContentFilterKey], i);
+ }
+ // Take last edited value to create the OR clause
+ else if (lookInAllFields) {
+ if (lastEditedFilterTableValue && [lastEditedFilterTableValue length]) {
+ filterCell = lastEditedFilterTableValue;
+ }
+ else {
+ [filterTableWhereClause setString:@""];
+ [filterTableWhereClause insertText:@""];
+ [filterTableWhereClause scrollRangeToVisible:NSMakeRange(0, 0)];
+
+ // If live search is set perform filtering
+ if ([filterTableLiveSearchCheckbox state] == NSOnState) {
+ [self filterTable:filterTableFilterButton];
+ }
+ }
+ }
+ // Take value from currently edited table cell
+ else if ([currentValue isKindOfClass:[NSString class]]) {
+ if (i == editedRow && anIndex == [[NSArrayObjectAtIndex([filterTableView tableColumns], [filterTableView editedColumn]) identifier] integerValue]) {
+ filterCell = (NSString*)currentValue;
+ }
+ else {
+ filterCell = NSArrayObjectAtIndex([filterCellData objectForKey:SPTableContentFilterKey], i);
+ }
+ }
+
+ if ([filterCell length]) {
+
+ // Recode special operators
+ filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≠" withString:@"!="];
+ filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≤" withString:@"<="];
+ filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≥" withString:@">="];
+
+ if (numberOfValues) {
+ [clause appendString:(lookInAllFields) ? @" OR " : @" AND "];
+ }
+
+ NSString *fieldName = [[filterCellData objectForKey:@"name"] backtickQuotedString];
+ NSString *filterTableDefaultOperatorWithFieldName = [filterTableDefaultOperator stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName];
+
+ opRange = [filterCell rangeOfString:@"`@`"];
+ defopRange = [filterTableDefaultOperator rangeOfString:@"`@`"];
+
+ // if cell data begins with ' or " treat it as it is
+ // by checking if default operator by itself contains a ' or " - if so
+ // remove first and if given the last ' or "
+ if ([filterCell isMatchedByRegex:@"^\\s*['\"]"]) {
+ if ([filterTableDefaultOperator isMatchedByRegex:@"['\"]"]) {
+ NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:@"^\\s*(['\"])(.*)\\1\\s*$"];
+
+ if ([matches count] && [matches = NSArrayObjectAtIndex(matches, 0) count] == 3) {
+ [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, NSArrayObjectAtIndex(matches, 2)];
+ }
+ else {
+ matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:@"^\\s*(['\"])(.*)\\s*$"];
+
+ if ([matches count] && [matches = NSArrayObjectAtIndex(matches, 0) count] == 3) {
+ [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, NSArrayObjectAtIndex(matches, 2)];
+ }
+ }
+ }
+ else {
+ [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, filterCell];
+ }
+ }
+ // If cell contains the field name placeholder
+ else if (opRange.length || defopRange.length) {
+ filterCell = [filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName];
+
+ if (defopRange.length) {
+ [clause appendFormat:filterTableDefaultOperatorWithFieldName, [filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName]];
+ }
+ else {
+ [clause appendString:[filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName]];
+ }
+ }
+ // If cell is equal to NULL
+ else if ([filterCell isMatchedByRegex:@"(?i)^\\s*null\\s*$"]) {
+ [clause appendFormat:@"%@ IS NULL", fieldName];
+ }
+ // If cell starts with an operator
+ else if ([filterCell isMatchedByRegex:re1]) {
+ NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:re1];
+
+ if ([matches count] && [matches = NSArrayObjectAtIndex(matches, 0) count] == 3) {
+ [clause appendFormat:@"%@ %@ %@", fieldName, NSArrayObjectAtIndex(matches, 1), NSArrayObjectAtIndex(matches, 2)];
+ }
+ }
+ // If cell consists of at least two words treat the first as operator and the rest as argument
+ else if ([filterCell isMatchedByRegex:re2]) {
+ NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:re2];
+
+ if ([matches count] && [matches = NSArrayObjectAtIndex(matches,0) count] == 3) {
+ [clause appendFormat:@"%@ %@ %@", fieldName, [NSArrayObjectAtIndex(matches, 1) uppercaseString], NSArrayObjectAtIndex(matches, 2)];
+ }
+ }
+ // Apply the default operator
+ else {
+ [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, filterCell];
+ }
+
+ numberOfValues++;
+ }
+ }
+
+ if (numberOfValues) {
+ [clause appendString:@"\nOR\n"];
+ }
+ }
+
+ // Remove last " OR " if any
+ [filterTableWhereClause setString:[clause length] > 3 ? [clause substringToIndex:([clause length] - 4)] : @""];
+
+ // Update syntax highlighting and uppercasing
+ [filterTableWhereClause insertText:@""];
+ [filterTableWhereClause scrollRangeToVisible:NSMakeRange(0, 0)];
+
+ // If live search is set perform filtering
+ if ([filterTableLiveSearchCheckbox state] == NSOnState) {
+ [self filterTable:filterTableFilterButton];
+ }
+#endif
+}
+
+/**
+ * Makes the content filter field have focus by making it the first responder.
+ */
+- (void)makeContentFilterHaveFocus
+{
+ NSDictionary *filter = [[contentFilters objectForKey:compareType] objectAtIndex:[[compareField selectedItem] tag]];
+
+ if ([filter objectForKey:@"NumberOfArguments"]) {
+
+ NSUInteger numOfArgs = [[filter objectForKey:@"NumberOfArguments"] integerValue];
+
+ switch (numOfArgs)
+ {
+ case 2:
+ [[firstBetweenField window] makeFirstResponder:firstBetweenField];
+ break;
+ case 1:
+ [[argumentField window] makeFirstResponder:argumentField];
+ break;
+ default:
+ [[compareField window] makeFirstResponder:compareField];
+ }
+ }
+}
+
+#endif
+
+@end
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index 010f2fad..5df189d2 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -35,6 +35,7 @@
1717F9DB1558114D0065C036 /* OCMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11D44DEF118F5887002AA43C /* OCMock.framework */; };
1717FA401558313A0065C036 /* RegexKitLite.m in Sources */ = {isa = PBXBuildFile; fileRef = 296DC8AB0F909194002A3258 /* RegexKitLite.m */; };
1717FA43155831600065C036 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 296DC8BE0F9091DF002A3258 /* libicucore.dylib */; };
+ 171B374115DA654300EBC7AB /* SPTableContentFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 171B374015DA654300EBC7AB /* SPTableContentFilter.m */; };
17292443107AC41000B21980 /* SPXMLExporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 17292442107AC41000B21980 /* SPXMLExporter.m */; };
172A65110F7BED7A001E861A /* SPConsoleMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 172A65100F7BED7A001E861A /* SPConsoleMessage.m */; };
173284EA1088FEDE0062E892 /* SPConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 173284E91088FEDE0062E892 /* SPConstants.m */; };
@@ -614,6 +615,8 @@
1713C75E140D8D5900CFD461 /* SPQueryConsoleDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPQueryConsoleDataSource.m; sourceTree = "<group>"; };
17148563125F5FF500321285 /* SPDatabaseCharacterSets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDatabaseCharacterSets.h; sourceTree = "<group>"; };
17148564125F5FF500321285 /* SPDatabaseCharacterSets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDatabaseCharacterSets.m; sourceTree = "<group>"; };
+ 171B373F15DA654300EBC7AB /* SPTableContentFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTableContentFilter.h; sourceTree = "<group>"; };
+ 171B374015DA654300EBC7AB /* SPTableContentFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTableContentFilter.m; sourceTree = "<group>"; };
17292441107AC41000B21980 /* SPXMLExporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPXMLExporter.h; sourceTree = "<group>"; };
17292442107AC41000B21980 /* SPXMLExporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPXMLExporter.m; sourceTree = "<group>"; };
172A650F0F7BED7A001E861A /* SPConsoleMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPConsoleMessage.h; sourceTree = "<group>"; };
@@ -1492,6 +1495,8 @@
children = (
17E6414E0EF01EF6001BC333 /* SPTableContent.h */,
17E6414F0EF01EF6001BC333 /* SPTableContent.m */,
+ 171B373F15DA654300EBC7AB /* SPTableContentFilter.h */,
+ 171B374015DA654300EBC7AB /* SPTableContentFilter.m */,
17386E0C1519257E002DC206 /* SPTableContentDelegate.h */,
17386E0D1519257E002DC206 /* SPTableContentDelegate.m */,
17386E0915192526002DC206 /* SPTableContentDataSource.h */,
@@ -3295,6 +3300,7 @@
58DF9F3315AB26C2003B4330 /* SPDateAdditions.m in Sources */,
58DF9F7315AB8509003B4330 /* SPSplitView.m in Sources */,
58DFC91615CB3501003B4330 /* BGHUDButtonCell.m in Sources */,
+ 171B374115DA654300EBC7AB /* SPTableContentFilter.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};