From 207386000afff61ec1b661a17fa9747bf7fb4407 Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Sun, 8 Sep 2013 20:37:21 +0000 Subject: Allow opened SQL files to be saved back to the original file, addressing Issue #1805: - Add new menu item for "Save Query", making the existing item "Save Query As" as an alternate - Track the opened SQL file and used encoding to be able to easily save the same file again - Standardise encoding detection to use the UniversalDetector framework and use that for opened SQL files where the encoding menu is not used --- Source/SPAppController.h | 1 - Source/SPAppController.m | 100 +++++++--------------------------------- Source/SPCustomQuery.m | 5 +- Source/SPDataImport.m | 27 +++-------- Source/SPDatabaseDocument.h | 4 ++ Source/SPDatabaseDocument.m | 14 +++++- Source/SPFileManagerAdditions.h | 1 + Source/SPFileManagerAdditions.m | 27 +++++++++++ 8 files changed, 69 insertions(+), 110 deletions(-) (limited to 'Source') diff --git a/Source/SPAppController.h b/Source/SPAppController.h index e9173007..71930092 100644 --- a/Source/SPAppController.h +++ b/Source/SPAppController.h @@ -105,7 +105,6 @@ - (NSMutableDictionary *)anonymizePreferencesForFeedbackReport:(NSMutableDictionary *)preferences; // Others -- (NSString *)contentOfFile:(NSString *)aPath; - (NSArray *)bundleCategoriesForScope:(NSString *)scope; - (NSArray *)bundleItemsForScope:(NSString *)scope; - (NSArray *)bundleCommandsForTrigger:(NSString *)trigger; diff --git a/Source/SPAppController.m b/Source/SPAppController.m index 0efa243d..02d1773b 100644 --- a/Source/SPAppController.m +++ b/Source/SPAppController.m @@ -317,21 +317,24 @@ } // Attempt to open the file into a string. + NSStringEncoding sqlEncoding; NSString *sqlString = nil; // If the user came from an openPanel use the chosen encoding if (encodingPopUp) { - NSError *error = nil; - sqlString = [NSString stringWithContentsOfFile:filename encoding:[[encodingPopUp selectedItem] tag] error:&error]; - if(error != nil) { - NSAlert *errorAlert = [NSAlert alertWithError:error]; - [errorAlert runModal]; - return; - } - - // Otherwise, read while attempting to autodetect the encoding + sqlEncoding = [[encodingPopUp selectedItem] tag]; + + // Otherwise, attempt to autodetect the encoding } else { - sqlString = [self contentOfFile:filename]; + sqlEncoding = [[NSFileManager defaultManager] detectEncodingforFileAtPath:filename]; + } + + NSError *error = nil; + sqlString = [NSString stringWithContentsOfFile:filename encoding:sqlEncoding error:&error]; + if(error != nil) { + NSAlert *errorAlert = [NSAlert alertWithError:error]; + [errorAlert runModal]; + return; } // if encodingPopUp is defined the filename comes from an openPanel and @@ -346,10 +349,12 @@ } else { // Pass query to the Query editor of the current document - [[self frontDocument] doPerformLoadQueryService:[self contentOfFile:filename]]; + [[self frontDocument] doPerformLoadQueryService:sqlString]; } [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:[NSURL fileURLWithPath:filename]]; + [[self frontDocument] setSqlFileURL:[NSURL fileURLWithPath:filename]]; + [[self frontDocument] setSqlFileEncoding:sqlEncoding]; break; // open only the first SQL file @@ -2014,79 +2019,6 @@ return YES; } -/** - * Insert content of a plain text file for a given path. - * In addition it tries to figure out the file's text encoding heuristically. - */ -- (NSString *)contentOfFile:(NSString *)aPath -{ - NSError *err = nil; - NSStringEncoding enc; - NSString *content = nil; - - // Make usage of the UNIX command "file" to get an info - // about file type and encoding. - NSTask *task=[[NSTask alloc] init]; - NSPipe *pipe=[[NSPipe alloc] init]; - NSFileHandle *handle; - NSString *result; - [task setLaunchPath:@"/usr/bin/file"]; - [task setArguments:[NSArray arrayWithObjects:aPath, @"-Ib", nil]]; - [task setStandardOutput:pipe]; - handle=[pipe fileHandleForReading]; - [task launch]; - result=[[NSString alloc] initWithData:[handle readDataToEndOfFile] - encoding:NSASCIIStringEncoding]; - - [pipe release]; - [task release]; - - // UTF16/32 files are detected as application/octet-stream resp. audio/mpeg - if( [result hasPrefix:@"text/plain"] - || [[[aPath pathExtension] lowercaseString] isEqualToString:SPFileExtensionSQL] - || [[[aPath pathExtension] lowercaseString] isEqualToString:@"txt"] - || [result hasPrefix:@"audio/mpeg"] - || [result hasPrefix:@"application/octet-stream"] - ) - { - // if UTF16/32 cocoa will try to find the correct encoding - if([result hasPrefix:@"application/octet-stream"] || [result hasPrefix:@"audio/mpeg"] || [result rangeOfString:@"utf-16"].length) - enc = 0; - else if([result rangeOfString:@"utf-8"].length) - enc = NSUTF8StringEncoding; - else if([result rangeOfString:@"iso-8859-1"].length) - enc = NSISOLatin1StringEncoding; - else if([result rangeOfString:@"us-ascii"].length) - enc = NSASCIIStringEncoding; - else - enc = 0; - - if(enc == 0) // cocoa tries to detect the encoding - content = [NSString stringWithContentsOfFile:aPath usedEncoding:&enc error:&err]; - else - content = [NSString stringWithContentsOfFile:aPath encoding:enc error:&err]; - - if(content) - { - [result release]; - return content; - } - // If UNIX "file" failed try cocoa's encoding detection - content = [NSString stringWithContentsOfFile:aPath encoding:enc error:&err]; - if(content) - { - [result release]; - return content; - } - } - - [result release]; - - NSLog(@"%@ ‘%@’.", NSLocalizedString(@"Couldn't read the file content of", @"Couldn't read the file content of"), aPath); - - return @""; -} - - (NSArray *)bundleCategoriesForScope:(NSString*)scope { return [bundleCategories objectForKey:scope]; diff --git a/Source/SPCustomQuery.m b/Source/SPCustomQuery.m index 27fc11c6..d4c540bb 100644 --- a/Source/SPCustomQuery.m +++ b/Source/SPCustomQuery.m @@ -1615,10 +1615,7 @@ */ - (void)doPerformQueryService:(NSString *)query { - [textView shouldChangeTextInRange:NSMakeRange(0, [[textView string] length]) replacementString:query]; - [textView setString:query]; - [textView didChangeText]; - [textView scrollRangeToVisible:NSMakeRange([query length], 0)]; + [self doPerformLoadQueryService:query]; [self runAllQueries:self]; } diff --git a/Source/SPDataImport.m b/Source/SPDataImport.m index 34c535c0..cb9c1bb8 100644 --- a/Source/SPDataImport.m +++ b/Source/SPDataImport.m @@ -51,7 +51,6 @@ #import "SPThreadAdditions.h" #import -#import #define SP_FILE_READ_ERROR_STRING NSLocalizedString(@"File read error", @"File read error title (Import Dialog)") @@ -420,18 +419,12 @@ [tableDocumentInstance setQueryMode:SPImportExportQueryMode]; // Determine the file encoding. The first item in the encoding menu is "Autodetect"; if - // this is selected, attempt to detect the encoding of the file (using first 2.5MB). + // this is selected, attempt to detect the encoding of the file if (![importEncodingPopup indexOfSelectedItem]) { - SPFileHandle *detectorFileHandle = [SPFileHandle fileHandleForReadingAtPath:filename]; - if (detectorFileHandle) { - UniversalDetector *fileEncodingDetector = [[UniversalDetector alloc] init]; - [fileEncodingDetector analyzeData:[detectorFileHandle readDataOfLength:2500000]]; - sqlEncoding = [fileEncodingDetector encoding]; - [fileEncodingDetector release]; - if ([SPMySQLConnection mySQLCharsetForStringEncoding:sqlEncoding]) { - connectionEncodingToRestore = [mySQLConnection encoding]; - [mySQLConnection queryString:[NSString stringWithFormat:@"SET NAMES '%@'", [SPMySQLConnection mySQLCharsetForStringEncoding:sqlEncoding]]]; - } + sqlEncoding = [[NSFileManager defaultManager] detectEncodingforFileAtPath:filename]; + if ([SPMySQLConnection mySQLCharsetForStringEncoding:sqlEncoding]) { + connectionEncodingToRestore = [mySQLConnection encoding]; + [mySQLConnection queryString:[NSString stringWithFormat:@"SET NAMES '%@'", [SPMySQLConnection mySQLCharsetForStringEncoding:sqlEncoding]]]; } // Otherwise, get the encoding to use from the menu @@ -782,15 +775,9 @@ [tableDocumentInstance setQueryMode:SPImportExportQueryMode]; // Determine the file encoding. The first item in the encoding menu is "Autodetect"; if - // this is selected, attempt to detect the encoding of the file (using first 2.5MB). + // this is selected, attempt to detect the encoding of the file. if (![importEncodingPopup indexOfSelectedItem]) { - SPFileHandle *detectorFileHandle = [SPFileHandle fileHandleForReadingAtPath:filename]; - if (detectorFileHandle) { - UniversalDetector *fileEncodingDetector = [[UniversalDetector alloc] init]; - [fileEncodingDetector analyzeData:[detectorFileHandle readDataOfLength:2500000]]; - csvEncoding = [fileEncodingDetector encoding]; - [fileEncodingDetector release]; - } + csvEncoding = [[NSFileManager defaultManager] detectEncodingforFileAtPath:filename]; // Otherwise, get the encoding to use from the menu } else { diff --git a/Source/SPDatabaseDocument.h b/Source/SPDatabaseDocument.h index d0a58dbb..c2a9124f 100644 --- a/Source/SPDatabaseDocument.h +++ b/Source/SPDatabaseDocument.h @@ -249,6 +249,8 @@ NSString *queryEditorInitString; #ifndef SP_CODA /* ivars */ + NSURL *sqlFileURL; + NSStringEncoding sqlFileEncoding; NSURL *spfFileURL; NSDictionary *spfSession; NSMutableDictionary *spfPreferences; @@ -305,6 +307,8 @@ #endif #ifndef SP_CODA /* ivars */ +@property (readwrite, retain) NSURL *sqlFileURL; +@property (readwrite, assign) NSStringEncoding sqlFileEncoding; @property (readwrite, assign) SPWindowController *parentWindowController; @property (readwrite, assign) NSTabViewItem *parentTabViewItem; #endif diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m index 3a887662..85246443 100644 --- a/Source/SPDatabaseDocument.m +++ b/Source/SPDatabaseDocument.m @@ -138,6 +138,8 @@ static NSString *SPAlterDatabaseAction = @"SPAlterDatabase"; @implementation SPDatabaseDocument #ifndef SP_CODA /* ivars */ +@synthesize sqlFileURL; +@synthesize sqlFileEncoding; @synthesize parentWindowController; @synthesize parentTabViewItem; #endif @@ -215,6 +217,7 @@ static NSString *SPAlterDatabaseAction = @"SPAlterDatabase"; queryEditorInitString = nil; #ifndef SP_CODA + sqlFileURL = nil; spfFileURL = nil; spfSession = nil; spfPreferences = [[NSMutableDictionary alloc] init]; @@ -2791,7 +2794,15 @@ static NSString *SPAlterDatabaseAction = @"SPAlterDatabase"; [panel setCanSelectHiddenExtension:YES]; // Save Query… - if( sender != nil && [sender tag] == 1006 ) { + if( sender != nil && ([sender tag] == 1006 || [sender tag] == 1008)) { + + // If Save was invoked, check whether the file was previously opened, and if so save without the panel + if ([sender tag] == 1006 && [[[self sqlFileURL] path] length]) { + NSError *error = nil; + NSString *content = [NSString stringWithString:[[[customQueryInstance valueForKeyPath:@"textView"] textStorage] string]]; + [content writeToURL:sqlFileURL atomically:YES encoding:sqlFileEncoding error:&error]; + return; + } // Save the editor's content as SQL file [panel setAccessoryView:[SPEncodingPopupAccessory encodingAccessory:[prefs integerForKey:SPLastSQLFileEncoding] @@ -6273,6 +6284,7 @@ static NSString *SPAlterDatabaseAction = @"SPAlterDatabase"; #endif if (queryEditorInitString) [queryEditorInitString release]; #ifndef SP_CODA + if (sqlFileURL) [sqlFileURL release]; if (spfFileURL) [spfFileURL release]; if (spfPreferences) [spfPreferences release]; if (spfSession) [spfSession release]; diff --git a/Source/SPFileManagerAdditions.h b/Source/SPFileManagerAdditions.h index e7cb81b2..b4ae8f07 100644 --- a/Source/SPFileManagerAdditions.h +++ b/Source/SPFileManagerAdditions.h @@ -34,5 +34,6 @@ - (NSString *)applicationSupportDirectoryForSubDirectory:(NSString*)subDirectory error:(NSError **)errorOut; - (NSString *)applicationSupportDirectoryForSubDirectory:(NSString*)subDirectory createIfNotExists:(BOOL)create error:(NSError **)errorOut; +- (NSStringEncoding)detectEncodingforFileAtPath:(NSString *)aPath; + (NSString *)temporaryDirectory; @end diff --git a/Source/SPFileManagerAdditions.m b/Source/SPFileManagerAdditions.m index 2e63b120..7868c427 100644 --- a/Source/SPFileManagerAdditions.m +++ b/Source/SPFileManagerAdditions.m @@ -31,6 +31,8 @@ // More info at #import "SPFileManagerAdditions.h" +#import "SPFileHandle.h" +#import enum { @@ -153,6 +155,31 @@ static NSString *DirectoryLocationDomain = @"DirectoryLocationDomain"; return resolvedPath; } +/** + * Use the UniversalDetector library to attempt to detect the encoding at the file at + * the supplied URL. Only the first five megabytes are read if the file is larger. + * As with all encoding detection, this will return only best-guess result except + * for where encoding markers exist. + * Uses a SPFileHandle internally so it can detect the encoding within gzipped and + * bzipped files. + * Returns NSUTF8StringEncoding if the encoding cannot be detected. + */ +- (NSStringEncoding)detectEncodingforFileAtPath:(NSString *)aPath +{ + NSStringEncoding detectedEncoding; + SPFileHandle *detectorFileHandle = [SPFileHandle fileHandleForReadingAtPath:aPath]; + if (!detectorFileHandle) { + return NSUTF8StringEncoding; + } + + UniversalDetector *fileEncodingDetector = [[UniversalDetector alloc] init]; + [fileEncodingDetector analyzeData:[detectorFileHandle readDataOfLength:5000000]]; + detectedEncoding = [fileEncodingDetector encoding]; + [fileEncodingDetector release]; + + return detectedEncoding; +} + + (NSString *)temporaryDirectory { NSString *tempDir = NSTemporaryDirectory(); -- cgit v1.2.3