diff options
author | Max <dmoagx@users.noreply.github.com> | 2018-05-08 01:59:50 +0200 |
---|---|---|
committer | Max <dmoagx@users.noreply.github.com> | 2018-05-08 01:59:50 +0200 |
commit | 1838bb50da412fc3875aeb95ab082a9721000efc (patch) | |
tree | c8f924a4affb67181bc25eaf9e35be1d0ae8a37c /Source | |
parent | d463f555748c9010ab7b570851b8648fa9e117a8 (diff) | |
download | sequelpro-1838bb50da412fc3875aeb95ab082a9721000efc.tar.gz sequelpro-1838bb50da412fc3875aeb95ab082a9721000efc.tar.bz2 sequelpro-1838bb50da412fc3875aeb95ab082a9721000efc.zip |
Move advanced filter table code into its own class + xib
Diffstat (limited to 'Source')
-rw-r--r-- | Source/SPFilterTableController.h | 129 | ||||
-rw-r--r-- | Source/SPFilterTableController.m | 698 | ||||
-rw-r--r-- | Source/SPHistoryController.m | 2 | ||||
-rw-r--r-- | Source/SPTableContent.h | 34 | ||||
-rw-r--r-- | Source/SPTableContent.m | 877 |
5 files changed, 995 insertions, 745 deletions
diff --git a/Source/SPFilterTableController.h b/Source/SPFilterTableController.h new file mode 100644 index 00000000..7bf02593 --- /dev/null +++ b/Source/SPFilterTableController.h @@ -0,0 +1,129 @@ +// +// SPFilterTableController.h +// sequel-pro +// +// Created by Max Lohrmann on 07.05.18. +// Copyright (c) 2018 Max Lohrmann. All rights reserved. +// Relocated from existing files. Previous copyright applies. +// +// 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> + +@class SPSplitView; +@class SPCopyTable; +@class SPTextView; + +@interface SPFilterTableController : NSWindowController +{ + IBOutlet SPSplitView *filterTableSplitView; + IBOutlet NSButton *filterTableFilterButton; + IBOutlet NSButton *filterTableClearButton; + IBOutlet NSButton *filterTableSearchAllFields; + + IBOutlet SPCopyTable *filterTableView; + + IBOutlet NSButton *filterTableLiveSearchCheckbox; + IBOutlet NSButton *filterTableNegateCheckbox; + IBOutlet NSButton *filterTableDistinctCheckbox; + + IBOutlet NSTextField *filterTableQueryTitle; + IBOutlet SPTextView *filterTableWhereClause; + + IBOutlet NSPanel *filterTableSetDefaultOperatorSheet; + IBOutlet NSComboBox* filterTableSetDefaultOperatorValue; + + NSUserDefaults *prefs; + + NSMutableDictionary *filterTableData; + BOOL filterTableNegate; + BOOL filterTableDistinct; + BOOL filterTableIsSwapped; + NSString *filterTableDefaultOperator; + NSString *lastEditedFilterTableValue; + + id target; + SEL action; +} + +/** + * Puts the filter table window on screen + * + * MUST BE CALLED ON THE UI THREAD! + */ +- (void)showFilterTableWindow; + +/** + * Restores filter table content state from serialized data + * + * MUST BE CALLED ON THE UI THREAD! + */ +- (void)setFilterTableData:(NSData *)arcData; + +/** + * Returns the current contents of the filter table window as serialized data + * + * MUST BE CALLED ON THE UI THREAD! + */ +- (NSData *)filterTableData; + +/** + * The SQL expression to use as filter. + * Can be nil if no filter is set! + * + * MUST BE CALLED ON THE UI THREAD! + */ +- (NSString *)tableFilterString; + +/** + * Will reconfigure the columns of the filter table view from the given array. + * Call with nil to reset the table view to its initial empty state. + * + * MUST BE CALLED ON THE UI THREAD! + */ +- (void)setColumns:(NSArray *)dataColumns; + +/** + * Will return YES if the SQL expression returned by -tableFilterString should be + * used in a "SELECT DISTINCT …" query. + * + * Results may be inconsistent if not called on the main thread! + */ +- (BOOL)isDistinct; + +/** + * Use this method to make the filter window indicate an error state after executing the filter. + * Pass 0 for error ID to indicate an OK state. + * + * MUST BE CALLED ON THE UI THREAD! + */ +- (void)setFilterError:(NSUInteger)errorID message:(NSString *)message sqlstate:(NSString *)sqlstate; + +/** + * Used when the filter table window wants to trigger filtering + * + * Results may be inconsistent if not called on the main thread! + */ +@property (assign, nonatomic) id target; +@property (assign, nonatomic) SEL action; + +@end diff --git a/Source/SPFilterTableController.m b/Source/SPFilterTableController.m new file mode 100644 index 00000000..dc1bf98a --- /dev/null +++ b/Source/SPFilterTableController.m @@ -0,0 +1,698 @@ +// +// SPFilterTableController.m +// sequel-pro +// +// Created by Max Lohrmann on 07.05.18. +// Copyright (c) 2018 Max Lohrmann. All rights reserved. +// Relocated from existing files. Previous copyright applies. +// +// 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 "SPFilterTableController.h" +#import "SPSplitView.h" +#import "SPCopyTable.h" +#import "SPTextView.h" +#import "RegexKitLite.h" +#import "SPTextAndLinkCell.h" + +static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOperator"; +static void *FilterTableKVOContext = &FilterTableKVOContext; + +@interface SPFilterTableController () <NSTableViewDataSource, NSTableViewDelegate, NSControlTextEditingDelegate> + +- (IBAction)filterTable:(id)sender; +- (IBAction)toggleLookAllFieldsMode:(id)sender; +- (IBAction)tableFilterClear:(id)sender; + +- (IBAction)toggleNegateClause:(id)sender; +- (IBAction)toggleDistinctSelect:(id)sender; +- (IBAction)setDefaultOperator:(id)sender; + +- (IBAction)closeSheet:(id)sender; +- (IBAction)showDefaultOperaterHelp:(id)sender; + +- (void)updateFilterTableClause:(id)currentValue; + ++ (NSString*)escapeFilterTableDefaultOperator:(NSString*)op; + +@end + +@implementation SPFilterTableController + +#pragma mark Public methods + +@synthesize target; +@synthesize action; + +- (instancetype)init { + if ((self = [super initWithWindowNibName:@"FilterTableWindow"])) { + target = nil; + action = NULL; + + prefs = [NSUserDefaults standardUserDefaults]; + [prefs addObserver:self + forKeyPath:SPDisplayTableViewVerticalGridlines + options:NSKeyValueObservingOptionNew + context:FilterTableKVOContext]; + + filterTableData = [[NSMutableDictionary alloc] initWithCapacity:1]; + + filterTableNegate = NO; + filterTableDistinct = NO; + filterTableIsSwapped = NO; + + lastEditedFilterTableValue = nil; + } + return self; +} + +- (void)dealloc +{ + //TODO this should be changed to the variant with …context: after 10.6 support is removed! + [prefs removeObserver:self forKeyPath:SPDisplayTableViewVerticalGridlines]; + + SPClear(filterTableData); + SPClear(lastEditedFilterTableValue); + SPClear(filterTableDefaultOperator); + [super dealloc]; +} + +- (void)showFilterTableWindow +{ + [[self window] makeKeyAndOrderFront:nil]; + [filterTableWhereClause setContinuousSpellCheckingEnabled:NO]; + [filterTableWhereClause setAutoindent:NO]; + [filterTableWhereClause setAutoindentIgnoresEnter:NO]; + [filterTableWhereClause setAutopair:[prefs boolForKey:SPCustomQueryAutoPairCharacters]]; + [filterTableWhereClause setAutohelp:NO]; + [filterTableWhereClause setAutouppercaseKeywords:[prefs boolForKey:SPCustomQueryAutoUppercaseKeywords]]; + [filterTableWhereClause setCompletionWasReinvokedAutomatically:NO]; + [filterTableWhereClause insertText:@""]; + [filterTableWhereClause didChangeText]; + + [[filterTableView window] makeFirstResponder:filterTableView]; +} + +- (void)setFilterTableData:(NSData*)arcData +{ + if(!arcData) return; + NSDictionary *filterData = [NSUnarchiver unarchiveObjectWithData:arcData]; + [filterTableData removeAllObjects]; + [filterTableData addEntriesFromDictionary:filterData]; + [[self window] makeKeyAndOrderFront:nil]; + [filterTableView reloadData]; +} + +- (NSData*) filterTableData +{ + if(![[self window] isVisible]) return nil; + + [filterTableView deselectAll:nil]; + + return [NSArchiver archivedDataWithRootObject:filterTableData]; +} + +- (NSString *)tableFilterString +{ + if([[[filterTableWhereClause textStorage] string] length]) { + if ([filterTableNegateCheckbox state] == NSOnState) { + return [NSString stringWithFormat:@"NOT (%@)", [[filterTableWhereClause textStorage] string]]; + } + else { + return [[filterTableWhereClause textStorage] string]; + } + } + else { + return nil; + } +} + +- (void)setColumns:(NSArray *)dataColumns +{ + [self window]; // make sure window is loaded + // Clear filter table + [filterTableView abortEditing]; + while ([[filterTableView tableColumns] count]) { + [NSArrayObjectAtIndex([filterTableView tableColumns], 0) setHeaderToolTip:nil]; // prevent crash #2414 + [filterTableView removeTableColumn:NSArrayObjectAtIndex([filterTableView tableColumns], 0)]; + } + // Clear filter table data + [filterTableData removeAllObjects]; + [filterTableWhereClause setString:@""]; + + // Clear error state + [self setFilterError:0 message:nil sqlstate:nil]; + + if(dataColumns) { + // Add the new columns to the filterTable + for (NSDictionary *columnDefinition in dataColumns ) { + // Set up column for filterTable + NSTableColumn *filterCol = [[NSTableColumn alloc] initWithIdentifier:[columnDefinition objectForKey:@"datacolumnindex"]]; + [[filterCol headerCell] setStringValue:[columnDefinition objectForKey:@"name"]]; + [filterCol setEditable:YES]; + SPTextAndLinkCell *filterDataCell = [[[SPTextAndLinkCell alloc] initTextCell:@""] autorelease]; + [filterDataCell setEditable:YES]; + [filterDataCell setLineBreakMode:NSLineBreakByTruncatingTail]; // add ellipsis for long values (default is to simply hide words) + [filterCol setDataCell:filterDataCell]; + [filterTableView addTableColumn:filterCol]; + [filterCol release]; + + [filterTableData setObject:[NSMutableDictionary dictionaryWithObjectsAndKeys: + [columnDefinition objectForKey:@"name"], @"name", + [columnDefinition objectForKey:@"typegrouping"], @"typegrouping", + [NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil], SPTableContentFilterKey, + nil] forKey:[columnDefinition objectForKey:@"datacolumnindex"]]; + } + } + + [filterTableView reloadData]; +} + +- (BOOL)isDistinct +{ + return filterTableDistinct; +} + +- (void)setFilterError:(NSUInteger)errorID message:(NSString *)message sqlstate:(NSString *)sqlstate +{ + if(errorID) { + [[self window] setTitle:[NSString stringWithFormat:@"%@ – %@", NSLocalizedString(@"Filter", @"filter label"), NSLocalizedString(@"WHERE clause not valid", @"WHERE clause not valid")]]; + } + else { + [[self window] setTitle:NSLocalizedString(@"Filter", @"filter label")]; + } +} + +#pragma mark - Internal methods + +- (void)windowDidLoad +{ + [filterTableView setGridStyleMask:([prefs boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone]; + + // Modify the filter table split view sizes + [filterTableSplitView setMinSize:135 ofSubviewAtIndex:1]; + + // Init Filter Table GUI + [filterTableDistinctCheckbox setState:(filterTableDistinct) ? NSOnState : NSOffState]; + [filterTableNegateCheckbox setState:(filterTableNegate) ? NSOnState : NSOffState]; + [filterTableLiveSearchCheckbox setState:NSOffState]; + + filterTableDefaultOperator = [[[self class] escapeFilterTableDefaultOperator:[prefs objectForKey:SPFilterTableDefaultOperator]] retain]; +} + +- (IBAction)filterTable:(id)sender +{ + if(target && action) [target performSelector:action withObject:self]; +} + +/** + * Generate WHERE clause to look for last typed pattern in all fields + */ +- (IBAction)toggleLookAllFieldsMode:(id)sender +{ + [self updateFilterTableClause:sender]; + + // If live search is set perform filtering + if ([filterTableLiveSearchCheckbox state] == NSOnState) { + [self filterTable:filterTableFilterButton]; + } +} + +/** + * Clear the filter table + */ +- (IBAction)tableFilterClear:(id)sender +{ + [filterTableView abortEditing]; + + if(filterTableData && [filterTableData count]) { + + // Clear filter data + for(NSNumber *col in [filterTableData allKeys]) + { + [[filterTableData objectForKey:col] setObject:[NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil] forKey:SPTableContentFilterKey]; + } + + [filterTableView reloadData]; + [filterTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; + [filterTableWhereClause setString:@""]; + + // Reload table + [self filterTable:nil]; + } +} + +/** + * Set filter table's Negate + */ +- (IBAction)toggleNegateClause:(id)sender +{ + filterTableNegate = !filterTableNegate; + + if (filterTableNegate) { + [filterTableQueryTitle setStringValue:NSLocalizedString(@"WHERE NOT query", @"Title of filter preview area when the query WHERE is negated")]; + } + 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) { + [self filterTable:filterTableFilterButton]; + } +} + +/** + * Set filter table's Distinct + */ +- (IBAction)toggleDistinctSelect:(id)sender +{ + filterTableDistinct = !filterTableDistinct; + + [filterTableDistinctCheckbox setState:(filterTableDistinct) ? NSOnState : NSOffState]; + + // If live search is set perform filtering + if ([filterTableLiveSearchCheckbox state] == NSOnState) { + [self filterTable:filterTableFilterButton]; + } +} + +/** + * Set filter table's default operator + */ +- (IBAction)setDefaultOperator:(id)sender +{ + [[self window] makeFirstResponder:filterTableView]; + + // Load history + if([prefs objectForKey:SPFilterTableDefaultOperatorLastItems]) { + NSMutableArray *lastItems = [NSMutableArray array]; + + [lastItems addObject:@"LIKE '%@%'"]; + + for(NSString* item in [prefs objectForKey:SPFilterTableDefaultOperatorLastItems]) + { + [lastItems addObject:item]; + } + + [filterTableSetDefaultOperatorValue removeAllItems]; + [filterTableSetDefaultOperatorValue addItemsWithObjectValues:lastItems]; + } + + [filterTableSetDefaultOperatorValue setStringValue:[prefs objectForKey:SPFilterTableDefaultOperator]]; + + [NSApp beginSheet:filterTableSetDefaultOperatorSheet + modalForWindow:[self window] + modalDelegate:self + didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) + contextInfo:SPTableFilterSetDefaultOperator]; +} + +/** + * Close an open sheet. + */ +- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo +{ + [sheet orderOut:self]; + + if([contextInfo isEqualToString:SPTableFilterSetDefaultOperator]) { + if(returnCode) { + if(filterTableDefaultOperator) [filterTableDefaultOperator release]; + NSString *newOperator = [filterTableSetDefaultOperatorValue stringValue]; + filterTableDefaultOperator = [[[self class] escapeFilterTableDefaultOperator:newOperator] retain]; + [prefs setObject:newOperator forKey:SPFilterTableDefaultOperator]; + + if(![newOperator isMatchedByRegex:@"(?i)like\\s+['\"]%@%['\"]\\s*"]) { + if(![prefs objectForKey:SPFilterTableDefaultOperatorLastItems]) + [prefs setObject:[NSMutableArray array] forKey:SPFilterTableDefaultOperatorLastItems]; + + NSMutableArray *lastItems = [NSMutableArray array]; + [lastItems setArray:[prefs objectForKey:SPFilterTableDefaultOperatorLastItems]]; + + if([lastItems containsObject:newOperator]) + [lastItems removeObject:newOperator]; + if([lastItems count] > 0) + [lastItems insertObject:newOperator atIndex:0]; + else + [lastItems addObject:newOperator]; + // Remember only the last 15 items + if([lastItems count] > 15) + while([lastItems count] > 15) + [filterTableSetDefaultOperatorValue removeItemAtIndex:[lastItems count]-1]; + + [prefs setObject:lastItems forKey:SPFilterTableDefaultOperatorLastItems]; + } + [self updateFilterTableClause:nil]; + } + } +} + +/** + * Closes the current sheet and stops the modal session + */ +- (IBAction)closeSheet:(id)sender +{ + [NSApp endSheet:[sender window] returnCode:[sender tag]]; + [[sender window] orderOut:self]; +} + +/** + * Opens the content filter help page in the default browser. + */ +- (IBAction)showDefaultOperaterHelp:(id)sender +{ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:SPLOCALIZEDURL_CONTENTFILTERHELP]]; +} + +/** + * 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 +{ + NSMutableString *clause = [NSMutableString string]; + NSInteger numberOfRows = [self numberOfRowsInTableView:filterTableView]; + NSInteger numberOfCols = [[filterTableView tableColumns] count]; + 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++) + { + NSInteger numberOfValues = 0; + + for (NSInteger anIndex = 0; anIndex < numberOfCols; anIndex++) + { + NSString *filterCell = nil; + NSDictionary *filterCellData = [NSDictionary dictionaryWithDictionary:[filterTableData objectForKey:[NSString stringWithFormat:@"%ld", (long)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]; + } +} + +#pragma mark - TableView datasource methods + +- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex +{ + if (filterTableIsSwapped) { + [[[filterTableData objectForKey:@(rowIndex)] objectForKey:SPTableContentFilterKey] replaceObjectAtIndex:([[tableColumn identifier] integerValue] - 1) withObject:(NSString *)object]; + } + else { + [[[filterTableData objectForKey:[tableColumn identifier]] objectForKey:SPTableContentFilterKey] replaceObjectAtIndex:rowIndex withObject:(NSString *)object]; + } + + [self updateFilterTableClause:nil]; +} + +- (NSInteger)numberOfRowsInTableView:(SPCopyTable *)tableView +{ + return filterTableIsSwapped ? [filterTableData count] : [[[filterTableData objectForKey:@"0"] objectForKey:SPTableContentFilterKey] count]; +} + +- (id)tableView:(SPCopyTable *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex +{ + NSUInteger columnIndex = [[tableColumn identifier] integerValue]; + + if (filterTableIsSwapped) { + // First column shows the field names + if (columnIndex == 0) { + return [[[NSTableHeaderCell alloc] initTextCell:[[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:@"name"]] autorelease]; + } + + return NSArrayObjectAtIndex([[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:SPTableContentFilterKey], columnIndex - 1); + } + + return NSArrayObjectAtIndex([[filterTableData objectForKey:[tableColumn identifier]] objectForKey:SPTableContentFilterKey], rowIndex); +} + +#pragma mark - TableView delegate methods + +- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)rowIndex +{ + return YES; +} + +- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex +{ + //if ([tableDocumentInstance isWorking]) return NO; + + return (filterTableIsSwapped && [[tableColumn identifier] integerValue] == 0) ? NO : YES; +} + +- (void)tableView:(SPCopyTable *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex +{ + if (filterTableIsSwapped && [[tableColumn identifier] integerValue] == 0) { + [cell setDrawsBackground:YES]; + [cell setBackgroundColor:[NSColor lightGrayColor]]; + } else { + [cell setDrawsBackground:NO]; + } +} + +- (NSString *)tableView:(NSTableView *)tableView toolTipForCell:(id)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation +{ + return nil; +} + +#pragma mark - Control delegate methods + +- (void)controlTextDidChange:(NSNotification *)notification +{ + if ([notification object] == filterTableView) { + + NSString *string = [[[[notification userInfo] objectForKey:@"NSFieldEditor"] textStorage] string]; + + if (string && [string length]) { + if (lastEditedFilterTableValue) [lastEditedFilterTableValue release]; + + lastEditedFilterTableValue = [[NSString stringWithString:string] retain]; + } + + [self updateFilterTableClause:string]; + } +} + +- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)editor +{ + return YES; +} + +- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)aFieldEditor +{ + return YES; +} + +- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command +{ + // Check firstly if SPCopyTable can handle command + if ([control control:control textView:textView doCommandBySelector:command]) return YES; + + // Trap the escape key + if ([[control window] methodForSelector:command] == [[control window] methodForSelector:@selector(cancelOperation:)]) { + // Abort editing + [control abortEditing]; + + return YES; + } + + return NO; +} + +#pragma mark - KVO + +/** + * This method is called as part of Key Value Observing which is used to watch for prefernce changes which effect the interface. + */ +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + // a parent class (or cocoa) can also use KVO, so we need to watch out to only catch those KVO messages we requested + if(context == FilterTableKVOContext) { + // Display table veiew vertical gridlines preference changed + if ([keyPath isEqualToString:SPDisplayTableViewVerticalGridlines]) { + [filterTableView setGridStyleMask:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone]; + } + } + else { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + +#pragma mark - + +/** + * Escape passed operator for usage as filterTableDefaultOperator. + */ ++ (NSString*)escapeFilterTableDefaultOperator:(NSString *)op +{ + if (!op) return @""; + + NSMutableString *newOp = [[[NSMutableString alloc] initWithCapacity:[op length]] autorelease]; + + [newOp setString:op]; + [newOp replaceOccurrencesOfRegex:@"%" withString:@"%%"]; + [newOp replaceOccurrencesOfRegex:@"(?<!`)@(?!=`)" withString:@"%@"]; + + return newOp; +} + +@end + diff --git a/Source/SPHistoryController.m b/Source/SPHistoryController.m index c39421a9..8a0554fe 100644 --- a/Source/SPHistoryController.m +++ b/Source/SPHistoryController.m @@ -250,7 +250,7 @@ NSDictionary *contentSelectedRows = [[tableContentInstance onMainThread] selectionDetailsAllowingIndexSelection:YES]; NSRect contentViewport = [[tableContentInstance onMainThread] viewport]; NSDictionary *contentFilter = [[tableContentInstance onMainThread] filterSettings]; - NSData *filterTableData = [tableContentInstance filterTableData]; + NSData *filterTableData = [[tableContentInstance onMainThread] filterTableData]; if (!theDatabase) return; // If a table is selected, save state information diff --git a/Source/SPTableContent.h b/Source/SPTableContent.h index d47dd467..c8f7100a 100644 --- a/Source/SPTableContent.h +++ b/Source/SPTableContent.h @@ -43,10 +43,8 @@ @class SPDatabaseDocument; @class SPTablesList; @class SPTableStructure; -#ifndef SP_CODA -@class SPSplitView; -#endif @class SPTableContentFilterController; +@class SPFilterTableController; typedef NS_ENUM(NSInteger, SPTableContentFilterSource) { SPTableContentFilterSourceNone = -1, @@ -102,21 +100,8 @@ typedef NS_ENUM(NSInteger, SPTableContentFilterSource) { #ifndef SP_CODA IBOutlet NSStepper *paginationPageStepper; - IBOutlet SPCopyTable *filterTableView; - IBOutlet NSPanel *filterTableWindow; - IBOutlet SPSplitView *filterTableSplitView; - IBOutlet NSTextField *filterTableQueryTitle; - IBOutlet NSButton *filterTableFilterButton; - IBOutlet NSButton *filterTableClearButton; - IBOutlet SPTextView *filterTableWhereClause; - IBOutlet NSButton *filterTableNegateCheckbox; - IBOutlet NSButton *filterTableDistinctCheckbox; - IBOutlet NSButton *filterTableLiveSearchCheckbox; - IBOutlet NSButton *filterTableSearchAllFields; - IBOutlet NSPanel *filterTableSetDefaultOperatorSheet; - IBOutlet NSComboBox* filterTableSetDefaultOperatorValue; - IBOutlet SPTableContentFilterController *filterControllerInstance; + IBOutlet SPFilterTableController *filterTableController; #endif SPMySQLConnection *mySQLConnection; @@ -140,12 +125,6 @@ typedef NS_ENUM(NSInteger, SPTableContentFilterSource) { NSUInteger contentPage; #ifndef SP_CODA - NSMutableDictionary *filterTableData; - BOOL filterTableNegate; - BOOL filterTableDistinct; - BOOL filterTableIsSwapped; - NSString *filterTableDefaultOperator; - NSString *lastEditedFilterTableValue; SPTableContentFilterSource activeFilter; NSString *schemeFilter; #endif @@ -243,14 +222,7 @@ typedef NS_ENUM(NSInteger, SPTableContentFilterSource) { - (void)removeRowSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo; // Filter Table -- (IBAction)tableFilterClear:(id)sender; - (IBAction)showFilterTable:(id)sender; -- (IBAction)toggleNegateClause:(id)sender; -- (IBAction)toggleDistinctSelect:(id)sender; -- (IBAction)setDefaultOperator:(id)sender; -- (IBAction)toggleLookAllFieldsMode:(id)sender; -- (IBAction)closeSheet:(id)sender; -- (IBAction)showDefaultOperaterHelp:(id)sender; // Data accessors - (NSArray *)currentResult; @@ -309,7 +281,5 @@ typedef NS_ENUM(NSInteger, SPTableContentFilterSource) { #pragma mark - SPTableContentFilter - (void)makeContentFilterHaveFocus; -- (void)updateFilterTableClause:(id)currentValue; -- (NSString*)escapeFilterTableDefaultOperator:(NSString*)op; @end diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index 934745f6..8d1a19a3 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -40,9 +40,6 @@ #import "SPTableData.h" #import "SPQueryController.h" #import "SPTextAndLinkCell.h" -#ifndef SP_CODA -#import "SPSplitView.h" -#endif #import "SPFieldEditorController.h" #import "SPTooltip.h" #import "RegexKitLite.h" @@ -60,6 +57,7 @@ #import "SPTableFilterParser.h" #import "SPFunctions.h" #import "SPTableContentFilterController.h" +#import "SPFilterTableController.h" #import <pthread.h> #import <SPMySQL/SPMySQL.h> @@ -71,10 +69,6 @@ */ static void *TableContentKVOContext = &TableContentKVOContext; -#ifndef SP_CODA -static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOperator"; -#endif - @interface SPTableContent () - (BOOL)cancelRowEditing; @@ -127,18 +121,11 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper tableValues = [[SPDataStorage alloc] init]; dataColumns = [[NSMutableArray alloc] init]; oldRow = [[NSMutableArray alloc] init]; -#ifndef SP_CODA - filterTableData = [[NSMutableDictionary alloc] initWithCapacity:1]; -#endif tableRowsCount = 0; previousTableRowsCount = 0; #ifndef SP_CODA - filterTableNegate = NO; - filterTableDistinct = NO; - filterTableIsSwapped = NO; - lastEditedFilterTableValue = nil; activeFilter = SPTableContentFilterSourceNone; schemeFilter = nil; paginationPopover = nil; @@ -198,7 +185,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper // Set the table content view's vertical gridlines if required [tableContentView setGridStyleMask:([prefs boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone]; - [filterTableView setGridStyleMask:([prefs boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone]; // Set the double-click action in blank areas of the table to create new rows [tableContentView setEmptyDoubleClickAction:@selector(addRow:)]; @@ -238,25 +224,10 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper [paginationView setFrame:paginationViewFrame]; [contentViewPane addSubview:paginationView]; } - - // Modify the filter table split view sizes - [filterTableSplitView setMinSize:135 ofSubviewAtIndex:1]; #endif [tableContentView setFieldEditorSelectedRange:NSMakeRange(0,0)]; -#ifndef SP_CODA - // Init Filter Table GUI - [filterTableDistinctCheckbox setState:(filterTableDistinct) ? NSOnState : NSOffState]; - [filterTableNegateCheckbox setState:(filterTableNegate) ? NSOnState : NSOffState]; - [filterTableLiveSearchCheckbox setState:NSOffState]; -#endif -#ifndef SP_CODA /* patch */ - filterTableDefaultOperator = [[self escapeFilterTableDefaultOperator:[prefs objectForKey:SPFilterTableDefaultOperator]] retain]; -#else -// filterTableDefaultOperator = [[self escapeFilterTableDefaultOperator:nil] retain]; -#endif - [prefs addObserver:self forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:TableContentKVOContext]; [prefs addObserver:self forKeyPath:SPGlobalResultTableFont options:NSKeyValueObservingOptionNew context:TableContentKVOContext]; [prefs addObserver:self forKeyPath:SPDisplayBinaryDataAsHex options:NSKeyValueObservingOptionNew context:TableContentKVOContext]; @@ -273,6 +244,9 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper object:contentAreaContainer]; [filterControllerInstance setTarget:self]; [filterControllerInstance setAction:@selector(filterTable:)]; + + [filterTableController setTarget:self]; + [filterTableController setAction:@selector(filterTable:)]; // Add observers for document task activity [[NSNotificationCenter defaultCenter] addObserver:self @@ -406,14 +380,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper [self clearDetailsToRestore]; #ifndef SP_CODA - // Clear filter table - while ([[filterTableView tableColumns] count]) { - [NSArrayObjectAtIndex([filterTableView tableColumns], 0) setHeaderToolTip:nil]; // prevent crash #2414 - [filterTableView removeTableColumn:NSArrayObjectAtIndex([filterTableView tableColumns], 0)]; - } - // Clear filter table data - [filterTableData removeAllObjects]; - [filterTableWhereClause setString:@""]; + [filterTableController setColumns:nil]; activeFilter = SPTableContentFilterSourceNone; #endif } @@ -432,10 +399,8 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper #endif NSArray *columnNames; NSMutableDictionary *preservedColumnWidths = nil; - NSTableColumn *theCol; -#ifndef SP_CODA - NSTableColumn *filterCol; -#endif + NSTableColumn *theCol; + BOOL enableInteraction = #ifndef SP_CODA /* checking toolbar state */ ![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:SPMainToolbarTableContent] || @@ -511,14 +476,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper } #ifndef SP_CODA // Remove existing columns from the filter table - [filterTableView abortEditing]; - while ([[filterTableView tableColumns] count]) { - [NSArrayObjectAtIndex([filterTableView tableColumns], 0) setHeaderToolTip:nil]; // prevent crash #2414 - [filterTableView removeTableColumn:NSArrayObjectAtIndex([filterTableView tableColumns], 0)]; - } - // Clear filter table data - [filterTableData removeAllObjects]; - [filterTableWhereClause setString:@""]; + [filterTableController setColumns:nil]; // TODO code smell //...but keep it, if the rule filter is the active one if(activeFilter != SPTableContentFilterSourceRuleFilter) activeFilter = SPTableContentFilterSourceNone; @@ -557,7 +515,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper #endif [tableContentView setRowHeight:2.0f+NSSizeToCGSize([@"{ǞṶḹÜ∑zgyf" sizeWithAttributes:@{NSFontAttributeName : tableFont}]).height]; - // Add the new columns to the table and filterTable + // Add the new columns to the table for (NSDictionary *columnDefinition in dataColumns ) { // Set up the column @@ -578,24 +536,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper [theCol setEditable:YES]; -#ifndef SP_CODA - // Set up column for filterTable - filterCol = [[NSTableColumn alloc] initWithIdentifier:[columnDefinition objectForKey:@"datacolumnindex"]]; - [[filterCol headerCell] setStringValue:[columnDefinition objectForKey:@"name"]]; - [filterCol setEditable:YES]; - SPTextAndLinkCell *filterDataCell = [[[SPTextAndLinkCell alloc] initTextCell:@""] autorelease]; - [filterDataCell setEditable:YES]; - [filterCol setDataCell:filterDataCell]; - [filterTableView addTableColumn:filterCol]; - [filterCol release]; - - [filterTableData setObject:[NSMutableDictionary dictionaryWithObjectsAndKeys: - [columnDefinition objectForKey:@"name"], @"name", - [columnDefinition objectForKey:@"typegrouping"], @"typegrouping", - [NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil], SPTableContentFilterKey, - nil] forKey:[columnDefinition objectForKey:@"datacolumnindex"]]; -#endif - // Set up the data cell depending on the column type id dataCell; if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"enum"]) { @@ -665,12 +605,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper [theCol release]; } -#ifndef SP_CODA - [filterTableView setDelegate:self]; - [filterTableView setDataSource:self]; - [filterTableView reloadData]; -#endif - // If the table has been reloaded and the previously selected sort column is still present, reselect it. if (sortColumnNumberToRestore != NSNotFound) { theCol = [tableContentView tableColumnWithIdentifier:[NSString stringWithFormat:@"%lld", (long long)sortColumnNumberToRestore]]; @@ -700,6 +634,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper currentFirstResponder = [currentFirstResponder delegate]; } + [filterTableController setColumns:dataColumns]; // Enable and initialize filter fields (with tags for position of menu item and field position) [filterControllerInstance updateFiltersFrom:self]; // Restore preserved filter settings if appropriate and valid @@ -732,10 +667,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper if (!previousTableRowsCount) { [self clearTableValues]; } -#ifndef SP_CODA - [filterTableView reloadData]; -#endif - } - (NSString *)selectedTable @@ -785,17 +716,17 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper // Notify any listeners that a query has started [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance]; + // Add a filter string if appropriate + filterString = [[self onMainThread] tableFilterString]; + // Start construction of the query string queryString = [NSMutableString stringWithFormat:@"SELECT %@%@ FROM %@", #ifndef SP_CODA - (activeFilter == SPTableContentFilterSourceTableFilter && [self tableFilterString] && filterTableDistinct) ? @"DISTINCT " : + (activeFilter == SPTableContentFilterSourceTableFilter && filterString && [filterTableController isDistinct]) ? @"DISTINCT " : #endif @"", [self fieldListForQuery], [selectedTable backtickQuotedString]]; - // Add a filter string if appropriate - filterString = [[self onMainThread] tableFilterString]; - if ([filterString length]) { [queryString appendFormat:@" WHERE %@", filterString]; isFiltered = YES; @@ -983,7 +914,9 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper #ifndef SP_CODA // Filter task came from filter table else if(activeFilter == SPTableContentFilterSourceTableFilter) { - [filterTableWindow setTitle:[NSString stringWithFormat:@"%@ – %@", NSLocalizedString(@"Filter", @"filter label"), NSLocalizedString(@"WHERE clause not valid", @"WHERE clause not valid")]]; + [[filterTableController onMainThread] setFilterError:[mySQLConnection lastErrorID] + message:[mySQLConnection lastErrorMessage] + sqlstate:[mySQLConnection lastSqlstate]]; } } #endif @@ -992,7 +925,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper #ifndef SP_CODA // Trigger a full reload if required if (fullTableReloadRequired) [self reloadTable:self]; - [[filterTableWindow onMainThread] setTitle:NSLocalizedString(@"Filter", @"filter label")]; + [[filterTableController onMainThread] setFilterError:0 message:nil sqlstate:nil]; #endif } } @@ -1069,18 +1002,8 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper } // Call did come from filter table and is filter table window still open? - if(activeFilter == SPTableContentFilterSourceTableFilter && [filterTableWindow isVisible]) { - if([[[filterTableWhereClause textStorage] string] length]) { - if ([filterTableNegateCheckbox state] == NSOnState) { - return [NSString stringWithFormat:@"NOT (%@)", [[filterTableWhereClause textStorage] string]]; - } - else { - return [[filterTableWhereClause textStorage] string]; - } - } - else { - return nil; - } + if(activeFilter == SPTableContentFilterSourceTableFilter && [[filterTableController window] isVisible]) { + return [filterTableController tableFilterString]; } #endif if(activeFilter == SPTableContentFilterSourceRuleFilter && showFilterRuleEditor) { @@ -1317,7 +1240,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper #ifndef SP_CODA // If the filter table is being used - the advanced filter - switch type - if(sender == filterTableFilterButton) { + if(sender == filterTableController) { activeFilter = SPTableContentFilterSourceTableFilter; } @@ -3054,46 +2977,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper } -/** - * Close an open sheet. - */ -- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo -{ -#ifndef SP_CODA - [sheet orderOut:self]; - - if([contextInfo isEqualToString:SPTableFilterSetDefaultOperator]) { - if(returnCode) { - if(filterTableDefaultOperator) [filterTableDefaultOperator release]; - NSString *newOperator = [filterTableSetDefaultOperatorValue stringValue]; - filterTableDefaultOperator = [[self escapeFilterTableDefaultOperator:newOperator] retain]; - [prefs setObject:newOperator forKey:SPFilterTableDefaultOperator]; - - if(![newOperator isMatchedByRegex:@"(?i)like\\s+['\"]%@%['\"]\\s*"]) { - if(![prefs objectForKey:SPFilterTableDefaultOperatorLastItems]) - [prefs setObject:[NSMutableArray array] forKey:SPFilterTableDefaultOperatorLastItems]; - - NSMutableArray *lastItems = [NSMutableArray array]; - [lastItems setArray:[prefs objectForKey:SPFilterTableDefaultOperatorLastItems]]; - - if([lastItems containsObject:newOperator]) - [lastItems removeObject:newOperator]; - if([lastItems count] > 0) - [lastItems insertObject:newOperator atIndex:0]; - else - [lastItems addObject:newOperator]; - // Remember only the last 15 items - if([lastItems count] > 15) - while([lastItems count] > 15) - [filterTableSetDefaultOperatorValue removeItemAtIndex:[lastItems count]-1]; - - [prefs setObject:lastItems forKey:SPFilterTableDefaultOperatorLastItems]; - } - [self updateFilterTableClause:nil]; - } - } -#endif -} + /** * Show Error sheet (can be called from inside of a endSheet selector) @@ -3290,160 +3174,11 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper #pragma mark Filter Table /** - * Clear the filter table - */ -- (IBAction)tableFilterClear:(id)sender -{ -#ifndef SP_CODA - - [filterTableView abortEditing]; - - if(filterTableData && [filterTableData count]) { - - // Clear filter data - for(NSNumber *col in [filterTableData allKeys]) - { - [[filterTableData objectForKey:col] setObject:[NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil] forKey:SPTableContentFilterKey]; - } - - [filterTableView reloadData]; - [filterTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; - [filterTableWhereClause setString:@""]; - - // Reload table - [self filterTable:nil]; - } -#endif -} - -/** * Show filter table */ - (IBAction)showFilterTable:(id)sender { -#ifndef SP_CODA - [filterTableWindow makeKeyAndOrderFront:nil]; - [filterTableWhereClause setContinuousSpellCheckingEnabled:NO]; - [filterTableWhereClause setAutoindent:NO]; - [filterTableWhereClause setAutoindentIgnoresEnter:NO]; - [filterTableWhereClause setAutopair:[prefs boolForKey:SPCustomQueryAutoPairCharacters]]; - [filterTableWhereClause setAutohelp:NO]; - [filterTableWhereClause setAutouppercaseKeywords:[prefs boolForKey:SPCustomQueryAutoUppercaseKeywords]]; - [filterTableWhereClause setCompletionWasReinvokedAutomatically:NO]; - [filterTableWhereClause insertText:@""]; - [filterTableWhereClause didChangeText]; - - [[filterTableView window] makeFirstResponder:filterTableView]; -#endif -} - -/** - * Set filter table's Negate - */ -- (IBAction)toggleNegateClause:(id)sender -{ -#ifndef SP_CODA - filterTableNegate = !filterTableNegate; - - if (filterTableNegate) { - [filterTableQueryTitle setStringValue:NSLocalizedString(@"WHERE NOT query", @"Title of filter preview area when the query WHERE is negated")]; - } - 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) { - [self filterTable:filterTableFilterButton]; - } -#endif - -} - -/** - * Set filter table's Distinct - */ -- (IBAction)toggleDistinctSelect:(id)sender -{ -#ifndef SP_CODA - filterTableDistinct = !filterTableDistinct; - - [filterTableDistinctCheckbox setState:(filterTableDistinct) ? NSOnState : NSOffState]; - - // If live search is set perform filtering - if ([filterTableLiveSearchCheckbox state] == NSOnState) { - [self filterTable:filterTableFilterButton]; - } -#endif - -} - -/** - * Set filter table's default operator - */ -- (IBAction)setDefaultOperator:(id)sender -{ -#ifndef SP_CODA - - [filterTableWindow makeFirstResponder:filterTableView]; - - // Load history - if([prefs objectForKey:SPFilterTableDefaultOperatorLastItems]) { - NSMutableArray *lastItems = [NSMutableArray array]; - - [lastItems addObject:@"LIKE '%@%'"]; - - for(NSString* item in [prefs objectForKey:SPFilterTableDefaultOperatorLastItems]) - { - [lastItems addObject:item]; - } - - [filterTableSetDefaultOperatorValue removeAllItems]; - [filterTableSetDefaultOperatorValue addItemsWithObjectValues:lastItems]; - } - - [filterTableSetDefaultOperatorValue setStringValue:[prefs objectForKey:SPFilterTableDefaultOperator]]; - - [NSApp beginSheet:filterTableSetDefaultOperatorSheet - modalForWindow:filterTableWindow - modalDelegate:self - didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) - contextInfo:SPTableFilterSetDefaultOperator]; -#endif - -} - -/** - * Generate WHERE clause to look for last typed pattern in all fields - */ -- (IBAction)toggleLookAllFieldsMode:(id)sender -{ - [self updateFilterTableClause:sender]; - -#ifndef SP_CODA - // If live search is set perform filtering - if ([filterTableLiveSearchCheckbox state] == NSOnState) { - [self filterTable:filterTableFilterButton]; - } -#endif - -} - -/** - * Closes the current sheet and stops the modal session - */ -- (IBAction)closeSheet:(id)sender -{ - [NSApp endSheet:[sender window] returnCode:[sender tag]]; - [[sender window] orderOut:self]; -} - -/** - * Opens the content filter help page in the default browser. - */ -- (IBAction)showDefaultOperaterHelp:(id)sender -{ - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:SPLOCALIZEDURL_CONTENTFILTERHELP]]; + [filterTableController showFilterTableWindow]; } #pragma mark - @@ -3664,29 +3399,14 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper [self setFiltersToRestore:nil]; } -- (void) setFilterTableData:(NSData*)arcData +- (NSData*) filterTableData { -#ifndef SP_CODA - if(!arcData) return; - NSDictionary *filterData = [NSUnarchiver unarchiveObjectWithData:arcData]; - [filterTableData removeAllObjects]; - [filterTableData addEntriesFromDictionary:filterData]; - [filterTableWindow makeKeyAndOrderFront:nil]; - // [filterTableView reloadData]; -#endif + return [filterTableController filterTableData]; } -- (NSData*) filterTableData +- (void)setFilterTableData:(NSData *)arcData; { -#ifndef SP_CODA - if(![filterTableWindow isVisible]) return nil; - - [filterTableView deselectAll:nil]; - - return [NSArchiver archivedDataWithRootObject:filterTableData]; -#else - return nil; -#endif + [filterTableController setFilterTableData:arcData]; } #pragma mark - @@ -3916,7 +3636,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper // Display table veiew vertical gridlines preference changed if ([keyPath isEqualToString:SPDisplayTableViewVerticalGridlines]) { [tableContentView setGridStyleMask:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone]; - [filterTableView setGridStyleMask:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone]; } // Table font preference changed else if ([keyPath isEqualToString:SPGlobalResultTableFont]) { @@ -3982,11 +3701,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper - (NSInteger)numberOfRowsInTableView:(SPCopyTable *)tableView { -#ifndef SP_CODA - if (tableView == filterTableView) { - return filterTableIsSwapped ? [filterTableData count] : [[[filterTableData objectForKey:@"0"] objectForKey:SPTableContentFilterKey] count]; - } -#endif if (tableView == tableContentView) { return tableRowsCount; } @@ -3997,20 +3711,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper - (id)tableView:(SPCopyTable *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex { NSUInteger columnIndex = [[tableColumn identifier] integerValue]; -#ifndef SP_CODA - if (tableView == filterTableView) { - if (filterTableIsSwapped) { - // First column shows the field names - if (columnIndex == 0) { - return [[[NSTableHeaderCell alloc] initTextCell:[[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:@"name"]] autorelease]; - } - - return NSArrayObjectAtIndex([[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:SPTableContentFilterKey], columnIndex - 1); - } - - return NSArrayObjectAtIndex([[filterTableData objectForKey:[tableColumn identifier]] objectForKey:SPTableContentFilterKey], rowIndex); - } -#endif if (tableView == tableContentView) { id value = nil; @@ -4081,20 +3781,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper - (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex { -#ifndef SP_CODA - if(tableView == filterTableView) { - if (filterTableIsSwapped) { - [[[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:SPTableContentFilterKey] replaceObjectAtIndex:([[tableColumn identifier] integerValue] - 1) withObject:(NSString *)object]; - } - else { - [[[filterTableData objectForKey:[tableColumn identifier]] objectForKey:SPTableContentFilterKey] replaceObjectAtIndex:rowIndex withObject:(NSString *)object]; - } - - [self updateFilterTableClause:nil]; - - return; - } -#endif if (tableView == tableContentView) { NSInteger columnIndex = [[tableColumn identifier] integerValue]; // If the current cell should have been edited in a sheet, do nothing - field closing will have already @@ -4186,187 +3872,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper #ifndef SP_CODA /** - * Escape passed operator for usage as filterTableDefaultOperator. - */ -- (NSString*)escapeFilterTableDefaultOperator:(NSString *)op -{ - if (!op) return @""; - - NSMutableString *newOp = [[[NSMutableString alloc] initWithCapacity:[op length]] autorelease]; - - [newOp setString:op]; - [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 -{ - 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 = nil; - NSDictionary *filterCellData = [NSDictionary dictionaryWithDictionary:[filterTableData objectForKey:[NSString stringWithFormat:@"%ld", (long)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]; - } -} - -/** * Makes the content filter field have focus by making it the first responder. */ - (void)makeContentFilterHaveFocus @@ -4546,135 +4051,128 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper { if ([tableDocumentInstance isWorking]) return NO; -#ifndef SP_CODA - if (tableView == filterTableView) { - return (filterTableIsSwapped && [[tableColumn identifier] integerValue] == 0) ? NO : YES; - } - else -#endif - if (tableView == tableContentView) { - - // Nothing is editable while the field editor is running. - // This guards against a special case where accessibility services might - // check if a table field is editable while the sheet is running. - if (fieldEditor) return NO; + if (tableView == tableContentView) { - // Ensure that row is editable since it could contain "(not loaded)" columns together with - // issue that the table has no primary key - NSString *wherePart = [NSString stringWithString:[self argumentForRow:[tableContentView selectedRow]]]; + // Nothing is editable while the field editor is running. + // This guards against a special case where accessibility services might + // check if a table field is editable while the sheet is running. + if (fieldEditor) return NO; - if (![wherePart length]) return NO; + // Ensure that row is editable since it could contain "(not loaded)" columns together with + // issue that the table has no primary key + NSString *wherePart = [NSString stringWithString:[self argumentForRow:[tableContentView selectedRow]]]; - // If the selected cell hasn't been loaded, load it. - if ([[tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]] isSPNotLoaded]) { + if (![wherePart length]) return NO; - // Only get the data for the selected column, not all of them - NSString *query = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@", [[[tableColumn headerCell] stringValue] backtickQuotedString], [selectedTable backtickQuotedString], wherePart]; + // If the selected cell hasn't been loaded, load it. + if ([[tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]] isSPNotLoaded]) { - SPMySQLResult *tempResult = [mySQLConnection queryString:query]; + // Only get the data for the selected column, not all of them + NSString *query = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@", [[[tableColumn headerCell] stringValue] backtickQuotedString], [selectedTable backtickQuotedString], wherePart]; - if (![tempResult numberOfRows]) { - SPOnewayAlertSheet( - NSLocalizedString(@"Error", @"error"), - [tableDocumentInstance parentWindow], - NSLocalizedString(@"Couldn't load the row. Reload the table to be sure that the row exists and use a primary key for your table.", @"message of panel when loading of row failed") - ); - return NO; - } + SPMySQLResult *tempResult = [mySQLConnection queryString:query]; - NSArray *tempRow = [tempResult getRowAsArray]; - - [tableValues replaceObjectInRow:rowIndex column:[[tableContentView tableColumns] indexOfObject:tableColumn] withObject:[tempRow objectAtIndex:0]]; - [tableContentView reloadData]; + if (![tempResult numberOfRows]) { + SPOnewayAlertSheet( + NSLocalizedString(@"Error", @"error"), + [tableDocumentInstance parentWindow], + NSLocalizedString(@"Couldn't load the row. Reload the table to be sure that the row exists and use a primary key for your table.", @"message of panel when loading of row failed") + ); + return NO; } - // Retrieve the column definition - NSDictionary *columnDefinition = [cqColumnDefinition objectAtIndex:[[tableColumn identifier] integerValue]]; + NSArray *tempRow = [tempResult getRowAsArray]; - // Open the editing sheet if required - if ([tableContentView shouldUseFieldEditorForRow:rowIndex column:[[tableColumn identifier] integerValue] checkWithLock:NULL]) { + [tableValues replaceObjectInRow:rowIndex column:[[tableContentView tableColumns] indexOfObject:tableColumn] withObject:[tempRow objectAtIndex:0]]; + [tableContentView reloadData]; + } - BOOL isBlob = [tableDataInstance columnIsBlobOrText:[[tableColumn headerCell] stringValue]]; + // Retrieve the column definition + NSDictionary *columnDefinition = [cqColumnDefinition objectAtIndex:[[tableColumn identifier] integerValue]]; - // A table is per definition editable - BOOL isFieldEditable = YES; + // Open the editing sheet if required + if ([tableContentView shouldUseFieldEditorForRow:rowIndex column:[[tableColumn identifier] integerValue] checkWithLock:NULL]) { - // Check for Views if field is editable - if ([tablesListInstance tableType] == SPTableTypeView) { - NSArray *editStatus = [self fieldEditStatusForRow:rowIndex andColumn:[[tableColumn identifier] integerValue]]; - isFieldEditable = [[editStatus objectAtIndex:0] integerValue] == 1; - } + BOOL isBlob = [tableDataInstance columnIsBlobOrText:[[tableColumn headerCell] stringValue]]; - NSUInteger fieldLength = 0; - NSString *fieldEncoding = nil; - BOOL allowNULL = YES; + // A table is per definition editable + BOOL isFieldEditable = YES; - NSString *fieldType = [columnDefinition objectForKey:@"type"]; + // Check for Views if field is editable + if ([tablesListInstance tableType] == SPTableTypeView) { + NSArray *editStatus = [self fieldEditStatusForRow:rowIndex andColumn:[[tableColumn identifier] integerValue]]; + isFieldEditable = [[editStatus objectAtIndex:0] integerValue] == 1; + } - if ([columnDefinition objectForKey:@"char_length"]) { - fieldLength = [[columnDefinition objectForKey:@"char_length"] integerValue]; - } + NSUInteger fieldLength = 0; + NSString *fieldEncoding = nil; + BOOL allowNULL = YES; - if ([columnDefinition objectForKey:@"null"]) { - allowNULL = (![[columnDefinition objectForKey:@"null"] integerValue]); - } + NSString *fieldType = [columnDefinition objectForKey:@"type"]; - if ([columnDefinition objectForKey:@"charset_name"] && ![[columnDefinition objectForKey:@"charset_name"] isEqualToString:@"binary"]) { - fieldEncoding = [columnDefinition objectForKey:@"charset_name"]; - } + if ([columnDefinition objectForKey:@"char_length"]) { + fieldLength = [[columnDefinition objectForKey:@"char_length"] integerValue]; + } - fieldEditor = [[SPFieldEditorController alloc] init]; + if ([columnDefinition objectForKey:@"null"]) { + allowNULL = (![[columnDefinition objectForKey:@"null"] integerValue]); + } - [fieldEditor setEditedFieldInfo:[NSDictionary dictionaryWithObjectsAndKeys: - [[tableColumn headerCell] stringValue], @"colName", - [self usedQuery], @"usedQuery", - @"content", @"tableSource", - nil]]; + if ([columnDefinition objectForKey:@"charset_name"] && ![[columnDefinition objectForKey:@"charset_name"] isEqualToString:@"binary"]) { + fieldEncoding = [columnDefinition objectForKey:@"charset_name"]; + } - [fieldEditor setTextMaxLength:fieldLength]; - [fieldEditor setFieldType:fieldType == nil ? @"" : fieldType]; - [fieldEditor setFieldEncoding:fieldEncoding == nil ? @"" : fieldEncoding]; - [fieldEditor setAllowNULL:allowNULL]; + fieldEditor = [[SPFieldEditorController alloc] init]; - id cellValue = [tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]]; + [fieldEditor setEditedFieldInfo:[NSDictionary dictionaryWithObjectsAndKeys: + [[tableColumn headerCell] stringValue], @"colName", + [self usedQuery], @"usedQuery", + @"content", @"tableSource", + nil]]; - if ([cellValue isNSNull]) { - cellValue = [NSString stringWithString:[prefs objectForKey:SPNullValue]]; - } + [fieldEditor setTextMaxLength:fieldLength]; + [fieldEditor setFieldType:fieldType == nil ? @"" : fieldType]; + [fieldEditor setFieldEncoding:fieldEncoding == nil ? @"" : fieldEncoding]; + [fieldEditor setAllowNULL:allowNULL]; - if ([self cellValueIsDisplayedAsHexForColumn:[[tableColumn identifier] integerValue]]) { - [fieldEditor setTextMaxLength:[[self tableView:tableContentView objectValueForTableColumn:tableColumn row:rowIndex] length]]; - isFieldEditable = NO; - } + id cellValue = [tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]]; - NSInteger editedColumn = 0; + if ([cellValue isNSNull]) { + cellValue = [NSString stringWithString:[prefs objectForKey:SPNullValue]]; + } - for (NSTableColumn* col in [tableContentView tableColumns]) - { - if ([[col identifier] isEqualToString:[tableColumn identifier]]) break; + if ([self cellValueIsDisplayedAsHexForColumn:[[tableColumn identifier] integerValue]]) { + [fieldEditor setTextMaxLength:[[self tableView:tableContentView objectValueForTableColumn:tableColumn row:rowIndex] length]]; + isFieldEditable = NO; + } - editedColumn++; - } + NSInteger editedColumn = 0; - [fieldEditor editWithObject:cellValue - fieldName:[[tableColumn headerCell] stringValue] - usingEncoding:[mySQLConnection stringEncoding] - isObjectBlob:isBlob - isEditable:isFieldEditable - withWindow:[tableDocumentInstance parentWindow] - sender:self - contextInfo:[NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInteger:rowIndex], @"rowIndex", - [NSNumber numberWithInteger:editedColumn], @"columnIndex", - [NSNumber numberWithBool:isFieldEditable], @"isFieldEditable", - nil]]; + for (NSTableColumn* col in [tableContentView tableColumns]) + { + if ([[col identifier] isEqualToString:[tableColumn identifier]]) break; - return NO; + editedColumn++; } - return YES; + [fieldEditor editWithObject:cellValue + fieldName:[[tableColumn headerCell] stringValue] + usingEncoding:[mySQLConnection stringEncoding] + isObjectBlob:isBlob + isEditable:isFieldEditable + withWindow:[tableDocumentInstance parentWindow] + sender:self + contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:rowIndex], @"rowIndex", + [NSNumber numberWithInteger:editedColumn], @"columnIndex", + [NSNumber numberWithBool:isFieldEditable], @"isFieldEditable", + nil]]; + + return NO; } + return YES; + } + return YES; } @@ -4714,13 +4212,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper */ - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)rowIndex { -#ifndef SP_CODA - if (tableView == filterTableView) { - return YES; - } - else -#endif - return tableView == tableContentView ? tableRowsSelectable : YES; + return tableView == tableContentView ? tableRowsSelectable : YES; } /** @@ -4775,76 +4267,62 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper */ - (void)tableView:(SPCopyTable *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex { -#ifndef SP_CODA - if (tableView == filterTableView) { - if (filterTableIsSwapped && [[tableColumn identifier] integerValue] == 0) { - [cell setDrawsBackground:YES]; - [cell setBackgroundColor:lightGrayColor]; - } - else { - [cell setDrawsBackground:NO]; - } + if (tableView == tableContentView) { - return; - } - else -#endif - if (tableView == tableContentView) { + if (![cell respondsToSelector:@selector(setTextColor:)]) return; - if (![cell respondsToSelector:@selector(setTextColor:)]) return; + BOOL cellIsNullOrUnloaded = NO; + BOOL cellIsLinkCell = [cell isMemberOfClass:[SPTextAndLinkCell class]]; - BOOL cellIsNullOrUnloaded = NO; - BOOL cellIsLinkCell = [cell isMemberOfClass:[SPTextAndLinkCell class]]; + NSUInteger columnIndex = [[tableColumn identifier] integerValue]; - NSUInteger columnIndex = [[tableColumn identifier] integerValue]; + // If user wants to edit 'cell' set text color to black and return to avoid + // writing in gray if value was NULL + if ([tableView editedColumn] != -1 + && [tableView editedRow] == rowIndex + && (NSUInteger)[[NSArrayObjectAtIndex([tableView tableColumns], [tableView editedColumn]) identifier] integerValue] == columnIndex) { + [cell setTextColor:blackColor]; + if (cellIsLinkCell) [cell setLinkActive:NO]; + return; + } - // If user wants to edit 'cell' set text color to black and return to avoid - // writing in gray if value was NULL - if ([tableView editedColumn] != -1 - && [tableView editedRow] == rowIndex - && (NSUInteger)[[NSArrayObjectAtIndex([tableView tableColumns], [tableView editedColumn]) identifier] integerValue] == columnIndex) { - [cell setTextColor:blackColor]; - if (cellIsLinkCell) [cell setLinkActive:NO]; - return; + // While the table is being loaded, additional validation is required - data + // locks must be used to avoid crashes, and indexes higher than the available + // rows or columns may be requested. Use gray to indicate loading in these cases. + if (isWorking) { + pthread_mutex_lock(&tableValuesLock); + + if (rowIndex < (NSInteger)tableRowsCount && columnIndex < [tableValues columnCount]) { + cellIsNullOrUnloaded = [tableValues cellIsNullOrUnloadedAtRow:rowIndex column:columnIndex]; } - // While the table is being loaded, additional validation is required - data - // locks must be used to avoid crashes, and indexes higher than the available - // rows or columns may be requested. Use gray to indicate loading in these cases. - if (isWorking) { - pthread_mutex_lock(&tableValuesLock); + pthread_mutex_unlock(&tableValuesLock); + } + else { + cellIsNullOrUnloaded = [tableValues cellIsNullOrUnloadedAtRow:rowIndex column:columnIndex]; + } - if (rowIndex < (NSInteger)tableRowsCount && columnIndex < [tableValues columnCount]) { - cellIsNullOrUnloaded = [tableValues cellIsNullOrUnloadedAtRow:rowIndex column:columnIndex]; - } + if (cellIsNullOrUnloaded) { + [cell setTextColor:rowIndex == [tableContentView selectedRow] ? whiteColor : lightGrayColor]; + } + else { + [cell setTextColor:blackColor]; - pthread_mutex_unlock(&tableValuesLock); - } - else { - cellIsNullOrUnloaded = [tableValues cellIsNullOrUnloadedAtRow:rowIndex column:columnIndex]; + if ([self cellValueIsDisplayedAsHexForColumn:[[tableColumn identifier] integerValue]]) { + [cell setTextColor:rowIndex == [tableContentView selectedRow] ? whiteColor : blueColor]; } + } - if (cellIsNullOrUnloaded) { - [cell setTextColor:rowIndex == [tableContentView selectedRow] ? whiteColor : lightGrayColor]; + // Disable link arrows for the currently editing row and for any NULL or unloaded cells + if (cellIsLinkCell) { + if (cellIsNullOrUnloaded || [tableView editedRow] == rowIndex) { + [cell setLinkActive:NO]; } else { - [cell setTextColor:blackColor]; - - if ([self cellValueIsDisplayedAsHexForColumn:[[tableColumn identifier] integerValue]]) { - [cell setTextColor:rowIndex == [tableContentView selectedRow] ? whiteColor : blueColor]; - } - } - - // Disable link arrows for the currently editing row and for any NULL or unloaded cells - if (cellIsLinkCell) { - if (cellIsNullOrUnloaded || [tableView editedRow] == rowIndex) { - [cell setLinkActive:NO]; - } - else { - [cell setLinkActive:YES]; - } + [cell setLinkActive:YES]; } } + } } #ifndef SP_CODA @@ -4857,10 +4335,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper */ - (NSString *)tableView:(NSTableView *)tableView toolTipForCell:(id)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation { - if (tableView == filterTableView) { - return nil; - } - else if (tableView == tableContentView) { + if (tableView == tableContentView) { if ([[aCell stringValue] length] < 2 || [tableDocumentInstance isWorking]) return nil; @@ -4923,12 +4398,11 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper // Show the cell string value as tooltip (including line breaks and tabs) // by using the cell's font [SPTooltip showWithObject:[aCell stringValue] - atLocation:pos - ofType:@"text" - displayOptions:[NSDictionary dictionaryWithObjectsAndKeys: - [[aCell font] familyName], @"fontname", - [NSString stringWithFormat:@"%f",[[aCell font] pointSize]], @"fontsize", - nil]]; + atLocation:pos + ofType:@"text" + displayOptions:[NSDictionary dictionaryWithObjectsAndKeys:[[aCell font] familyName], @"fontname", + [NSString stringWithFormat:@"%f", [[aCell font] pointSize]], @"fontsize", + nil]]; return nil; } @@ -4968,24 +4442,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper return YES; } -- (void)controlTextDidChange:(NSNotification *)notification -{ -#ifndef SP_CODA - if ([notification object] == filterTableView) { - - NSString *string = [[[[notification userInfo] objectForKey:@"NSFieldEditor"] textStorage] string]; - - if (string && [string length]) { - if (lastEditedFilterTableValue) [lastEditedFilterTableValue release]; - - lastEditedFilterTableValue = [[NSString stringWithString:string] retain]; - } - - [self updateFilterTableClause:string]; - } -#endif -} - /** * If the user selected a table cell which is a blob field and tried to edit it * cancel the inline edit, display the field editor sheet instead for editing @@ -5128,13 +4584,10 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper SPClear(dataColumns); SPClear(oldRow); #ifndef SP_CODA - for (id retainedObject in nibObjectsToRelease) [retainedObject release]; + for (id retainedObject in nibObjectsToRelease) [retainedObject release]; SPClear(nibObjectsToRelease); SPClear(paginationPopover); - SPClear(filterTableData); - if (lastEditedFilterTableValue) SPClear(lastEditedFilterTableValue); - if (filterTableDefaultOperator) SPClear(filterTableDefaultOperator); #endif if (selectedTable) SPClear(selectedTable); if (keys) SPClear(keys); |