diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/SPContentFilterManager.m | 1 | ||||
-rw-r--r-- | Source/SPCustomQuery.m | 1 | ||||
-rw-r--r-- | Source/SPDatabaseDocument.m | 1 | ||||
-rw-r--r-- | Source/SPNarrowDownCompletion.m | 1 | ||||
-rw-r--r-- | Source/SPQueryConsoleDataSource.h | 30 | ||||
-rw-r--r-- | Source/SPQueryConsoleDataSource.m | 89 | ||||
-rw-r--r-- | Source/SPQueryController.h | 39 | ||||
-rw-r--r-- | Source/SPQueryController.m | 680 | ||||
-rw-r--r-- | Source/SPQueryControllerInitializer.h | 32 | ||||
-rw-r--r-- | Source/SPQueryControllerInitializer.m | 135 | ||||
-rw-r--r-- | Source/SPQueryDocumentsController.h | 56 | ||||
-rw-r--r-- | Source/SPQueryDocumentsController.m | 407 | ||||
-rw-r--r-- | Source/SPQueryFavoriteManager.m | 1 | ||||
-rw-r--r-- | Source/SPTableContent.m | 1 | ||||
-rw-r--r-- | Source/SPTextView.m | 1 |
15 files changed, 855 insertions, 620 deletions
diff --git a/Source/SPContentFilterManager.m b/Source/SPContentFilterManager.m index 529ba8b5..0d4b7c18 100644 --- a/Source/SPContentFilterManager.m +++ b/Source/SPContentFilterManager.m @@ -27,6 +27,7 @@ #import "ImageAndTextCell.h" #import "RegexKitLite.h" #import "SPQueryController.h" +#import "SPQueryDocumentsController.h" #import "SPTableContent.h" #import "SPConnectionController.h" #ifndef SP_REFACTOR /* headers */ diff --git a/Source/SPCustomQuery.m b/Source/SPCustomQuery.m index 80b69358..c10d62d3 100644 --- a/Source/SPCustomQuery.m +++ b/Source/SPCustomQuery.m @@ -39,6 +39,7 @@ #import "SPQueryFavoriteManager.h" #endif #import "SPQueryController.h" +#import "SPQueryDocumentsController.h" #import "SPEncodingPopupAccessory.h" #import "SPDataStorage.h" #import "SPAlertSheets.h" diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m index 85dc0615..52858787 100644 --- a/Source/SPDatabaseDocument.m +++ b/Source/SPDatabaseDocument.m @@ -48,6 +48,7 @@ enum { #import "SPExportController.h" #endif #import "SPQueryController.h" +#import "SPQueryDocumentsController.h" #ifndef SP_REFACTOR /* headers */ #import "SPWindowController.h" #endif diff --git a/Source/SPNarrowDownCompletion.m b/Source/SPNarrowDownCompletion.m index 226becf6..9079954d 100644 --- a/Source/SPNarrowDownCompletion.m +++ b/Source/SPNarrowDownCompletion.m @@ -34,6 +34,7 @@ #import "SPQueryController.h" #import "RegexKitLite.h" #import "SPTextView.h" +#import "SPQueryDocumentsController.h" #pragma mark - #pragma mark attribute definition diff --git a/Source/SPQueryConsoleDataSource.h b/Source/SPQueryConsoleDataSource.h new file mode 100644 index 00000000..229e3b9d --- /dev/null +++ b/Source/SPQueryConsoleDataSource.h @@ -0,0 +1,30 @@ +// +// $Id$ +// +// SPQueryConsoleDataSource.h +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on August 30, 2011 +// Copyright (c) 2011 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPQueryController.h" + +@interface SPQueryController (SPQueryConsoleDataSource) + +@end diff --git a/Source/SPQueryConsoleDataSource.m b/Source/SPQueryConsoleDataSource.m new file mode 100644 index 00000000..29a8dc7a --- /dev/null +++ b/Source/SPQueryConsoleDataSource.m @@ -0,0 +1,89 @@ +// +// $Id$ +// +// SPQueryConsoleDataSource.m +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on August 30, 2011 +// Copyright (c) 2011 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPQueryConsoleDataSource.h" +#import "SPConsoleMessage.h" + +static NSUInteger SPMessageTruncateCharacterLength = 256; + +@implementation SPQueryController (SPQueryConsoleDataSource) + +/** + * Table view delegate method. Returns the number of rows in the table veiw. + */ +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView +{ +#ifndef SP_REFACTOR + return [messagesVisibleSet count]; +#else + return 0; +#endif +} + +/** + * Table view delegate method. Returns the specific object for the request column and row. + */ +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +{ +#ifndef SP_REFACTOR + NSString *returnValue = nil; + + id object = [[messagesVisibleSet objectAtIndex:row] valueForKey:[tableColumn identifier]]; + + if ([[tableColumn identifier] isEqualToString:SPTableViewDateColumnID]) { + + returnValue = [dateFormatter stringFromDate:(NSDate *)object]; + } + else { + if ([(NSString *)object length] > SPMessageTruncateCharacterLength) { + object = [NSString stringWithFormat:@"%@...", [object substringToIndex:SPMessageTruncateCharacterLength]]; + } + + returnValue = object; + } + + NSMutableDictionary *stringAtributes = nil; + + if (consoleFont) { + stringAtributes = [NSMutableDictionary dictionaryWithObject:consoleFont forKey:NSFontAttributeName]; + } + + // If this is an error message give it a red colour + if ([(SPConsoleMessage *)[messagesVisibleSet objectAtIndex:row] isError]) { + if (stringAtributes) { + [stringAtributes setObject:[NSColor redColor] forKey:NSForegroundColorAttributeName]; + } + else { + stringAtributes = [NSMutableDictionary dictionaryWithObject:[NSColor redColor] forKey:NSForegroundColorAttributeName]; + } + } + + return [[[NSAttributedString alloc] initWithString:returnValue attributes:stringAtributes] autorelease]; +#else + return nil; +#endif +} + +@end diff --git a/Source/SPQueryController.h b/Source/SPQueryController.h index fdbddb70..8ac4570d 100644 --- a/Source/SPQueryController.h +++ b/Source/SPQueryController.h @@ -23,10 +23,17 @@ // // More info at <http://code.google.com/p/sequel-pro/> +#ifndef SP_REFACTOR +static NSString *SPQueryConsoleWindowAutoSaveName = @"QueryConsole"; + +// Table view column identifier constants +static NSString *SPTableViewDateColumnID = @"messageDate"; +static NSString *SPTableViewConnectionColumnID = @"messageConnection"; +#endif + @interface SPQueryController : NSWindowController { #ifndef SP_REFACTOR /* ivars */ - // QueryConsoleController IBOutlet NSView *saveLogView; IBOutlet NSTableView *consoleTableView; IBOutlet NSSearchField *consoleSearchField; @@ -64,14 +71,11 @@ } #ifndef SP_REFACTOR - @property (readwrite, retain) NSFont *consoleFont; - #endif + (SPQueryController *)sharedQueryController; -// QueryConsoleController - (IBAction)copy:(id)sender; - (IBAction)clearConsole:(id)sender; - (IBAction)saveConsoleAs:(id)sender; @@ -90,31 +94,4 @@ - (NSUInteger)consoleMessageCount; -// Completion List Controller -- (NSArray*)functionList; -- (NSArray*)keywordList; -- (NSString*)argumentSnippetForFunction:(NSString*)func; - -// DocumentsController -- (NSURL *)registerDocumentWithFileURL:(NSURL *)fileURL andContextInfo:(NSMutableDictionary *)contextInfo; -- (void)removeRegisteredDocumentWithFileURL:(NSURL *)fileURL; - -- (void)addFavorite:(NSDictionary *)favorite forFileURL:(NSURL *)fileURL; -- (void)replaceFavoritesByArray:(NSArray *)favoritesArray forFileURL:(NSURL *)fileURL; -- (void)removeFavoriteAtIndex:(NSUInteger)index forFileURL:(NSURL *)fileURL; -- (void)insertFavorite:(NSDictionary *)favorite atIndex:(NSUInteger)index forFileURL:(NSURL *)fileURL; - -- (void)addHistory:(NSString *)history forFileURL:(NSURL *)fileURL; -- (void)replaceHistoryByArray:(NSArray *)historyArray forFileURL:(NSURL *)fileURL; - -- (void)replaceContentFilterByArray:(NSArray *)contentFilterArray ofType:(NSString *)filterType forFileURL:(NSURL *)fileURL; - -- (NSMutableArray *)favoritesForFileURL:(NSURL *)fileURL; -- (NSMutableArray *)historyForFileURL:(NSURL *)fileURL; -- (NSArray *)historyMenuItemsForFileURL:(NSURL *)fileURL; -- (NSUInteger)numberOfHistoryItemsForFileURL:(NSURL *)fileURL; -- (NSMutableDictionary *)contentFilterForFileURL:(NSURL *)fileURL; - -- (NSArray *)queryFavoritesForFileURL:(NSURL *)fileURL andTabTrigger:(NSString *)tabTrigger includeGlobals:(BOOL)includeGlobals; - @end diff --git a/Source/SPQueryController.m b/Source/SPQueryController.m index 08678f83..46c9da3d 100644 --- a/Source/SPQueryController.m +++ b/Source/SPQueryController.m @@ -26,18 +26,11 @@ #import "SPQueryController.h" #import "SPConsoleMessage.h" #import "SPCustomQuery.h" +#import "SPQueryControllerInitializer.h" #import "pthread.h" -#define MESSAGE_TRUNCATE_CHARACTER_LENGTH 256 - -#ifndef SP_REFACTOR -// Table view column identifier constants -static NSString *SPTableViewDateColumnID = @"messageDate"; -static NSString *SPTableViewConnectionColumnID = @"messageConnection"; -#endif - -@interface SPQueryController (PrivateAPI) +@interface SPQueryController () - (void)_updateFilterState; - (BOOL)_messageMatchesCurrentFilters:(NSString *)message; @@ -51,9 +44,7 @@ static SPQueryController *sharedQueryController = nil; @implementation SPQueryController #ifndef SP_REFACTOR - @synthesize consoleFont; - #endif /** @@ -75,8 +66,9 @@ static SPQueryController *sharedQueryController = nil; @synchronized(self) { return [[self sharedQueryController] retain]; } + #ifdef SP_REFACTOR - return nil; // only here to stop clang's "can reach end of non-void function" + return nil; // only here to stop clang's 'can reach end of non-void function' #endif } @@ -111,41 +103,11 @@ static SPQueryController *sharedQueryController = nil; pthread_mutex_init(&consoleLock, NULL); #endif - NSError *readError = nil; - NSString *convError = nil; - NSPropertyListFormat format; - NSDictionary *completionPlist; - NSData *completionTokensData = [NSData dataWithContentsOfFile:[NSBundle pathForResource:@"CompletionTokens.plist" ofType:nil inDirectory:[[NSBundle mainBundle] bundlePath]] - options:NSMappedRead error:&readError]; - - - completionPlist = [NSDictionary dictionaryWithDictionary:[NSPropertyListSerialization propertyListFromData:completionTokensData - mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&convError]]; - - if(completionPlist == nil || readError != nil || convError != nil) { - NSLog(@"Error while reading “CompletionTokens.plist”:\n%@\n%@", [readError localizedDescription], convError); - NSBeep(); - } else { - if([completionPlist objectForKey:@"core_keywords"]) { - completionKeywordList = [[NSArray arrayWithArray:[completionPlist objectForKey:@"core_keywords"]] retain]; - } else { - NSLog(@"No “core_keywords” array found."); - NSBeep(); - } - if([completionPlist objectForKey:@"core_builtin_functions"]) { - completionFunctionList = [[NSArray arrayWithArray:[completionPlist objectForKey:@"core_builtin_functions"]] retain]; - } else { - NSLog(@"No “core_builtin_functions” array found."); - NSBeep(); - } - if([completionPlist objectForKey:@"function_argument_snippets"]) { - functionArgumentSnippets = [[NSDictionary dictionaryWithDictionary:[completionPlist objectForKey:@"function_argument_snippets"]] retain]; - } else { - NSLog(@"No “function_argument_snippets” dictionary found."); - NSBeep(); - } + NSError *error = [self loadCompletionLists]; + + if (error) { + NSLog(@"Error loading completion tokens data: %@", [error localizedDescription]); } - } return self; @@ -165,48 +127,6 @@ static SPQueryController *sharedQueryController = nil; - (void)release { } -/** - * Set the window's auto save name and initialise display - */ -- (void)awakeFromNib -{ -#ifndef SP_REFACTOR /* init ivars */ - prefs = [NSUserDefaults standardUserDefaults]; - - [self setWindowFrameAutosaveName:@"QueryConsole"]; - - // Show/hide table columns - [[consoleTableView tableColumnWithIdentifier:SPTableViewDateColumnID] setHidden:![prefs boolForKey:SPConsoleShowTimestamps]]; - [[consoleTableView tableColumnWithIdentifier:SPTableViewConnectionColumnID] setHidden:![prefs boolForKey:SPConsoleShowConnections]]; - - showSelectStatementsAreDisabled = ![prefs boolForKey:SPConsoleShowSelectsAndShows]; - showHelpStatementsAreDisabled = ![prefs boolForKey:SPConsoleShowHelps]; - - [self _updateFilterState]; - - [loggingDisabledTextField setStringValue:([prefs boolForKey:SPConsoleEnableLogging]) ? @"" : NSLocalizedString(@"Query logging is currently disabled", @"query logging disabled label")]; - - // Setup data formatter - dateFormatter = [[NSDateFormatter alloc] init]; - - [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; - - [dateFormatter setDateStyle:NSDateFormatterNoStyle]; - [dateFormatter setTimeStyle:NSDateFormatterMediumStyle]; - - // Set the process table view's vertical gridlines if required - [consoleTableView setGridStyleMask:([prefs boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone]; - - // Set the strutcture and index view's font - BOOL useMonospacedFont = [prefs boolForKey:SPUseMonospacedFonts]; - - for (NSTableColumn *column in [consoleTableView tableColumns]) - { - [[column dataCell] setFont:(useMonospacedFont) ? [NSFont fontWithName:SPDefaultMonospacedFontName size:[NSFont smallSystemFontSize]] : [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - } -#endif -} - #pragma mark - #pragma mark QueryConsoleController @@ -288,7 +208,7 @@ static SPQueryController *sharedQueryController = nil; [panel setAccessoryView:saveLogView]; - [panel beginSheetForDirectory:nil file:NSLocalizedString(@"ConsoleLog",@"Console : Save as : Initial filename") modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; + [panel beginSheetForDirectory:nil file:NSLocalizedString(@"ConsoleLog", @"Console : Save as : Initial filename") modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; #endif } @@ -383,65 +303,6 @@ static SPQueryController *sharedQueryController = nil; } #pragma mark - -#pragma mark Tableview delegate methods - -/** - * Table view delegate method. Returns the number of rows in the table veiw. - */ -- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView -{ -#ifndef SP_REFACTOR - return [messagesVisibleSet count]; -#else - return 0; -#endif -} - -/** - * Table view delegate method. Returns the specific object for the request column and row. - */ -- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row -{ -#ifndef SP_REFACTOR - NSString *returnValue = nil; - - id object = [[messagesVisibleSet objectAtIndex:row] valueForKey:[tableColumn identifier]]; - - if ([[tableColumn identifier] isEqualToString:SPTableViewDateColumnID]) { - - returnValue = [dateFormatter stringFromDate:(NSDate *)object]; - } - else { - if ([(NSString *)object length] > MESSAGE_TRUNCATE_CHARACTER_LENGTH) { - object = [NSString stringWithFormat:@"%@...", [object substringToIndex:MESSAGE_TRUNCATE_CHARACTER_LENGTH]]; - } - - returnValue = object; - } - - NSMutableDictionary *stringAtributes = nil; - - if (consoleFont) { - stringAtributes = [NSMutableDictionary dictionaryWithObject:consoleFont forKey:NSFontAttributeName]; - } - - // If this is an error message give it a red colour - if ([(SPConsoleMessage *)[messagesVisibleSet objectAtIndex:row] isError]) { - if (stringAtributes) { - [stringAtributes setObject:[NSColor redColor] forKey:NSForegroundColorAttributeName]; - } - else { - stringAtributes = [NSMutableDictionary dictionaryWithObject:[NSColor redColor] forKey:NSForegroundColorAttributeName]; - } - } - - return [[[NSAttributedString alloc] initWithString:returnValue attributes:stringAtributes] autorelease]; -#else - return nil; -#endif -} - -#pragma mark - #pragma mark Other /** @@ -450,13 +311,12 @@ static SPQueryController *sharedQueryController = nil; - (void)controlTextDidChange:(NSNotification *)notification { #ifndef SP_REFACTOR - id object = [notification object]; - - if ([object isEqualTo:consoleSearchField]) { + if ([[notification object] isEqualTo:consoleSearchField]) { // Store the state of the text filter and the current filter string for later quick reference - [activeFilterString setString:[[object stringValue] lowercaseString]]; - filterIsActive = [activeFilterString length]?YES:NO; + [activeFilterString setString:[[[notification object] stringValue] lowercaseString]]; + + filterIsActive = [activeFilterString length] > 0; [self _updateFilterState]; } @@ -471,7 +331,7 @@ static SPQueryController *sharedQueryController = nil; #ifndef SP_REFACTOR // Show/hide logging disabled label if ([keyPath isEqualToString:SPConsoleEnableLogging]) { - [loggingDisabledTextField setStringValue:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? @"" : @"Query logging is currently disabled"]; + [loggingDisabledTextField setStringValue:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? @"" : NSLocalizedString(@"Query logging is currently disabled", @"query logging currently disabled label")]; } // Display table veiew vertical gridlines preference changed else if ([keyPath isEqualToString:SPDisplayTableViewVerticalGridlines]) { @@ -511,7 +371,7 @@ static SPQueryController *sharedQueryController = nil; return [[self window] validateMenuItem:menuItem]; } -- (BOOL) allowConsoleUpdate +- (BOOL)allowConsoleUpdate { #ifndef SP_REFACTOR return allowConsoleUpdate; @@ -520,10 +380,11 @@ static SPQueryController *sharedQueryController = nil; #endif } -- (void) setAllowConsoleUpdate:(BOOL)allowUpdate +- (void)setAllowConsoleUpdate:(BOOL)allowUpdate { #ifndef SP_REFACTOR allowConsoleUpdate = allowUpdate; + if (allowUpdate && [[self window] isVisible]) [self updateEntries]; #endif } @@ -544,459 +405,73 @@ static SPQueryController *sharedQueryController = nil; */ - (NSString *)windowFrameAutosaveName { - return @"QueryConsole"; -} - -#pragma mark - -#pragma mark Completion List Controller - -/** - * Return an array of all pre-defined SQL functions for completion. - */ -- (NSArray*)functionList -{ - if(completionFunctionList != nil && [completionFunctionList count]) - return completionFunctionList; - return [NSArray array]; -} - -/** - * Return an array of all pre-defined SQL keywords for completion. - */ -- (NSArray*)keywordList -{ - if(completionKeywordList != nil && [completionKeywordList count]) - return completionKeywordList; - return [NSArray array]; + return SPQueryConsoleWindowAutoSaveName; } /** - * Return the parameter list as snippet of the passed SQL functions for completion. - * - * @param func The name of the function whose parameter list is asked for - */ -- (NSString*)argumentSnippetForFunction:(NSString*)func -{ - if(functionArgumentSnippets && [functionArgumentSnippets objectForKey:[func uppercaseString]]) - return [functionArgumentSnippets objectForKey:[func uppercaseString]]; - return @""; -} - -#pragma mark - -#pragma mark DocumentsController - -- (NSURL *)registerDocumentWithFileURL:(NSURL *)fileURL andContextInfo:(NSMutableDictionary *)contextInfo -{ -#ifndef SP_REFACTOR - // Register a new untiled document and return its URL - if(fileURL == nil) { - NSURL *new = [NSURL URLWithString:[[NSString stringWithFormat:NSLocalizedString(@"Untitled %ld",@"Title of a new Sequel Pro Document"), (unsigned long)untitledDocumentCounter] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; - untitledDocumentCounter++; - - if(![favoritesContainer objectForKey:[new absoluteString]]) { - NSMutableArray *arr = [[NSMutableArray alloc] init]; - [favoritesContainer setObject:arr forKey:[new absoluteString]]; - [arr release]; - } - - // Set the global history coming from the Prefs as default if available - if(![historyContainer objectForKey:[new absoluteString]]) { - if([prefs objectForKey:SPQueryHistory]) { - NSMutableArray *arr = [[NSMutableArray alloc] init]; - [arr addObjectsFromArray:[prefs objectForKey:SPQueryHistory]]; - [historyContainer setObject:arr forKey:[new absoluteString]]; - [arr release]; - } else { - NSMutableArray *arr = [[NSMutableArray alloc] init]; - [historyContainer setObject:[NSMutableArray array] forKey:[new absoluteString]]; - [arr release]; - } - } - - // Set the doc-based content filters - if(![contentFilterContainer objectForKey:[new absoluteString]]) { - [contentFilterContainer setObject:[NSMutableDictionary dictionary] forKey:[new absoluteString]]; - } - - return new; - - } - - // Register a spf file to manage all query favorites and query history items - // file path based (incl. Untitled docs) in a dictionary whereby the key represents the file URL as string. - if(![favoritesContainer objectForKey:[fileURL absoluteString]]) { - if(contextInfo != nil && [contextInfo objectForKey:SPQueryFavorites] && [[contextInfo objectForKey:SPQueryFavorites] count]) { - NSMutableArray *arr = [[NSMutableArray alloc] init]; - [arr addObjectsFromArray:[contextInfo objectForKey:SPQueryFavorites]]; - [favoritesContainer setObject:arr forKey:[fileURL absoluteString]]; - [arr release]; - } else { - NSMutableArray *arr = [[NSMutableArray alloc] init]; - [favoritesContainer setObject:arr forKey:[fileURL absoluteString]]; - [arr release]; - } - } - - if(![historyContainer objectForKey:[fileURL absoluteString]]) { - if(contextInfo != nil && [contextInfo objectForKey:SPQueryHistory] && [[contextInfo objectForKey:SPQueryHistory] count]) { - NSMutableArray *arr = [[NSMutableArray alloc] init]; - [arr addObjectsFromArray:[contextInfo objectForKey:SPQueryHistory]]; - [historyContainer setObject:arr forKey:[fileURL absoluteString]]; - [arr release]; - } else { - NSMutableArray *arr = [[NSMutableArray alloc] init]; - [historyContainer setObject:arr forKey:[fileURL absoluteString]]; - [arr release]; - } - } - - if(![contentFilterContainer objectForKey:[fileURL absoluteString]]) { - if(contextInfo != nil && [contextInfo objectForKey:SPContentFilters]) { - [contentFilterContainer setObject:[contextInfo objectForKey:SPContentFilters] forKey:[fileURL absoluteString]]; - } else { - NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; - [contentFilterContainer setObject:dict forKey:[fileURL absoluteString]]; - [dict release]; - } - } - - return fileURL; -#else - return nil; -#endif -} - -- (void)removeRegisteredDocumentWithFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - // Check for multiple instance of the same document. - // Remove it if only one instance was registerd. - NSArray *allDocs = [[NSApp delegate] orderedDocuments]; - NSMutableArray *allURLs = [NSMutableArray array]; - for(id doc in allDocs) { - if (![doc fileURL]) continue; - if([allURLs containsObject:[doc fileURL]]) - return; - else - [allURLs addObject:[doc fileURL]]; - } - - if([favoritesContainer objectForKey:[fileURL absoluteString]]) - [favoritesContainer removeObjectForKey:[fileURL absoluteString]]; - if([historyContainer objectForKey:[fileURL absoluteString]]) - [historyContainer removeObjectForKey:[fileURL absoluteString]]; - if([contentFilterContainer objectForKey:[fileURL absoluteString]]) - [contentFilterContainer removeObjectForKey:[fileURL absoluteString]]; -#endif -} - -- (void)replaceContentFilterByArray:(NSArray *)contentFilterArray ofType:(NSString *)filterType forFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - if([contentFilterContainer objectForKey:[fileURL absoluteString]]) { - NSMutableDictionary *c = [[NSMutableDictionary alloc] init]; - [c setDictionary:[contentFilterContainer objectForKey:[fileURL absoluteString]]]; - [c setObject:contentFilterArray forKey:filterType]; - [contentFilterContainer setObject:c forKey:[fileURL absoluteString]]; - [c release]; - } -#endif -} - -- (void)replaceFavoritesByArray:(NSArray *)favoritesArray forFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - if([favoritesContainer objectForKey:[fileURL absoluteString]]) - [favoritesContainer setObject:favoritesArray forKey:[fileURL absoluteString]]; -#endif -} - -- (void)replaceHistoryByArray:(NSArray *)historyArray forFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - if([historyContainer objectForKey:[fileURL absoluteString]]) - [historyContainer setObject:historyArray forKey:[fileURL absoluteString]]; - - // Inform all opened documents to update the history list - for(id doc in [[NSApp delegate] orderedDocuments]) - if([[doc valueForKeyPath:@"customQueryInstance"] respondsToSelector:@selector(historyItemsHaveBeenUpdated:)]) - [[doc valueForKeyPath:@"customQueryInstance"] performSelectorOnMainThread:@selector(historyItemsHaveBeenUpdated:) withObject:self waitUntilDone:NO]; - - // User did choose to clear the global history list - if(![fileURL isFileURL] && ![historyArray count]) - [prefs setObject:historyArray forKey:SPQueryHistory]; -#endif -} - -- (void)addFavorite:(NSDictionary *)favorite forFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - if([favoritesContainer objectForKey:[fileURL absoluteString]]) - [[favoritesContainer objectForKey:[fileURL absoluteString]] addObject:favorite]; -#endif -} - -- (void)addHistory:(NSString *)history forFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - NSUInteger maxHistoryItems = [[prefs objectForKey:SPCustomQueryMaxHistoryItems] integerValue]; - - // Save each history item due to its document source - if([historyContainer objectForKey:[fileURL absoluteString]]) { - - // Remove all duplicates by using a NSPopUpButton - NSPopUpButton *uniquifier = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0,0,0,0) pullsDown:YES]; - [uniquifier addItemsWithTitles:[historyContainer objectForKey:[fileURL absoluteString]]]; - [uniquifier insertItemWithTitle:history atIndex:0]; - - while ( (NSUInteger)[uniquifier numberOfItems] > maxHistoryItems ) - [uniquifier removeItemAtIndex:[uniquifier numberOfItems]-1]; - - [self replaceHistoryByArray:[uniquifier itemTitles] forFileURL:fileURL]; - [uniquifier release]; - } - - // Save history items coming from each Untitled document in the global Preferences successively - // regardingless of the source document. - if(![fileURL isFileURL]) { - - // Remove all duplicates by using a NSPopUpButton - NSPopUpButton *uniquifier = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0,0,0,0) pullsDown:YES]; - [uniquifier addItemsWithTitles:[prefs objectForKey:SPQueryHistory]]; - [uniquifier insertItemWithTitle:history atIndex:0]; - - while ( (NSUInteger)[uniquifier numberOfItems] > maxHistoryItems ) - [uniquifier removeItemAtIndex:[uniquifier numberOfItems]-1]; - - [prefs setObject:[uniquifier itemTitles] forKey:SPQueryHistory]; - [uniquifier release]; - } -#endif -} - -- (NSMutableArray *)favoritesForFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - if([favoritesContainer objectForKey:[fileURL absoluteString]]) - return [favoritesContainer objectForKey:[fileURL absoluteString]]; -#endif - - return [NSMutableArray array]; -} - -- (NSMutableArray *)historyForFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - if([historyContainer objectForKey:[fileURL absoluteString]]) - return [historyContainer objectForKey:[fileURL absoluteString]]; -#endif - - return [NSMutableArray array]; -} - -- (NSArray *)historyMenuItemsForFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - if([historyContainer objectForKey:[fileURL absoluteString]]) { - NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:[[historyContainer objectForKey:[fileURL absoluteString]] count]]; - NSMenuItem *historyMenuItem; - for(NSString* history in [historyContainer objectForKey:[fileURL absoluteString]]) { - historyMenuItem = [[[NSMenuItem alloc] initWithTitle:([history length] > 64) ? [NSString stringWithFormat:@"%@…", [history substringToIndex:63]] : history - action:NULL - keyEquivalent:@""] autorelease]; - [historyMenuItem setToolTip:([history length] > 256) ? [NSString stringWithFormat:@"%@…", [history substringToIndex:255]] : history]; - [returnArray addObject:historyMenuItem]; - } - - return returnArray; - } -#endif - - return [NSArray array]; -} - -/** - * Return the number of history items for the passed file URL - * - * @param fileURL The NSURL of the current active SPDatabaseDocument - * - */ -- (NSUInteger)numberOfHistoryItemsForFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - if([historyContainer objectForKey:[fileURL absoluteString]]) - return [[historyContainer objectForKey:[fileURL absoluteString]] count]; - else -#endif - return 0; -} - -/** - * Return a mutable dictionary of all content filters for the passed file URL. - * If no content filters were found it returns an empty mutable dictionary. - * - * @param fileURL The NSURL of the current active SPDatabaseDocument - * - */ -- (NSMutableDictionary *)contentFilterForFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - if([contentFilterContainer objectForKey:[fileURL absoluteString]]) - return [contentFilterContainer objectForKey:[fileURL absoluteString]]; -#endif - - return [NSMutableDictionary dictionary]; -} - -- (NSArray *)queryFavoritesForFileURL:(NSURL *)fileURL andTabTrigger:(NSString *)tabTrigger includeGlobals:(BOOL)includeGlobals -{ - if(![tabTrigger length]) return [NSArray array]; - - NSMutableArray *result = [[NSMutableArray alloc] init]; - for(id fav in [self favoritesForFileURL:fileURL]) { - if([fav objectForKey:@"tabtrigger"] && [[fav objectForKey:@"tabtrigger"] isEqualToString:tabTrigger]) - [result addObject:fav]; - } - -#ifndef SP_REFACTOR - if(includeGlobals && [prefs objectForKey:SPQueryFavorites]) { - for(id fav in [prefs objectForKey:SPQueryFavorites]) { - if([fav objectForKey:@"tabtrigger"] && [[fav objectForKey:@"tabtrigger"] isEqualToString:tabTrigger]) { - [result addObject:fav]; - break; - } - } - } -#endif - - return [result autorelease]; -} - -/** - * Remove a Query Favorite the passed file URL - * - * @param index The index of the to be removed favorite - * - * @param fileURL The NSURL of the current active SPDatabaseDocument - * - */ -- (void)removeFavoriteAtIndex:(NSUInteger)index forFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - [[favoritesContainer objectForKey:[fileURL absoluteString]] removeObjectAtIndex:index]; -#endif -} - -- (void)insertFavorite:(NSDictionary *)favorite atIndex:(NSUInteger)index forFileURL:(NSURL *)fileURL -{ -#ifndef SP_REFACTOR - [[favoritesContainer objectForKey:[fileURL absoluteString]] insertObject:favorite atIndex:index]; -#endif -} - -#pragma mark - - -/** - * Dealloc. - */ -- (void)dealloc -{ -#ifndef SP_REFACTOR - messagesVisibleSet = nil; -#endif - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - -#ifndef SP_REFACTOR - [dateFormatter release], dateFormatter = nil; - - [messagesFullSet release], messagesFullSet = nil; - [messagesFilteredSet release], messagesFilteredSet = nil; - [activeFilterString release], activeFilterString = nil; - - [favoritesContainer release], favoritesContainer = nil; - [historyContainer release], historyContainer = nil; - [contentFilterContainer release], contentFilterContainer = nil; -#endif - - if(completionKeywordList) [completionKeywordList release]; - if(completionFunctionList) [completionFunctionList release]; - if(functionArgumentSnippets) [functionArgumentSnippets release]; - -#ifndef SP_REFACTOR - pthread_mutex_destroy(&consoleLock); -#endif - - [super dealloc]; -} - -@end - -@implementation SPQueryController (PrivateAPI) - -/** * Updates the filtered result set based on any filter string and whether or not * all SELECT nd SHOW statements should be shown within the console. */ - (void)_updateFilterState { #ifndef SP_REFACTOR - // Display start progress spinner [progressIndicator setHidden:NO]; [progressIndicator startAnimation:self]; - + // Don't allow clearing the console while filtering its content [saveConsoleButton setEnabled:NO]; [clearConsoleButton setEnabled:NO]; - + [messagesFilteredSet removeAllObjects]; - + // If filtering is disabled and all show/selects are shown, empty the filtered // result set and set the full set to visible. if (!filterIsActive && !showSelectStatementsAreDisabled && !showHelpStatementsAreDisabled) { messagesVisibleSet = messagesFullSet; - + [consoleTableView reloadData]; [consoleTableView scrollRowToVisible:([messagesVisibleSet count] - 1)]; - + [saveConsoleButton setEnabled:YES]; [clearConsoleButton setEnabled:YES]; - + [saveConsoleButton setTitle:NSLocalizedString(@"Save As...", @"save as button title")]; - + // Hide progress spinner [progressIndicator setHidden:YES]; [progressIndicator stopAnimation:self]; + return; } - + // Cache frequently used selector, avoiding dynamic binding overhead IMP messageMatchesFilters = [self methodForSelector:@selector(_messageMatchesCurrentFilters:)]; - + // Loop through all the messages in the full set to determine which should be // added to the filtered set. for (SPConsoleMessage *message in messagesFullSet) { - + // Add a reference to the message to the filtered set if filters are active and the // current message matches them if ((messageMatchesFilters)(self, @selector(_messageMatchesCurrentFilters:), [message message])) { [messagesFilteredSet addObject:message]; } } - + // Ensure that the filtered set is marked as the currently visible set. messagesVisibleSet = messagesFilteredSet; - + [consoleTableView reloadData]; [consoleTableView scrollRowToVisible:([messagesVisibleSet count] - 1)]; - + if ([messagesVisibleSet count] > 0) { [saveConsoleButton setEnabled:YES]; [clearConsoleButton setEnabled:YES]; } - + [saveConsoleButton setTitle:NSLocalizedString(@"Save View As...", @"save view as button title")]; - + // Hide progress spinner [progressIndicator setHidden:YES]; [progressIndicator stopAnimation:self]; @@ -1010,32 +485,27 @@ static SPQueryController *sharedQueryController = nil; - (BOOL)_messageMatchesCurrentFilters:(NSString *)message { BOOL messageMatchesCurrentFilters = YES; - + #ifndef SP_REFACTOR // Check whether to hide the message based on the current filter text, if any - if (filterIsActive - && [message rangeOfString:activeFilterString options:NSCaseInsensitiveSearch].location == NSNotFound) - { + if (filterIsActive && [message rangeOfString:activeFilterString options:NSCaseInsensitiveSearch].location == NSNotFound) { messageMatchesCurrentFilters = NO; } - + // If hiding SELECTs and SHOWs is toggled to on, check whether the message is a SELECT or SHOW - if (messageMatchesCurrentFilters - && showSelectStatementsAreDisabled - && ([[message uppercaseString] hasPrefix:@"SELECT"] || [[message uppercaseString] hasPrefix:@"SHOW"])) + if (messageMatchesCurrentFilters && + showSelectStatementsAreDisabled && + ([[message uppercaseString] hasPrefix:@"SELECT"] || [[message uppercaseString] hasPrefix:@"SHOW"])) { messageMatchesCurrentFilters = NO; } - + // If hiding HELP is toggled to on, check whether the message is a HELP - if (messageMatchesCurrentFilters - && showHelpStatementsAreDisabled - && ([[message uppercaseString] hasPrefix:@"HELP"])) - { + if (messageMatchesCurrentFilters && showHelpStatementsAreDisabled && ([[message uppercaseString] hasPrefix:@"HELP"])) { messageMatchesCurrentFilters = NO; } #endif - + return messageMatchesCurrentFilters; } @@ -1046,30 +516,29 @@ static SPQueryController *sharedQueryController = nil; - (NSString *)_getConsoleStringWithTimeStamps:(BOOL)timeStamps connections:(BOOL)connections { NSMutableString *consoleString = [NSMutableString string]; - + #ifndef SP_REFACTOR for (SPConsoleMessage *message in messagesVisibleSet) { // As we are going to save the messages as an SQL file we need to comment // the timestamps and connections if included. if (timeStamps || connections) [consoleString appendString:@"/* "]; - + // If the timestamp column is not hidden we need to include them in the copy if (timeStamps) [consoleString appendFormat:@"%@ ", [dateFormatter stringFromDate:[message messageDate]]]; - + // If the connection column is not hidden we need to include them in the copy if (connections) [consoleString appendFormat:@"%@ ", [message messageConnection]]; - + // Close the comment if (timeStamps || connections) [consoleString appendString:@"*/ "]; - + [consoleString appendFormat:@"%@\n", [message message]]; - } #endif - + return consoleString; } @@ -1080,20 +549,18 @@ static SPQueryController *sharedQueryController = nil; { #ifndef SP_REFACTOR NSString *messageTemp = [[message stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] stringByReplacingOccurrencesOfString:@"\n" withString:@" "]; - + // Only append a semi-colon (;) if the supplied message is not an error - if (!error) { - messageTemp = [messageTemp stringByAppendingString:@";"]; - } - + if (!error) messageTemp = [messageTemp stringByAppendingString:@";"]; + SPConsoleMessage *consoleMessage = [SPConsoleMessage consoleMessageWithMessage:messageTemp date:[NSDate date] connection:connection]; - + [consoleMessage setIsError:error]; - + pthread_mutex_lock(&consoleLock); [messagesFullSet addObject:consoleMessage]; - + // If filtering is active, determine whether to add a reference to the filtered set if ((showSelectStatementsAreDisabled || showHelpStatementsAreDisabled || filterIsActive) && [self _messageMatchesCurrentFilters:[consoleMessage message]]) @@ -1102,7 +569,7 @@ static SPQueryController *sharedQueryController = nil; [saveConsoleButton setEnabled:YES]; [clearConsoleButton setEnabled:YES]; } - + // Reload the table and scroll to the new message if it's visible (for speed) if (allowConsoleUpdate && [[self window] isVisible]) { [consoleTableView noteNumberOfRowsChanged]; @@ -1114,4 +581,39 @@ static SPQueryController *sharedQueryController = nil; #endif } +#pragma mark - + +/** + * Dealloc. + */ +- (void)dealloc +{ +#ifndef SP_REFACTOR + messagesVisibleSet = nil; +#endif + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + +#ifndef SP_REFACTOR + [dateFormatter release], dateFormatter = nil; + + [messagesFullSet release], messagesFullSet = nil; + [messagesFilteredSet release], messagesFilteredSet = nil; + [activeFilterString release], activeFilterString = nil; + + [favoritesContainer release], favoritesContainer = nil; + [historyContainer release], historyContainer = nil; + [contentFilterContainer release], contentFilterContainer = nil; +#endif + + if (completionKeywordList) [completionKeywordList release], completionKeywordList = nil; + if (completionFunctionList) [completionFunctionList release], completionFunctionList = nil; + if (functionArgumentSnippets) [functionArgumentSnippets release], functionArgumentSnippets = nil; + +#ifndef SP_REFACTOR + pthread_mutex_destroy(&consoleLock); +#endif + + [super dealloc]; +} + @end diff --git a/Source/SPQueryControllerInitializer.h b/Source/SPQueryControllerInitializer.h new file mode 100644 index 00000000..2bee90af --- /dev/null +++ b/Source/SPQueryControllerInitializer.h @@ -0,0 +1,32 @@ +// +// $Id$ +// +// SPQueryControllerInitializer.h +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on September 1, 2011 +// Copyright (c) 2011 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPQueryController.h" + +@interface SPQueryController (SPQueryControllerInitializer) + +- (NSError *)loadCompletionLists; + +@end diff --git a/Source/SPQueryControllerInitializer.m b/Source/SPQueryControllerInitializer.m new file mode 100644 index 00000000..2d43898e --- /dev/null +++ b/Source/SPQueryControllerInitializer.m @@ -0,0 +1,135 @@ +// +// $Id$ +// +// SPQueryControllerInitializer.m +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on September 1, 2011 +// Copyright (c) 2011 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPQueryControllerInitializer.h" + +static NSString *SPCompletionTokensFilename = @"CompletionTokens.plist"; + +static NSString *SPCompletionTokensKeywordsKey = @"core_keywords"; +static NSString *SPCompletionTokensFunctionsKey = @"core_builtin_functions"; +static NSString *SPCompletionTokensSnippetsKey = @"function_argument_snippets"; + +@interface SPQueryController () + +- (void)_updateFilterState; + +@end + +@implementation SPQueryController (SPQueryControllerInitializer) + +/** + * Set the window's auto save name and initialise display + */ +- (void)awakeFromNib +{ +#ifndef SP_REFACTOR /* init ivars */ + prefs = [NSUserDefaults standardUserDefaults]; + + [self setWindowFrameAutosaveName:SPQueryConsoleWindowAutoSaveName]; + + // Show/hide table columns + [[consoleTableView tableColumnWithIdentifier:SPTableViewDateColumnID] setHidden:![prefs boolForKey:SPConsoleShowTimestamps]]; + [[consoleTableView tableColumnWithIdentifier:SPTableViewConnectionColumnID] setHidden:![prefs boolForKey:SPConsoleShowConnections]]; + + showSelectStatementsAreDisabled = ![prefs boolForKey:SPConsoleShowSelectsAndShows]; + showHelpStatementsAreDisabled = ![prefs boolForKey:SPConsoleShowHelps]; + + [self _updateFilterState]; + + [loggingDisabledTextField setStringValue:([prefs boolForKey:SPConsoleEnableLogging]) ? @"" : NSLocalizedString(@"Query logging is currently disabled", @"query logging disabled label")]; + + // Setup data formatter + dateFormatter = [[NSDateFormatter alloc] init]; + + [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; + + [dateFormatter setDateStyle:NSDateFormatterNoStyle]; + [dateFormatter setTimeStyle:NSDateFormatterMediumStyle]; + + // Set the process table view's vertical gridlines if required + [consoleTableView setGridStyleMask:([prefs boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone]; + + // Set the strutcture and index view's font + BOOL useMonospacedFont = [prefs boolForKey:SPUseMonospacedFonts]; + + for (NSTableColumn *column in [consoleTableView tableColumns]) + { + [[column dataCell] setFont:(useMonospacedFont) ? [NSFont fontWithName:SPDefaultMonospacedFontName size:[NSFont smallSystemFontSize]] : [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + } +#endif +} + +/** + * Loads the query controller's completion tokens data. + */ +- (NSError *)loadCompletionLists +{ + NSError *readError = nil; + NSString *convError = nil; + NSString *errorDescription = nil; + + NSPropertyListFormat format; + NSData *completionTokensData = [NSData dataWithContentsOfFile: + [NSBundle pathForResource:SPCompletionTokensFilename + ofType:nil + inDirectory:[[NSBundle mainBundle] bundlePath]] + options:NSMappedRead error:&readError]; + + NSDictionary *completionPlist = [NSDictionary dictionaryWithDictionary: + [NSPropertyListSerialization propertyListFromData:completionTokensData + mutabilityOption:NSPropertyListMutableContainersAndLeaves + format:&format + errorDescription:&convError]]; + + if (completionPlist == nil || readError != nil || convError != nil) { + errorDescription = [NSString stringWithFormat:@"Error reading '%@': %@, %@", SPCompletionTokensFilename, [readError localizedDescription], convError]; + } + else { + if ([completionPlist objectForKey:SPCompletionTokensKeywordsKey]) { + completionKeywordList = [[NSArray arrayWithArray:[completionPlist objectForKey:SPCompletionTokensKeywordsKey]] retain]; + } + else { + errorDescription = [NSString stringWithFormat:@"No '%@' array found.", SPCompletionTokensKeywordsKey]; + } + + if ([completionPlist objectForKey:SPCompletionTokensFunctionsKey]) { + completionFunctionList = [[NSArray arrayWithArray:[completionPlist objectForKey:SPCompletionTokensFunctionsKey]] retain]; + } + else { + errorDescription = [NSString stringWithFormat:@"No '%@' array found.", SPCompletionTokensFunctionsKey]; + } + + if ([completionPlist objectForKey:SPCompletionTokensSnippetsKey]) { + functionArgumentSnippets = [[NSDictionary dictionaryWithDictionary:[completionPlist objectForKey:SPCompletionTokensSnippetsKey]] retain]; + } + else { + errorDescription = [NSString stringWithFormat:@"No '%@' dictionary found.", SPCompletionTokensSnippetsKey]; + } + } + + return errorDescription ? [NSError errorWithDomain:NSCocoaErrorDomain code:1 userInfo:[NSDictionary dictionaryWithObject:errorDescription forKey:NSLocalizedDescriptionKey]] : nil; +} + +@end diff --git a/Source/SPQueryDocumentsController.h b/Source/SPQueryDocumentsController.h new file mode 100644 index 00000000..2d109b27 --- /dev/null +++ b/Source/SPQueryDocumentsController.h @@ -0,0 +1,56 @@ +// +// $Id$ +// +// SPQueryDocumentsController.h +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on August 30, 2011 +// Copyright (c) 2011 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPQueryController.h" + +@interface SPQueryController (SPQueryDocumentsController) + +- (NSURL *)registerDocumentWithFileURL:(NSURL *)fileURL andContextInfo:(NSMutableDictionary *)contextInfo; +- (void)removeRegisteredDocumentWithFileURL:(NSURL *)fileURL; + +- (void)addFavorite:(NSDictionary *)favorite forFileURL:(NSURL *)fileURL; +- (void)replaceFavoritesByArray:(NSArray *)favoritesArray forFileURL:(NSURL *)fileURL; +- (void)removeFavoriteAtIndex:(NSUInteger)index forFileURL:(NSURL *)fileURL; +- (void)insertFavorite:(NSDictionary *)favorite atIndex:(NSUInteger)index forFileURL:(NSURL *)fileURL; + +- (void)addHistory:(NSString *)history forFileURL:(NSURL *)fileURL; +- (void)replaceHistoryByArray:(NSArray *)historyArray forFileURL:(NSURL *)fileURL; + +- (void)replaceContentFilterByArray:(NSArray *)contentFilterArray ofType:(NSString *)filterType forFileURL:(NSURL *)fileURL; + +- (NSMutableArray *)favoritesForFileURL:(NSURL *)fileURL; +- (NSMutableArray *)historyForFileURL:(NSURL *)fileURL; +- (NSArray *)historyMenuItemsForFileURL:(NSURL *)fileURL; +- (NSUInteger)numberOfHistoryItemsForFileURL:(NSURL *)fileURL; +- (NSMutableDictionary *)contentFilterForFileURL:(NSURL *)fileURL; + +- (NSArray *)queryFavoritesForFileURL:(NSURL *)fileURL andTabTrigger:(NSString *)tabTrigger includeGlobals:(BOOL)includeGlobals; + +// Completion list controller +- (NSArray*)functionList; +- (NSArray*)keywordList; +- (NSString*)argumentSnippetForFunction:(NSString*)func; + +@end diff --git a/Source/SPQueryDocumentsController.m b/Source/SPQueryDocumentsController.m new file mode 100644 index 00000000..b4c3df22 --- /dev/null +++ b/Source/SPQueryDocumentsController.m @@ -0,0 +1,407 @@ +// +// $Id$ +// +// SPQueryDocumentsController.m +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on August 30, 2011 +// Copyright (c) 2011 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPQueryDocumentsController.h" + +@implementation SPQueryController (SPQueryDocumentsController) + +- (NSURL *)registerDocumentWithFileURL:(NSURL *)fileURL andContextInfo:(NSMutableDictionary *)contextInfo +{ +#ifndef SP_REFACTOR + // Register a new untiled document and return its URL + if (fileURL == nil) { + NSURL *new = [NSURL URLWithString:[[NSString stringWithFormat:NSLocalizedString(@"Untitled %ld",@"Title of a new Sequel Pro Document"), (unsigned long)untitledDocumentCounter] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + untitledDocumentCounter++; + + if (![favoritesContainer objectForKey:[new absoluteString]]) { + NSMutableArray *arr = [[NSMutableArray alloc] init]; + [favoritesContainer setObject:arr forKey:[new absoluteString]]; + [arr release]; + } + + // Set the global history coming from the Prefs as default if available + if (![historyContainer objectForKey:[new absoluteString]]) { + if ([prefs objectForKey:SPQueryHistory]) { + NSMutableArray *arr = [[NSMutableArray alloc] init]; + [arr addObjectsFromArray:[prefs objectForKey:SPQueryHistory]]; + [historyContainer setObject:arr forKey:[new absoluteString]]; + [arr release]; + } + else { + NSMutableArray *arr = [[NSMutableArray alloc] init]; + [historyContainer setObject:[NSMutableArray array] forKey:[new absoluteString]]; + [arr release]; + } + } + + // Set the doc-based content filters + if (![contentFilterContainer objectForKey:[new absoluteString]]) { + [contentFilterContainer setObject:[NSMutableDictionary dictionary] forKey:[new absoluteString]]; + } + + return new; + } + + // Register a spf file to manage all query favorites and query history items + // file path based (incl. Untitled docs) in a dictionary whereby the key represents the file URL as string. + if (![favoritesContainer objectForKey:[fileURL absoluteString]]) { + if (contextInfo != nil && [contextInfo objectForKey:SPQueryFavorites] && [[contextInfo objectForKey:SPQueryFavorites] count]) { + NSMutableArray *arr = [[NSMutableArray alloc] init]; + [arr addObjectsFromArray:[contextInfo objectForKey:SPQueryFavorites]]; + [favoritesContainer setObject:arr forKey:[fileURL absoluteString]]; + [arr release]; + } + else { + NSMutableArray *arr = [[NSMutableArray alloc] init]; + [favoritesContainer setObject:arr forKey:[fileURL absoluteString]]; + [arr release]; + } + } + + if (![historyContainer objectForKey:[fileURL absoluteString]]) { + if (contextInfo != nil && [contextInfo objectForKey:SPQueryHistory] && [[contextInfo objectForKey:SPQueryHistory] count]) { + NSMutableArray *arr = [[NSMutableArray alloc] init]; + [arr addObjectsFromArray:[contextInfo objectForKey:SPQueryHistory]]; + [historyContainer setObject:arr forKey:[fileURL absoluteString]]; + [arr release]; + } + else { + NSMutableArray *arr = [[NSMutableArray alloc] init]; + [historyContainer setObject:arr forKey:[fileURL absoluteString]]; + [arr release]; + } + } + + if (![contentFilterContainer objectForKey:[fileURL absoluteString]]) { + if (contextInfo != nil && [contextInfo objectForKey:SPContentFilters]) { + [contentFilterContainer setObject:[contextInfo objectForKey:SPContentFilters] forKey:[fileURL absoluteString]]; + } + else { + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + [contentFilterContainer setObject:dict forKey:[fileURL absoluteString]]; + [dict release]; + } + } + + return fileURL; +#else + return nil; +#endif +} + +- (void)removeRegisteredDocumentWithFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + // Check for multiple instance of the same document. + // Remove it if only one instance was registerd. + NSArray *allDocs = [[NSApp delegate] orderedDocuments]; + NSMutableArray *allURLs = [NSMutableArray array]; + + for (id doc in allDocs) + { + if (![doc fileURL]) continue; + + if ([allURLs containsObject:[doc fileURL]]) { + return; + } + else { + [allURLs addObject:[doc fileURL]]; + } + } + + if ([favoritesContainer objectForKey:[fileURL absoluteString]]) { + [favoritesContainer removeObjectForKey:[fileURL absoluteString]]; + } + + if ([historyContainer objectForKey:[fileURL absoluteString]]) { + [historyContainer removeObjectForKey:[fileURL absoluteString]]; + } + + if ([contentFilterContainer objectForKey:[fileURL absoluteString]]) { + [contentFilterContainer removeObjectForKey:[fileURL absoluteString]]; + } +#endif +} + +- (void)replaceContentFilterByArray:(NSArray *)contentFilterArray ofType:(NSString *)filterType forFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + if ([contentFilterContainer objectForKey:[fileURL absoluteString]]) { + NSMutableDictionary *c = [[NSMutableDictionary alloc] init]; + [c setDictionary:[contentFilterContainer objectForKey:[fileURL absoluteString]]]; + [c setObject:contentFilterArray forKey:filterType]; + [contentFilterContainer setObject:c forKey:[fileURL absoluteString]]; + [c release]; + } +#endif +} + +- (void)replaceFavoritesByArray:(NSArray *)favoritesArray forFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + if ([favoritesContainer objectForKey:[fileURL absoluteString]]) { + [favoritesContainer setObject:favoritesArray forKey:[fileURL absoluteString]]; + } +#endif +} + +/** + * Remove a Query Favorite the passed file URL + * + * @param index The index of the to be removed favorite + * + * @param fileURL The NSURL of the current active SPDatabaseDocument + */ +- (void)removeFavoriteAtIndex:(NSUInteger)index forFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + [[favoritesContainer objectForKey:[fileURL absoluteString]] removeObjectAtIndex:index]; +#endif +} + +- (void)insertFavorite:(NSDictionary *)favorite atIndex:(NSUInteger)index forFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + [[favoritesContainer objectForKey:[fileURL absoluteString]] insertObject:favorite atIndex:index]; +#endif +} + +- (void)replaceHistoryByArray:(NSArray *)historyArray forFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + if ([historyContainer objectForKey:[fileURL absoluteString]]) { + [historyContainer setObject:historyArray forKey:[fileURL absoluteString]]; + } + + // Inform all opened documents to update the history list + for (id doc in [[NSApp delegate] orderedDocuments]) + { + if([[doc valueForKeyPath:@"customQueryInstance"] respondsToSelector:@selector(historyItemsHaveBeenUpdated:)]) { + [[doc valueForKeyPath:@"customQueryInstance"] performSelectorOnMainThread:@selector(historyItemsHaveBeenUpdated:) withObject:self waitUntilDone:NO]; + } + } + + // User did choose to clear the global history list + if (![fileURL isFileURL] && ![historyArray count]) { + [prefs setObject:historyArray forKey:SPQueryHistory]; + } +#endif +} + +- (void)addFavorite:(NSDictionary *)favorite forFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + if ([favoritesContainer objectForKey:[fileURL absoluteString]]) { + [[favoritesContainer objectForKey:[fileURL absoluteString]] addObject:favorite]; + } +#endif +} + +- (void)addHistory:(NSString *)history forFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + NSUInteger maxHistoryItems = [[prefs objectForKey:SPCustomQueryMaxHistoryItems] integerValue]; + + // Save each history item due to its document source + if ([historyContainer objectForKey:[fileURL absoluteString]]) { + + // Remove all duplicates by using a NSPopUpButton + NSPopUpButton *uniquifier = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0,0,0,0) pullsDown:YES]; + + [uniquifier addItemsWithTitles:[historyContainer objectForKey:[fileURL absoluteString]]]; + [uniquifier insertItemWithTitle:history atIndex:0]; + + while ((NSUInteger)[uniquifier numberOfItems] > maxHistoryItems) + { + [uniquifier removeItemAtIndex:[uniquifier numberOfItems]-1]; + } + + [self replaceHistoryByArray:[uniquifier itemTitles] forFileURL:fileURL]; + [uniquifier release]; + } + + // Save history items coming from each Untitled document in the global Preferences successively + // regardingless of the source document. + if (![fileURL isFileURL]) { + + // Remove all duplicates by using a NSPopUpButton + NSPopUpButton *uniquifier = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0,0,0,0) pullsDown:YES]; + [uniquifier addItemsWithTitles:[prefs objectForKey:SPQueryHistory]]; + [uniquifier insertItemWithTitle:history atIndex:0]; + + while ((NSUInteger)[uniquifier numberOfItems] > maxHistoryItems) + { + [uniquifier removeItemAtIndex:[uniquifier numberOfItems] - 1]; + } + + [prefs setObject:[uniquifier itemTitles] forKey:SPQueryHistory]; + [uniquifier release]; + } +#endif +} + +- (NSMutableArray *)favoritesForFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + if ([favoritesContainer objectForKey:[fileURL absoluteString]]) { + return [favoritesContainer objectForKey:[fileURL absoluteString]]; + } +#endif + + return [NSMutableArray array]; +} + +- (NSMutableArray *)historyForFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + if ([historyContainer objectForKey:[fileURL absoluteString]]) { + return [historyContainer objectForKey:[fileURL absoluteString]]; + } +#endif + + return [NSMutableArray array]; +} + +- (NSArray *)historyMenuItemsForFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + if ([historyContainer objectForKey:[fileURL absoluteString]]) { + NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:[[historyContainer objectForKey:[fileURL absoluteString]] count]]; + NSMenuItem *historyMenuItem; + + for (NSString* history in [historyContainer objectForKey:[fileURL absoluteString]]) + { + historyMenuItem = [[[NSMenuItem alloc] initWithTitle:([history length] > 64) ? [NSString stringWithFormat:@"%@…", [history substringToIndex:63]] : history + action:NULL + keyEquivalent:@""] autorelease]; + + [historyMenuItem setToolTip:([history length] > 256) ? [NSString stringWithFormat:@"%@…", [history substringToIndex:255]] : history]; + [returnArray addObject:historyMenuItem]; + } + + return returnArray; + } +#endif + + return [NSArray array]; +} + +/** + * Return the number of history items for the passed file URL + * + * @param fileURL The NSURL of the current active SPDatabaseDocument + * + */ +- (NSUInteger)numberOfHistoryItemsForFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + if ([historyContainer objectForKey:[fileURL absoluteString]]) { + return [[historyContainer objectForKey:[fileURL absoluteString]] count]; + } + else { + return 0; + } +#endif + + return 0; +} + +/** + * Return a mutable dictionary of all content filters for the passed file URL. + * If no content filters were found it returns an empty mutable dictionary. + * + * @param fileURL The NSURL of the current active SPDatabaseDocument + * + */ +- (NSMutableDictionary *)contentFilterForFileURL:(NSURL *)fileURL +{ +#ifndef SP_REFACTOR + if ([contentFilterContainer objectForKey:[fileURL absoluteString]]) { + return [contentFilterContainer objectForKey:[fileURL absoluteString]]; + } +#endif + + return [NSMutableDictionary dictionary]; +} + +- (NSArray *)queryFavoritesForFileURL:(NSURL *)fileURL andTabTrigger:(NSString *)tabTrigger includeGlobals:(BOOL)includeGlobals +{ + if (![tabTrigger length]) return [NSArray array]; + + NSMutableArray *result = [[NSMutableArray alloc] init]; + + for (id fav in [self favoritesForFileURL:fileURL]) + { + if ([fav objectForKey:@"tabtrigger"] && [[fav objectForKey:@"tabtrigger"] isEqualToString:tabTrigger]) { + [result addObject:fav]; + } + } + +#ifndef SP_REFACTOR + if (includeGlobals && [prefs objectForKey:SPQueryFavorites]) { + + for (id fav in [prefs objectForKey:SPQueryFavorites]) + { + if ([fav objectForKey:@"tabtrigger"] && [[fav objectForKey:@"tabtrigger"] isEqualToString:tabTrigger]) { + [result addObject:fav]; + break; + } + } + } +#endif + + return [result autorelease]; +} + +#pragma mark - +#pragma mark Completion list controller + +/** + * Return an array of all pre-defined SQL functions for completion. + */ +- (NSArray*)functionList +{ + return (completionFunctionList != nil && [completionFunctionList count]) ? completionFunctionList : [NSArray array]; +} + +/** + * Return an array of all pre-defined SQL keywords for completion. + */ +- (NSArray*)keywordList +{ + return (completionKeywordList != nil && [completionKeywordList count]) ? completionKeywordList : [NSArray array]; +} + +/** + * Return the parameter list as snippet of the passed SQL functions for completion. + * + * @param func The name of the function whose parameter list is asked for + */ +- (NSString*)argumentSnippetForFunction:(NSString*)func +{ + return (functionArgumentSnippets && [functionArgumentSnippets objectForKey:[func uppercaseString]]) ? [functionArgumentSnippets objectForKey:[func uppercaseString]] : @""; +} + +@end diff --git a/Source/SPQueryFavoriteManager.m b/Source/SPQueryFavoriteManager.m index 7fe5797f..90575e33 100644 --- a/Source/SPQueryFavoriteManager.m +++ b/Source/SPQueryFavoriteManager.m @@ -27,6 +27,7 @@ #import "ImageAndTextCell.h" #import "SPEncodingPopupAccessory.h" #import "SPQueryController.h" +#import "SPQueryDocumentsController.h" #import "SPConnectionController.h" #import "RegexKitLite.h" #import "SPTextView.h" diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index 7304f53e..aef067a6 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -35,6 +35,7 @@ #import "SPDataCellFormatter.h" #import "SPTableData.h" #import "SPQueryController.h" +#import "SPQueryDocumentsController.h" #import "SPTextAndLinkCell.h" #ifndef SP_REFACTOR #import "QLPreviewPanel.h" diff --git a/Source/SPTextView.m b/Source/SPTextView.m index 5edad98f..83a1eb22 100644 --- a/Source/SPTextView.m +++ b/Source/SPTextView.m @@ -27,6 +27,7 @@ #import "SPDatabaseDocument.h" #import "SPNarrowDownCompletion.h" #import "SPQueryController.h" +#import "SPQueryDocumentsController.h" #import "SPTooltip.h" #import "SPTablesList.h" #import "SPNavigatorController.h" |