aboutsummaryrefslogtreecommitdiffstats
path: root/Source/SPExportFilenameUtilities.m
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2011-05-07 15:31:54 +0000
committerrowanbeentje <rowan@beent.je>2011-05-07 15:31:54 +0000
commit0c2a225a68ef1512e51ff3a48fef1fa13eacce60 (patch)
tree624b65144aac2f2a5d57172f8dd7625a5a8e236a /Source/SPExportFilenameUtilities.m
parent47a1c49e95403e6da6c9e0ae979144fd5c1dff08 (diff)
downloadsequelpro-0c2a225a68ef1512e51ff3a48fef1fa13eacce60.tar.gz
sequelpro-0c2a225a68ef1512e51ff3a48fef1fa13eacce60.tar.bz2
sequelpro-0c2a225a68ef1512e51ff3a48fef1fa13eacce60.zip
Implement a large number of minor fixes and improvements to export functionality, including an overhaul of filename NSTokenField functionality:
- Improve the NSTokenField used for export filenames: only tokenise reserved tokens, don't tokenise reserved words which are parts of other words, allow the comma to be used, update tokenisation during typing, and prevent whitespace triming. - Save the last selected export path, and make the path selection button open a dialog to the selected directory - Save the export filename and restore on future uses of the export dialog (only if the name contains placeholder tokens, so one-off export names aren't saved) - If the advanced options are collapsed, display a summary of the selected options next to the disclosure triangle - Display a small warning in the corner of the window if the export file cannot be imported into Sequel Pro, to warn those people attempting to back up their databases in XML - Clarify and improve the export warning dialog if files already exist or could not be created; make the simpler file-exists cases reflect OS-style dialogs, alter wording based on the number of files that failed and how they failed, and only show the "replace" or "skip" type buttons if it makes sense to do so. - Fix a mutation-during-enumeration error when skipping files - If "Cancel" is chosen in the export file creation replace/error dialog, redisplay the export sheet with the previous selection still active - Add support for year, month and day tokens in the filename token list - Don't allow blank custom filenames, before or after tokenisation, as this can cause problems - instead fall back to default filenames in those cases - Only append the extension if one hasn't been set - on all export formats, extending r3284 - If exporting to multiple files option is enabled but only one table is selected, supply that table name for filename table tokens - Update the progress bar to reflect update progress when exporting CSV data - Fix a bug causing exports to hang if the low-memory advanced option was set and content was selected to export and any empty tables were encountered - Save memory use and compression advanced export settings across sessions - Update localisable strings
Diffstat (limited to 'Source/SPExportFilenameUtilities.m')
-rw-r--r--Source/SPExportFilenameUtilities.m174
1 files changed, 127 insertions, 47 deletions
diff --git a/Source/SPExportFilenameUtilities.m b/Source/SPExportFilenameUtilities.m
index a485eb6b..a4330e36 100644
--- a/Source/SPExportFilenameUtilities.m
+++ b/Source/SPExportFilenameUtilities.m
@@ -26,6 +26,7 @@
#import "SPExportFilenameUtilities.h"
#import "SPTablesList.h"
#import "SPDatabaseViewController.h"
+#import "SPExportFileNameTokenObject.h"
@implementation SPExportController (SPExportFilenameUtilities)
@@ -41,9 +42,9 @@
// Get the current export file extension
NSString *extension = [self currentDefaultExportFileExtension];
- filename = [self expandCustomFilenameFormatFromString:[exportCustomFilenameTokenField stringValue] usingTableName:[[tablesListInstance tables] objectAtIndex:1]];
+ filename = [self expandCustomFilenameFormatUsingTableName:[[tablesListInstance tables] objectAtIndex:1]];
- if ([extension length] > 0) filename = [filename stringByAppendingPathExtension:extension];
+ if (![[filename pathExtension] length] && [extension length] > 0) filename = [filename stringByAppendingPathExtension:extension];
}
else {
filename = [self generateDefaultExportFilename];
@@ -57,7 +58,81 @@
*/
- (void)updateAvailableExportFilenameTokens
{
- [exportCustomFilenameTokensField setStringValue:((exportSource == SPQueryExport) || (exportType == SPDotExport)) ? NSLocalizedString(@"host,database,date,time", @"custom export filename tokens without table") : NSLocalizedString(@"host,database,table,date,time", @"default custom export filename tokens")];
+ [exportCustomFilenameTokensField setStringValue:((exportSource == SPQueryExport) || (exportType == SPDotExport)) ? NSLocalizedString(@"host,database,date,year,month,day,time", @"custom export filename tokens without table") : NSLocalizedString(@"host,database,table,date,year,month,day,time", @"default custom export filename tokens")];
+}
+
+/**
+ * Take a supplied string and return the token for it - a SPExportFileNameTokenObject if the token
+ * has been recognized, or the supplied NSString if unmatched.
+ */
+- (id)tokenObjectForString:(NSString *)stringToTokenize
+{
+ if ([[exportCustomFilenameTokensField objectValue] containsObject:stringToTokenize]) {
+ SPExportFileNameTokenObject *newToken = [[SPExportFileNameTokenObject alloc] init];
+ [newToken setTokenContent:stringToTokenize];
+ return [newToken autorelease];
+ }
+
+ return stringToTokenize;
+}
+
+/**
+ * Tokenize the filename field.
+ * This is called on a delay after text entry to update the tokens during text entry.
+ * There's no API to perform tokenizing, but the same result can be achieved by using the return key;
+ * however, this only works if the cursor is after text, not after a token.
+ */
+- (void)tokenizeCustomFilenameTokenField
+{
+ NSCharacterSet *nonAlphanumericSet = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
+ NSArray *validTokens = [exportCustomFilenameTokensField objectValue];
+
+ if ([exportCustomFilenameTokenField currentEditor] == nil) return;
+
+ NSRange selectedRange = [[exportCustomFilenameTokenField currentEditor] selectedRange];
+ if (selectedRange.location == NSNotFound) return;
+ if (selectedRange.length > 0) return;
+
+ // Retrieve the object value of the token field. This consists of plain text and recognised tokens interspersed.
+ NSArray *representedObjects = [exportCustomFilenameTokenField objectValue];
+
+ // Walk through the strings - not the tokens - and determine whether any need tokenizing
+ BOOL tokenizingRequired = NO;
+ for (id representedObject in representedObjects) {
+ if ([representedObject isKindOfClass:[SPExportFileNameTokenObject class]]) continue;
+ NSArray *tokenParts = [representedObject componentsSeparatedByCharactersInSet:nonAlphanumericSet];
+ for (NSString *tokenPart in tokenParts) {
+ if ([validTokens containsObject:tokenPart]) {
+ tokenizingRequired = YES;
+ break;
+ }
+ }
+ }
+
+ // If no tokenizing is required, don't process any further.
+ if (!tokenizingRequired) return;
+
+ // Detect where the cursor is currently located. If it's at the end of a token, also return -
+ // or the enter key would result in closing the sheet.
+ NSUInteger stringPosition = 0;
+ for (id representedObject in representedObjects) {
+ if ([representedObject isKindOfClass:[SPExportFileNameTokenObject class]]) {
+ stringPosition++;
+ } else {
+ stringPosition += [(NSString *)representedObject length];
+ }
+ if (selectedRange.location <= stringPosition) {
+ if ([representedObject isKindOfClass:[SPExportFileNameTokenObject class]]) return;
+ break;
+ }
+ }
+
+ // All conditions met - synthesize the return key to trigger tokenization.
+ NSEvent *tokenizingEvent = [NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[[exportCustomFilenameTokenField window] windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x24];
+ [[NSApplication sharedApplication] postEvent:tokenizingEvent atStart:NO];
+
+ // Update the filename preview
+ [self updateDisplayedExportFilename];
}
/**
@@ -129,57 +204,59 @@
/**
* Expands the custom filename format based on the selected tokens.
+ * Uses the current custom filename field as a data source.
*
- * @param format The filename format that is to be expanded.
* @param table A table name to be used within the expanded filename.
*
* @return The expanded filename.
*/
-- (NSString *)expandCustomFilenameFormatFromString:(NSString *)format usingTableName:(NSString *)table
+- (NSString *)expandCustomFilenameFormatUsingTableName:(NSString *)table
{
- NSMutableString *string = [NSMutableString stringWithString:format];
-
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+ NSMutableString *string = [NSMutableString string];
+ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
-
- [dateFormatter setDateStyle:NSDateFormatterShortStyle];
- [dateFormatter setTimeStyle:NSDateFormatterNoStyle];
-
- [string replaceOccurrencesOfString:NSLocalizedString(@"host", @"export filename host token")
- withString:[tableDocumentInstance host]
- options:NSLiteralSearch
- range:NSMakeRange(0, [string length])];
-
- [string replaceOccurrencesOfString:NSLocalizedString(@"database", @"export filename database token")
- withString:[tableDocumentInstance database]
- options:NSLiteralSearch
- range:NSMakeRange(0, [string length])];
-
- [string replaceOccurrencesOfString:NSLocalizedString(@"table", @"table")
- withString:(table) ? table : @""
- options:NSLiteralSearch
- range:NSMakeRange(0, [string length])];
-
- [string replaceOccurrencesOfString:NSLocalizedString(@"date", @"export filename date token")
- withString:[dateFormatter stringFromDate:[NSDate date]]
- options:NSLiteralSearch
- range:NSMakeRange(0, [string length])];
-
- [dateFormatter setDateStyle:NSDateFormatterNoStyle];
- [dateFormatter setTimeStyle:NSDateFormatterShortStyle];
-
- [string replaceOccurrencesOfString:NSLocalizedString(@"time", @"export filename time token")
- withString:[dateFormatter stringFromDate:[NSDate date]]
- options:NSLiteralSearch
- range:NSMakeRange(0, [string length])];
-
- // Strip comma separators
- [string replaceOccurrencesOfString:@","
- withString:@""
- options:NSLiteralSearch
- range:NSMakeRange(0, [string length])];
-
+
+ // Walk through the token field, appending token replacements or strings
+ NSArray *representedFilenameParts = [exportCustomFilenameTokenField objectValue];
+ for (id filenamePart in representedFilenameParts) {
+ if ([filenamePart isKindOfClass:[SPExportFileNameTokenObject class]]) {
+ NSString *tokenContent = [filenamePart tokenContent];
+
+ if ([tokenContent isEqualToString:NSLocalizedString(@"host", @"export filename host token")]) {
+ [string appendString:[tableDocumentInstance host]];
+
+ } else if ([tokenContent isEqualToString:NSLocalizedString(@"database", @"export filename database token")]) {
+ [string appendString:[tableDocumentInstance database]];
+
+ } else if ([tokenContent isEqualToString:NSLocalizedString(@"table", @"table")]) {
+ [string appendString:(table) ? table : @""];
+
+ } else if ([tokenContent isEqualToString:NSLocalizedString(@"date", @"export filename date token")]) {
+ [dateFormatter setDateStyle:NSDateFormatterShortStyle];
+ [dateFormatter setTimeStyle:NSDateFormatterNoStyle];
+ [string appendString:[dateFormatter stringFromDate:[NSDate date]]];
+
+ } else if ([tokenContent isEqualToString:NSLocalizedString(@"year", @"export filename date token")]) {
+ [string appendString:[[NSDate date] descriptionWithCalendarFormat:@"%Y" timeZone:nil locale:nil]];
+
+ } else if ([tokenContent isEqualToString:NSLocalizedString(@"month", @"export filename date token")]) {
+ [string appendString:[[NSDate date] descriptionWithCalendarFormat:@"%m" timeZone:nil locale:nil]];
+
+ } else if ([tokenContent isEqualToString:NSLocalizedString(@"day", @"export filename date token")]) {
+ [string appendString:[[NSDate date] descriptionWithCalendarFormat:@"%d" timeZone:nil locale:nil]];
+
+ } else if ([tokenContent isEqualToString:NSLocalizedString(@"time", @"export filename time token")]) {
+ [dateFormatter setDateStyle:NSDateFormatterNoStyle];
+ [dateFormatter setTimeStyle:NSDateFormatterShortStyle];
+ [string appendString:[dateFormatter stringFromDate:[NSDate date]]];
+
+ }
+ } else {
+ [string appendString:filenamePart];
+ }
+ }
+
// Replace colons with hyphens
[string replaceOccurrencesOfString:@":"
withString:@"-"
@@ -193,7 +270,10 @@
range:NSMakeRange(0, [string length])];
[dateFormatter release];
-
+
+ // Don't allow empty strings - if an empty string resulted, revert to the default string
+ if (![string length]) [string setString:[self generateDefaultExportFilename]];
+
return string;
}