aboutsummaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Source')
-rw-r--r--Source/SPAppController.h1
-rw-r--r--Source/SPAppController.m39
-rw-r--r--Source/SPDatabaseDocument.h7
-rw-r--r--Source/SPDatabaseDocument.m1264
-rw-r--r--Source/SPTableContent.m4
-rw-r--r--Source/SPTablesList.h5
-rw-r--r--Source/SPTablesList.m69
7 files changed, 804 insertions, 585 deletions
diff --git a/Source/SPAppController.h b/Source/SPAppController.h
index 3c18911e..35c9ee63 100644
--- a/Source/SPAppController.h
+++ b/Source/SPAppController.h
@@ -54,6 +54,7 @@
// Window management
- (IBAction)newWindow:(id)sender;
- (IBAction)newTab:(id)sender;
+- (IBAction)duplicateTab:(id)sender;
- (NSWindow *) frontDocumentWindow;
// IBAction methods
diff --git a/Source/SPAppController.m b/Source/SPAppController.m
index 534b3741..c5704f80 100644
--- a/Source/SPAppController.m
+++ b/Source/SPAppController.m
@@ -124,10 +124,14 @@
return NO;
}
- if([menuItem action] == @selector(newTab:))
+ if ([menuItem action] == @selector(newTab:))
{
return ([[self frontDocumentWindow] attachedSheet] == nil);
}
+ if ([menuItem action] == @selector(duplicateTab:))
+ {
+ return ([[self frontDocument] getConnection] != nil);
+ }
return YES;
}
@@ -319,7 +323,7 @@
[frontController addNewConnection:self];
}
- [[self frontDocument] initWithConnectionFile:filename];
+ [[self frontDocument] setStateFromConnectionFile:filename];
}
else if([[[filename pathExtension] lowercaseString] isEqualToString:[SPBundleFileExtension lowercaseString]]) {
@@ -418,7 +422,7 @@
[newWindowController addNewConnection:self];
[[self frontDocument] setIsSavedInBundle:isBundleFile];
- [[self frontDocument] initWithConnectionFile:fileName];
+ [[self frontDocument] setStateFromConnectionFile:fileName];
}
} else {
@@ -657,6 +661,35 @@
}
/**
+ * Duplicate the current connection tab
+ */
+- (IBAction)duplicateTab:(id)sender
+{
+ SPDatabaseDocument *theFrontDocument = [self frontDocument];
+ if (!theFrontDocument) return [self newTab:sender];
+
+ // Add a new tab to the window
+ if ([[self frontDocumentWindow] isMiniaturized]) [[self frontDocumentWindow] deminiaturize:self];
+ [[[self frontDocumentWindow] windowController] addNewConnection:self];
+
+ // Get the state of the previously-frontmost document
+ NSDictionary *allStateDetails = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:YES], @"connection",
+ [NSNumber numberWithBool:YES], @"history",
+ [NSNumber numberWithBool:YES], @"session",
+ [NSNumber numberWithBool:YES], @"query",
+ [NSNumber numberWithBool:YES], @"password",
+ nil];
+ NSMutableDictionary *theFrontState = [NSMutableDictionary dictionaryWithDictionary:[theFrontDocument stateIncludingDetails:allStateDetails]];
+
+ // Ensure it's set to autoconnect
+ [theFrontState setObject:[NSNumber numberWithBool:YES] forKey:@"auto_connect"];
+
+ // Set the connection on the new tab
+ [[self frontDocument] setState:theFrontState];
+}
+
+/**
* Retrieve the frontmost document window; returns nil if not found.
*/
- (NSWindow *) frontDocumentWindow
diff --git a/Source/SPDatabaseDocument.h b/Source/SPDatabaseDocument.h
index 9563fd58..e4d12346 100644
--- a/Source/SPDatabaseDocument.h
+++ b/Source/SPDatabaseDocument.h
@@ -215,7 +215,6 @@
- (BOOL)couldCommitCurrentViewActions;
- (void)initQueryEditorWithString:(NSString *)query;
-- (void)initWithConnectionFile:(NSString *)path;
// Connection callback and methods
- (void)setConnection:(MCPConnection *)theConnection;
@@ -322,6 +321,7 @@
// Menu methods
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem;
+- (IBAction)openDatabaseInNewTab:(id)sender;
- (IBAction)saveConnectionSheet:(id)sender;
- (IBAction)import:(id)sender;
- (IBAction)importFromClipboard:(id)sender;
@@ -357,4 +357,9 @@
- (void)handleSchemeCommand:(NSDictionary*)commandDict;
- (NSDictionary*)shellVariables;
+// State saving and setting
+- (NSDictionary *) stateIncludingDetails:(NSDictionary *)detailsToReturn;
+- (BOOL)setState:(NSDictionary *)stateDetails;
+- (void)setStateFromConnectionFile:(NSString *)path;
+
@end
diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m
index 4e26a8c4..4626f031 100644
--- a/Source/SPDatabaseDocument.m
+++ b/Source/SPDatabaseDocument.m
@@ -246,403 +246,6 @@
}
/**
- * Initialise the document with the connection file at the supplied path.
- */
-- (void)initWithConnectionFile:(NSString *)path
-{
- NSError *readError = nil;
- NSString *convError = nil;
- NSPropertyListFormat format;
-
- NSString *encryptpw = nil;
- NSDictionary *data = nil;
- NSDictionary *connection = nil;
- NSDictionary *spf = nil;
-
- NSInteger connectionType = -1;
-
- [self updateWindowTitle:self];
-
- // Clean fields
- [connectionController setName:@""];
- [connectionController setUser:@""];
- [connectionController setHost:@""];
- [connectionController setPort:@""];
- [connectionController setSocket:@""];
- [connectionController setUseSSL:NSOffState];
- [connectionController setSslKeyFileLocationEnabled:NSOffState];
- [connectionController setSslKeyFileLocation:nil];
- [connectionController setSslCertificateFileLocationEnabled:NSOffState];
- [connectionController setSslCertificateFileLocation:nil];
- [connectionController setSslCACertFileLocationEnabled:NSOffState];
- [connectionController setSslCACertFileLocation:nil];
- [connectionController setSshHost:@""];
- [connectionController setSshUser:@""];
- [connectionController setSshKeyLocationEnabled:NSOffState];
- [connectionController setSshKeyLocation:nil];
- [connectionController setSshPort:@""];
- [connectionController setDatabase:@""];
- [connectionController setPassword:nil];
- [connectionController setSshPassword:nil];
-
- // Deselect all favorites
- [[connectionController valueForKeyPath:@"favoritesTable"] deselectAll:connectionController];
- // Suppress the possibility to choose an other connection from the favorites
- // if a connection should initialized by SPF file. Otherwise it could happen
- // that the SPF file runs out of sync.
- [[connectionController valueForKeyPath:@"favoritesTable"] setEnabled:NO];
-
-
- NSData *pData = [NSData dataWithContentsOfFile:path options:NSUncachedRead error:&readError];
-
- spf = [[NSPropertyListSerialization propertyListFromData:pData
- mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain];
-
- if(!spf || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
- NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading connection data file", @"error while reading connection data file")]
- defaultButton:NSLocalizedString(@"OK", @"OK button")
- alternateButton:nil
- otherButton:nil
- informativeTextWithFormat:NSLocalizedString(@"Connection data file couldn't be read.", @"error while reading connection data file")];
-
- [alert setAlertStyle:NSCriticalAlertStyle];
- [alert runModal];
- if (spf) [spf release];
- [self closeAndDisconnect];
- return;
- }
-
- // For dispatching later
- if(![[spf objectForKey:@"format"] isEqualToString:@"connection"]) {
- NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Warning", @"warning")]
- defaultButton:NSLocalizedString(@"OK", @"OK button")
- alternateButton:nil
- otherButton:nil
- informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"The chosen file “%@” contains ‘%@’ data.", @"message while reading a spf file which matches non-supported formats."), path, [spf objectForKey:@"format"]]];
-
- [alert setAlertStyle:NSWarningAlertStyle];
- [spf release];
- [self closeAndDisconnect];
- [alert runModal];
- return;
- }
-
- if(![spf objectForKey:@"data"]) {
- NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading connection data file", @"error while reading connection data file")]
- defaultButton:NSLocalizedString(@"OK", @"OK button")
- alternateButton:nil
- otherButton:nil
- informativeTextWithFormat:NSLocalizedString(@"No data found.", @"no data found")];
-
- [alert setAlertStyle:NSCriticalAlertStyle];
- [alert runModal];
- [spf release];
- [self closeAndDisconnect];
- return;
- }
-
- // Ask for a password if SPF file passwords were encrypted as sheet
- if([spf objectForKey:@"encrypted"] && [[spf valueForKey:@"encrypted"] boolValue]) {
-
- if([self isSaveInBundle] && [[[NSApp delegate] spfSessionDocData] objectForKey:@"e_string"]) {
- encryptpw = [[[NSApp delegate] spfSessionDocData] objectForKey:@"e_string"];
- } else {
- [inputTextWindowHeader setStringValue:NSLocalizedString(@"Connection file is encrypted", @"Connection file is encrypted")];
- [inputTextWindowMessage setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Please enter the password for ‘%@’:", @"Please enter the password"), ([self isSaveInBundle]) ? [[[[NSApp delegate] sessionURL] absoluteString] lastPathComponent] : [path lastPathComponent]]];
- [inputTextWindowSecureTextField setStringValue:@""];
- [inputTextWindowSecureTextField selectText:nil];
-
- [NSApp beginSheet:inputTextWindow modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
-
- // wait for encryption password
- NSModalSession session = [NSApp beginModalSessionForWindow:inputTextWindow];
- for (;;) {
-
- // Execute code on DefaultRunLoop
- [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
- beforeDate:[NSDate distantFuture]];
-
- // Break the run loop if editSheet was closed
- if ([NSApp runModalSession:session] != NSRunContinuesResponse
- || ![inputTextWindow isVisible])
- break;
-
- // Execute code on DefaultRunLoop
- [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
- beforeDate:[NSDate distantFuture]];
-
- }
- [NSApp endModalSession:session];
- [inputTextWindow orderOut:nil];
- [NSApp endSheet:inputTextWindow];
-
- if(passwordSheetReturnCode) {
- encryptpw = [inputTextWindowSecureTextField stringValue];
- if([self isSaveInBundle]) {
- NSMutableDictionary *spfSessionData = [NSMutableDictionary dictionary];
- [spfSessionData addEntriesFromDictionary:[[NSApp delegate] spfSessionDocData]];
- [spfSessionData setObject:encryptpw forKey:@"e_string"];
- [[NSApp delegate] setSpfSessionDocData:spfSessionData];
- }
- } else {
- [self closeAndDisconnect];
- [spf release];
- return;
- }
- }
- }
-
- if([[spf objectForKey:@"data"] isKindOfClass:[NSDictionary class]])
- data = [NSDictionary dictionaryWithDictionary:[spf objectForKey:@"data"]];
- else if([[spf objectForKey:@"data"] isKindOfClass:[NSData class]]) {
- NSData *decryptdata = nil;
- decryptdata = [[[NSMutableData alloc] initWithData:[(NSData *)[spf objectForKey:@"data"] dataDecryptedWithPassword:encryptpw]] autorelease];
- if(decryptdata != nil && [decryptdata length]) {
- NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:decryptdata] autorelease];
- data = (NSDictionary *)[unarchiver decodeObjectForKey:@"data"];
- [unarchiver finishDecoding];
- }
- if(data == nil) {
- NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading connection data file", @"error while reading connection data file")]
- defaultButton:NSLocalizedString(@"OK", @"OK button")
- alternateButton:nil
- otherButton:nil
- informativeTextWithFormat:NSLocalizedString(@"Wrong data format or password.", @"wrong data format or password")];
-
- [alert setAlertStyle:NSCriticalAlertStyle];
- [alert runModal];
- [self closeAndDisconnect];
- [spf release];
- return;
- }
- }
-
- if(data == nil) {
- NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading connection data file", @"error while reading connection data file")]
- defaultButton:NSLocalizedString(@"OK", @"OK button")
- alternateButton:nil
- otherButton:nil
- informativeTextWithFormat:NSLocalizedString(@"Wrong data format.", @"wrong data format")];
-
- [alert setAlertStyle:NSCriticalAlertStyle];
- [alert runModal];
- [self closeAndDisconnect];
- [spf release];
- return;
- }
-
-
- if(![data objectForKey:@"connection"]) {
- NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading connection data file", @"error while reading connection data file")]
- defaultButton:NSLocalizedString(@"OK", @"OK button")
- alternateButton:nil
- otherButton:nil
- informativeTextWithFormat:NSLocalizedString(@"No connection data found.", @"no connection data found")];
-
- [alert setAlertStyle:NSCriticalAlertStyle];
- [alert runModal];
- [self closeAndDisconnect];
- [spf release];
- return;
- }
-
- [spfDocData setObject:[NSNumber numberWithBool:NO] forKey:@"encrypted"];
- if(encryptpw != nil) {
- [spfDocData setObject:[NSNumber numberWithBool:YES] forKey:@"encrypted"];
- [spfDocData setObject:encryptpw forKey:@"e_string"];
- }
- encryptpw = nil;
-
- connection = [NSDictionary dictionaryWithDictionary:[data objectForKey:@"connection"]];
-
- if([connection objectForKey:@"type"]) {
- if([[connection objectForKey:@"type"] isEqualToString:@"SPTCPIPConnection"])
- connectionType = SPTCPIPConnection;
- else if([[connection objectForKey:@"type"] isEqualToString:@"SPSocketConnection"])
- connectionType = SPSocketConnection;
- else if([[connection objectForKey:@"type"] isEqualToString:@"SPSSHTunnelConnection"])
- connectionType = SPSSHTunnelConnection;
- else
- connectionType = SPTCPIPConnection;
-
- [connectionController setType:connectionType];
- [connectionController resizeTabViewToConnectionType:connectionType animating:NO];
- }
-
- if([connection objectForKey:@"name"])
- [connectionController setName:[connection objectForKey:@"name"]];
- if([connection objectForKey:@"user"])
- [connectionController setUser:[connection objectForKey:@"user"]];
- if([connection objectForKey:@"host"])
- [connectionController setHost:[connection objectForKey:@"host"]];
- if([connection objectForKey:@"port"])
- [connectionController setPort:[NSString stringWithFormat:@"%ld", (long)[[connection objectForKey:@"port"] integerValue]]];
-
- if([connection objectForKey:@"useSSL"])
- [connectionController setUseSSL:[[connection objectForKey:@"useSSL"] intValue]];
- if([connection objectForKey:@"sslKeyFileLocationEnabled"])
- [connectionController setSslKeyFileLocationEnabled:[[connection objectForKey:@"sslKeyFileLocationEnabled"] intValue]];
- if([connection objectForKey:@"sslKeyFileLocation"])
- [connectionController setSslKeyFileLocation:[connection objectForKey:@"sslKeyFileLocation"]];
- if([connection objectForKey:@"sslCertificateFileLocationEnabled"])
- [connectionController setSslCertificateFileLocationEnabled:[[connection objectForKey:@"sslCertificateFileLocationEnabled"] intValue]];
- if([connection objectForKey:@"sslCertificateFileLocation"])
- [connectionController setSslCertificateFileLocation:[connection objectForKey:@"sslCertificateFileLocation"]];
- if([connection objectForKey:@"sslCACertFileLocationEnabled"])
- [connectionController setSslCACertFileLocationEnabled:[[connection objectForKey:@"sslCACertFileLocationEnabled"] intValue]];
- if([connection objectForKey:@"sslCACertFileLocation"])
- [connectionController setSslCACertFileLocation:[connection objectForKey:@"sslCACertFileLocation"]];
-
- if([connection objectForKey:@"kcid"] && [(NSString *)[connection objectForKey:@"kcid"] length])
- [self setKeychainID:[connection objectForKey:@"kcid"]];
-
- // Set password - if not in SPF file try to get it via the KeyChain
- if([connection objectForKey:@"password"])
- [connectionController setPassword:[connection objectForKey:@"password"]];
- else {
- NSString *pw = [self keychainPasswordForConnection:nil];
- if(pw)
- [connectionController setPassword:pw];
- }
-
- if(connectionType == SPSocketConnection && [connection objectForKey:@"socket"])
- [connectionController setSocket:[connection objectForKey:@"socket"]];
-
- if(connectionType == SPSSHTunnelConnection) {
- if([connection objectForKey:@"ssh_host"])
- [connectionController setSshHost:[connection objectForKey:@"ssh_host"]];
- if([connection objectForKey:@"ssh_user"])
- [connectionController setSshUser:[connection objectForKey:@"ssh_user"]];
- if([connection objectForKey:@"ssh_keyLocationEnabled"])
- [connectionController setSshKeyLocationEnabled:[[connection objectForKey:@"ssh_keyLocationEnabled"] intValue]];
- if([connection objectForKey:@"ssh_keyLocation"])
- [connectionController setSshKeyLocation:[connection objectForKey:@"ssh_keyLocation"]];
- if([connection objectForKey:@"ssh_port"])
- [connectionController setSshPort:[NSString stringWithFormat:@"%ld", (long)[[connection objectForKey:@"ssh_port"] integerValue]]];
-
- // Set ssh password - if not in SPF file try to get it via the KeyChain
- if([connection objectForKey:@"ssh_password"])
- [connectionController setSshPassword:[connection objectForKey:@"ssh_password"]];
- else {
- NSString *sshpw = [self keychainPasswordForSSHConnection:nil];
- if(sshpw)
- [connectionController setSshPassword:sshpw];
- }
-
- }
-
- if([connection objectForKey:@"database"])
- [connectionController setDatabase:[connection objectForKey:@"database"]];
-
- if([data objectForKey:@"session"]) {
- spfSession = [[NSDictionary dictionaryWithDictionary:[data objectForKey:@"session"]] retain];
- [spfDocData setObject:[NSNumber numberWithBool:YES] forKey:@"include_session"];
- }
-
- if(![self isSaveInBundle]) {
- [self setFileURL:[NSURL fileURLWithPath:path]];
- [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:[NSURL fileURLWithPath:path]];
- }
-
- if([spf objectForKey:SPQueryFavorites])
- [spfPreferences setObject:[spf objectForKey:SPQueryFavorites] forKey:SPQueryFavorites];
- if([spf objectForKey:SPQueryHistory])
- [spfPreferences setObject:[spf objectForKey:SPQueryHistory] forKey:SPQueryHistory];
- if([spf objectForKey:SPContentFilters])
- [spfPreferences setObject:[spf objectForKey:SPContentFilters] forKey:SPContentFilters];
-
- [spfDocData setObject:[NSNumber numberWithBool:([connection objectForKey:@"password"]) ? YES : NO] forKey:@"save_password"];
-
- [spfDocData setObject:[NSNumber numberWithBool:NO] forKey:@"auto_connect"];
-
- [connectionController updateSSLInterface:self];
-
- if([spf objectForKey:@"auto_connect"] && [[spf valueForKey:@"auto_connect"] boolValue]) {
- [spfDocData setObject:[NSNumber numberWithBool:YES] forKey:@"auto_connect"];
- [connectionController initiateConnection:self];
- }
- [spf release];
-}
-
-/**
- * Restore session from SPF file if given
- */
-- (void)restoreSession
-{
- NSAutoreleasePool *taskPool = [[NSAutoreleasePool alloc] init];
-
- // Check and set the table
- NSArray *tables = [tablesListInstance tables];
-
- BOOL isSelectedTableDefined = YES;
-
- if([tables indexOfObject:[spfSession objectForKey:@"table"]] == NSNotFound) {
- isSelectedTableDefined = NO;
- }
-
- // Restore toolbar setting
- if([spfSession objectForKey:@"isToolbarVisible"])
- [mainToolbar setVisible:[[spfSession objectForKey:@"isToolbarVisible"] boolValue]];
-
- // Reset database view encoding if differs from default
- if([spfSession objectForKey:@"connectionEncoding"] && ![[mySQLConnection encoding] isEqualToString:[spfSession objectForKey:@"connectionEncoding"]])
- [self setConnectionEncoding:[spfSession objectForKey:@"connectionEncoding"] reloadingViews:YES];
-
- if(isSelectedTableDefined) {
- // Set table content details for restore
- if([spfSession objectForKey:@"contentSortCol"])
- [tableContentInstance setSortColumnNameToRestore:[spfSession objectForKey:@"contentSortCol"] isAscending:[[spfSession objectForKey:@"contentSortCol"] boolValue]];
- if([spfSession objectForKey:@"contentPageNumber"])
- [tableContentInstance setPageToRestore:[[spfSession objectForKey:@"pageNumber"] integerValue]];
- if([spfSession objectForKey:@"contentViewport"])
- [tableContentInstance setViewportToRestore:NSRectFromString([spfSession objectForKey:@"contentViewport"])];
- if([spfSession objectForKey:@"contentFilter"])
- [tableContentInstance setFiltersToRestore:[spfSession objectForKey:@"contentFilter"]];
-
- // Select table
- [tablesListInstance selectTableAtIndex:[NSNumber numberWithInteger:[tables indexOfObject:[spfSession objectForKey:@"table"]]]];
-
- // TODO up to now it doesn't work
- if([spfSession objectForKey:@"contentSelectedIndexSet"]) {
- NSMutableIndexSet *anIndexSet = [NSMutableIndexSet indexSet];
- NSArray *items = [spfSession objectForKey:@"contentSelectedIndexSet"];
- NSUInteger i;
- for(i=0; i<[items count]; i++)
- [anIndexSet addIndex:(NSUInteger)NSArrayObjectAtIndex(items, i)];
-
- [tableContentInstance setSelectedRowIndexesToRestore:anIndexSet];
- }
-
- [[tablesListInstance valueForKeyPath:@"tablesListView"] scrollRowToVisible:[tables indexOfObject:[spfSession objectForKey:@"selectedTable"]]];
-
- }
-
- // Select view
- if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_STRUCTURE"])
- [self viewStructure:self];
- else if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_CONTENT"])
- [self viewContent:self];
- else if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_CUSTOMQUERY"])
- [self viewQuery:self];
- else if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_STATUS"])
- [self viewStatus:self];
- else if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_RELATIONS"])
- [self viewRelations:self];
- else if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_TRIGGERS"])
- [self viewTriggers:self];
-
- [self updateWindowTitle:self];
-
- // dealloc spfSession data
- [spfSession release];
- spfSession = nil;
-
- // End the task
- [self endTask];
- [taskPool drain];
-}
-
-/**
* Set the return code for entering the encryption passowrd sheet
*/
- (IBAction)closePasswordSheet:(id)sender
@@ -2556,7 +2159,7 @@
{
[[NSApp delegate] newWindow:self];
SPDatabaseDocument *newTableDocument = [[NSApp delegate] frontDocument];
- [newTableDocument initWithConnectionFile:[[self fileURL] path]];
+ [newTableDocument setStateFromConnectionFile:[[self fileURL] path]];
}
/**
@@ -3297,182 +2900,72 @@
}
- NSString *aString;
-
- NSMutableDictionary *spfdata = [NSMutableDictionary dictionary];
- NSMutableDictionary *connection = [NSMutableDictionary dictionary];
- NSMutableDictionary *session = nil;
- NSMutableDictionary *data = [NSMutableDictionary dictionary];
+ // Set up the dictionary to save to file, together with a data store
+ NSMutableDictionary *spfStructure = [NSMutableDictionary dictionary];
+ NSMutableDictionary *spfData = [NSMutableDictionary dictionary];
- NSIndexSet *contentSelectedIndexSet = [tableContentInstance selectedRowIndexes];
-
- [spfdata setObject:[NSNumber numberWithInteger:1] forKey:@"version"];
- [spfdata setObject:@"connection" forKey:@"format"];
- [spfdata setObject:@"mysql" forKey:@"rdbms_type"];
+ // Add basic details
+ [spfStructure setObject:[NSNumber numberWithInteger:1] forKey:@"version"];
+ [spfStructure setObject:@"connection" forKey:@"format"];
+ [spfStructure setObject:@"mysql" forKey:@"rdbms_type"];
if([self mySQLVersion])
- [spfdata setObject:[self mySQLVersion] forKey:@"rdbms_version"];
-
- // Store the preferences - take them from the current document URL to catch renaming
- [spfdata setObject:[[SPQueryController sharedQueryController] favoritesForFileURL:[self fileURL]] forKey:SPQueryFavorites];
- [spfdata setObject:[[SPQueryController sharedQueryController] historyForFileURL:[self fileURL]] forKey:SPQueryHistory];
- [spfdata setObject:[[SPQueryController sharedQueryController] contentFilterForFileURL:[self fileURL]] forKey:SPContentFilters];
-
- [spfdata setObject:[spfDocData_temp objectForKey:@"encrypted"] forKey:@"encrypted"];
-
- [spfdata setObject:[spfDocData_temp objectForKey:@"auto_connect"] forKey:@"auto_connect"];
-
- if([[self keyChainID] length])
- [connection setObject:[self keyChainID] forKey:@"kcid"];
- [connection setObject:[self name] forKey:@"name"];
- [connection setObject:[self host] forKey:@"host"];
- [connection setObject:[self user] forKey:@"user"];
-
- [connection setObject:[NSNumber numberWithInt:[connectionController useSSL]] forKey:@"useSSL"];
- [connection setObject:[NSNumber numberWithInt:[connectionController sslKeyFileLocationEnabled]] forKey:@"sslKeyFileLocationEnabled"];
- [connection setObject:[connectionController sslKeyFileLocation] forKey:@"sslKeyFileLocation"];
- [connection setObject:[NSNumber numberWithInt:[connectionController sslCertificateFileLocationEnabled]] forKey:@"sslCertificateFileLocationEnabled"];
- [connection setObject:[connectionController sslCertificateFileLocation] forKey:@"sslCertificateFileLocation"];
- [connection setObject:[NSNumber numberWithInt:[connectionController sslCACertFileLocationEnabled]] forKey:@"sslCACertFileLocationEnabled"];
- [connection setObject:[connectionController sslCACertFileLocation] forKey:@"sslCACertFileLocation"];
+ [spfStructure setObject:[self mySQLVersion] forKey:@"rdbms_version"];
- switch([connectionController type]) {
- case SPTCPIPConnection:
- aString = @"SPTCPIPConnection";
- break;
- case SPSocketConnection:
- aString = @"SPSocketConnection";
- if ([connectionController socket] && [[connectionController socket] length]) [connection setObject:[connectionController socket] forKey:@"socket"];
- break;
- case SPSSHTunnelConnection:
- aString = @"SPSSHTunnelConnection";
- [connection setObject:[connectionController sshHost] forKey:@"ssh_host"];
- [connection setObject:[connectionController sshUser] forKey:@"ssh_user"];
- [connection setObject:[NSNumber numberWithInt:[connectionController sshKeyLocationEnabled]] forKey:@"ssh_keyLocationEnabled"];
- [connection setObject:[connectionController sshKeyLocation] forKey:@"ssh_keyLocation"];
- if([connectionController sshPort] && [[connectionController sshPort] length])
- [connection setObject:[NSNumber numberWithInteger:[[connectionController sshPort] integerValue]] forKey:@"ssh_port"];
- break;
- default:
- aString = @"SPTCPIPConnection";
- }
- [connection setObject:aString forKey:@"type"];
+ // Add auto-connect if appropriate
+ [spfStructure setObject:[spfDocData_temp objectForKey:@"auto_connect"] forKey:@"auto_connect"];
- if([[spfDocData_temp objectForKey:@"save_password"] boolValue]) {
-
- NSString *pw = [self keychainPasswordForConnection:nil];
- if(![pw length]) pw = [connectionController password];
- if (pw)
- [connection setObject:pw forKey:@"password"];
- else
- [connection setObject:@"" forKey:@"password"];
-
- if([connectionController type] == SPSSHTunnelConnection) {
- NSString *sshpw = [self keychainPasswordForSSHConnection:nil];
- if(![sshpw length]) sshpw = [connectionController sshPassword];
- if (sshpw)
- [connection setObject:sshpw forKey:@"ssh_password"];
- else
- [connection setObject:@"" forKey:@"ssh_password"];
- }
- }
-
- if([connectionController port] && [[connectionController port] length])
- [connection setObject:[NSNumber numberWithInteger:[[connectionController port] integerValue]] forKey:@"port"];
-
- if([[self database] length])
- [connection setObject:[self database] forKey:@"database"];
+ // Set up the document details to store
+ NSMutableDictionary *stateDetailsToSave = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:YES], @"connection",
+ [NSNumber numberWithBool:YES], @"history",
+ nil];
// Include session data like selected table, view etc. ?
- if([[spfDocData_temp objectForKey:@"include_session"] boolValue]) {
-
- session = [NSMutableDictionary dictionary];
-
- if([[self table] length])
- [session setObject:[self table] forKey:@"table"];
- if([tableContentInstance sortColumnName])
- [session setObject:[tableContentInstance sortColumnName] forKey:@"contentSortCol"];
-
- switch([spHistoryControllerInstance currentlySelectedView]){
- case SPTableViewStructure:
- aString = @"SP_VIEW_STRUCTURE";
- break;
- case SPTableViewContent:
- aString = @"SP_VIEW_CONTENT";
- break;
- case SPTableViewCustomQuery:
- aString = @"SP_VIEW_CUSTOMQUERY";
- break;
- case SPTableViewStatus:
- aString = @"SP_VIEW_STATUS";
- break;
- case SPTableViewRelations:
- aString = @"SP_VIEW_RELATIONS";
- break;
- case SPTableViewTriggers:
- aString = @"SP_VIEW_TRIGGERS";
- break;
- default:
- aString = @"SP_VIEW_STRUCTURE";
- }
- [session setObject:aString forKey:@"view"];
-
- [session setObject:[NSNumber numberWithBool:[[parentWindow toolbar] isVisible]] forKey:@"isToolbarVisible"];
- [session setObject:[mySQLConnection encoding] forKey:@"connectionEncoding"];
-
- [session setObject:[NSNumber numberWithBool:[tableContentInstance sortColumnIsAscending]] forKey:@"contentSortColIsAsc"];
- [session setObject:[NSNumber numberWithInteger:[tableContentInstance pageNumber]] forKey:@"contentPageNumber"];
- [session setObject:[NSNumber numberWithFloat:[tableContentInstance tablesListWidth]] forKey:@"windowVerticalDividerPosition"];
- [session setObject:NSStringFromRect([tableContentInstance viewport]) forKey:@"contentViewport"];
- if([tableContentInstance filterSettings])
- [session setObject:[tableContentInstance filterSettings] forKey:@"contentFilter"];
-
- if (contentSelectedIndexSet && [contentSelectedIndexSet count]) {
- NSMutableArray *indices = [NSMutableArray array];
- NSUInteger indexBuffer[[contentSelectedIndexSet count]];
- NSUInteger limit = [contentSelectedIndexSet getIndexes:indexBuffer maxCount:[contentSelectedIndexSet count] inIndexRange:NULL];
- NSUInteger idx;
- for (idx = 0; idx < limit; idx++) {
- [indices addObject:[NSNumber numberWithInteger:indexBuffer[idx]]];
- }
- [session setObject:indices forKey:@"contentSelectedIndexSet"];
- }
- }
-
- if([[spfDocData_temp objectForKey:@"save_editor_content"] boolValue]) {
- if(session == nil)
- session = [NSMutableDictionary dictionary];
-
- if([[[[customQueryInstance valueForKeyPath:@"textView"] textStorage] string] length] > 50000)
- [session setObject:[[[[[customQueryInstance valueForKeyPath:@"textView"] textStorage] string] dataUsingEncoding:NSUTF8StringEncoding] compress] forKey:@"queries"];
- else
- [session setObject:[[[customQueryInstance valueForKeyPath:@"textView"] textStorage] string] forKey:@"queries"];
- }
-
- [data setObject:connection forKey:@"connection"];
- if(session != nil)
- [data setObject:session forKey:@"session"];
-
- if(![[spfDocData_temp objectForKey:@"encrypted"] boolValue]) {
- [spfdata setObject:data forKey:@"data"];
+ if ([[spfDocData_temp objectForKey:@"include_session"] boolValue])
+ [stateDetailsToSave setObject:[NSNumber numberWithBool:YES] forKey:@"session"];
+
+ // Include the query editor contents if asked to
+ if ([[spfDocData_temp objectForKey:@"save_editor_content"] boolValue]) {
+ [stateDetailsToSave setObject:[NSNumber numberWithBool:YES] forKey:@"query"];
+ [stateDetailsToSave setObject:[NSNumber numberWithBool:YES] forKey:@"enablecompression"];
+ }
+
+ // Add passwords if asked to
+ if ([[spfDocData_temp objectForKey:@"save_password"] boolValue])
+ [stateDetailsToSave setObject:[NSNumber numberWithBool:YES] forKey:@"password"];
+
+ // Retrieve details and add to the appropriate dictionaries
+ NSMutableDictionary *stateDetails = [NSMutableDictionary dictionaryWithDictionary:[self stateIncludingDetails:stateDetailsToSave]];
+ [spfStructure setObject:[stateDetails objectForKey:SPQueryFavorites] forKey:SPQueryFavorites];
+ [spfStructure setObject:[stateDetails objectForKey:SPQueryHistory] forKey:SPQueryHistory];
+ [spfStructure setObject:[stateDetails objectForKey:SPContentFilters] forKey:SPContentFilters];
+ [stateDetails removeObjectsForKeys:[NSArray arrayWithObjects:SPQueryFavorites, SPQueryHistory, SPContentFilters, nil]];
+ [spfData addEntriesFromDictionary:stateDetails];
+
+ // Determine whether to use encryption when adding the data
+ [spfStructure setObject:[spfDocData_temp objectForKey:@"encrypted"] forKey:@"encrypted"];
+ if (![[spfDocData_temp objectForKey:@"encrypted"] boolValue]) {
+ [spfStructure setObject:spfData forKey:@"data"];
} else {
- NSMutableData *encryptdata = [[[NSMutableData alloc] init] autorelease];
- NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:encryptdata] autorelease];
- [archiver encodeObject:data forKey:@"data"];
+ NSMutableData *dataToEncrypt = [[[NSMutableData alloc] init] autorelease];
+ NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:dataToEncrypt] autorelease];
+ [archiver encodeObject:spfData forKey:@"data"];
[archiver finishEncoding];
- [spfdata setObject:[encryptdata dataEncryptedWithPassword:[spfDocData_temp objectForKey:@"e_string"]] forKey:@"data"];
+ [spfStructure setObject:[dataToEncrypt dataEncryptedWithPassword:[spfDocData_temp objectForKey:@"e_string"]] forKey:@"data"];
}
+ // Convert to plist
NSString *err = nil;
- NSData *plist = [NSPropertyListSerialization dataFromPropertyList:spfdata
- format:NSPropertyListXMLFormat_v1_0
- errorDescription:&err];
+ NSData *plist = [NSPropertyListSerialization dataFromPropertyList:spfStructure
+ format:NSPropertyListXMLFormat_v1_0
+ errorDescription:&err];
- if(err != nil) {
+ if (err != nil) {
NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while converting connection data", @"error while converting connection data")]
defaultButton:NSLocalizedString(@"OK", @"OK button")
alternateButton:nil
- otherButton:nil
- informativeTextWithFormat:err];
+ otherButton:nil
+ informativeTextWithFormat:err];
[alert setAlertStyle:NSCriticalAlertStyle];
[alert runModal];
@@ -3481,18 +2974,18 @@
NSError *error = nil;
[plist writeToFile:fileName options:NSAtomicWrite error:&error];
- if(error != nil){
+ if (error != nil){
NSAlert *errorAlert = [NSAlert alertWithError:error];
[errorAlert runModal];
return NO;
}
- if(contextInfo == nil) {
+ if (contextInfo == nil) {
// Register and update query favorites, content filter, and history for the (new) file URL
NSMutableDictionary *preferences = [[NSMutableDictionary alloc] init];
- [preferences setObject:[spfdata objectForKey:SPQueryHistory] forKey:SPQueryHistory];
- [preferences setObject:[spfdata objectForKey:SPQueryFavorites] forKey:SPQueryFavorites];
- [preferences setObject:[spfdata objectForKey:SPContentFilters] forKey:SPContentFilters];
+ [preferences setObject:[spfStructure objectForKey:SPQueryHistory] forKey:SPQueryHistory];
+ [preferences setObject:[spfStructure objectForKey:SPQueryFavorites] forKey:SPQueryFavorites];
+ [preferences setObject:[spfStructure objectForKey:SPContentFilters] forKey:SPContentFilters];
[[SPQueryController sharedQueryController] registerDocumentWithFileURL:[NSURL fileURLWithPath:fileName] andContextInfo:preferences];
[self setFileURL:[NSURL fileURLWithPath:fileName]];
@@ -3512,6 +3005,35 @@
}
/**
+ * Open the currently selected database in a new tab, clearing any table selection.
+ */
+- (IBAction)openDatabaseInNewTab:(id)sender
+{
+
+ // Add a new tab to the window
+ [[parentWindow windowController] addNewConnection:self];
+
+ // Get the current state
+ NSDictionary *allStateDetails = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:YES], @"connection",
+ [NSNumber numberWithBool:YES], @"history",
+ [NSNumber numberWithBool:YES], @"session",
+ [NSNumber numberWithBool:YES], @"query",
+ [NSNumber numberWithBool:YES], @"password",
+ nil];
+ NSMutableDictionary *currentState = [NSMutableDictionary dictionaryWithDictionary:[self stateIncludingDetails:allStateDetails]];
+
+ // Ensure it's set to autoconnect, and clear the table
+ [currentState setObject:[NSNumber numberWithBool:YES] forKey:@"auto_connect"];
+ NSMutableDictionary *sessionDict = [NSMutableDictionary dictionaryWithDictionary:[currentState objectForKey:@"session"]];
+ [sessionDict removeObjectForKey:@"table"];
+ [currentState setObject:sessionDict forKey:@"session"];
+
+ // Set the connection on the new tab
+ [[[NSApp delegate] frontDocument] setState:currentState];
+}
+
+/**
* Passes the request to the dataImport object
*/
- (IBAction)import:(id)sender
@@ -3594,10 +3116,11 @@
}
}
- if ([menuItem action] == @selector(import:) ||
- [menuItem action] == @selector(removeDatabase:) ||
- [menuItem action] == @selector(copyDatabase:) ||
- [menuItem action] == @selector(renameDatabase:) ||
+ if ([menuItem action] == @selector(import:) ||
+ [menuItem action] == @selector(removeDatabase:) ||
+ [menuItem action] == @selector(copyDatabase:) ||
+ [menuItem action] == @selector(renameDatabase:) ||
+ [menuItem action] == @selector(openDatabaseInNewTab:) ||
[menuItem action] == @selector(refreshTables:))
{
return ([self database] != nil);
@@ -4355,6 +3878,611 @@
}
#pragma mark -
+#pragma mark State saving and setting
+
+/**
+ * Retrieve the current database document state for saving. A supplied dictionary
+ * determines the level of detail that is required, with the following optional keys:
+ * - connection: Connection settings (with keychain references where available) and database
+ * - password: Whether to include passwords in the returned connection details
+ * - session: Selected table and view, together with content view filter, sort, scroll position
+ * - history: query history, per-doc query favourites, and per-doc content filters
+ * - query: custom query editor content
+ * - enablecompression: large (>50k) custom query editor contents will be stored as compressed data
+ * If none of these are supplied, nil will be returned.
+ */
+- (NSDictionary *) stateIncludingDetails:(NSDictionary *)detailsToReturn
+{
+ BOOL returnConnection = [[detailsToReturn objectForKey:@"connection"] boolValue];
+ BOOL includePasswords = [[detailsToReturn objectForKey:@"password"] boolValue];
+ BOOL returnSession = [[detailsToReturn objectForKey:@"session"] boolValue];
+ BOOL returnHistory = [[detailsToReturn objectForKey:@"history"] boolValue];
+ BOOL returnQuery = [[detailsToReturn objectForKey:@"query"] boolValue];
+
+ if (!returnConnection && !returnSession && !returnHistory && !returnQuery) return nil;
+ NSMutableDictionary *stateDetails = [NSMutableDictionary dictionary];
+
+ // Add connection details
+ if (returnConnection) {
+ NSMutableDictionary *connection = [NSMutableDictionary dictionary];
+
+ [connection setObject:@"mysql" forKey:@"rdbms_type"];
+
+ NSString *connectionType;
+ switch ([connectionController type]) {
+ case SPTCPIPConnection:
+ connectionType = @"SPTCPIPConnection";
+ break;
+ case SPSocketConnection:
+ connectionType = @"SPSocketConnection";
+ if ([connectionController socket] && [[connectionController socket] length]) [connection setObject:[connectionController socket] forKey:@"socket"];
+ break;
+ case SPSSHTunnelConnection:
+ connectionType = @"SPSSHTunnelConnection";
+ [connection setObject:[connectionController sshHost] forKey:@"ssh_host"];
+ [connection setObject:[connectionController sshUser] forKey:@"ssh_user"];
+ [connection setObject:[NSNumber numberWithInt:[connectionController sshKeyLocationEnabled]] forKey:@"ssh_keyLocationEnabled"];
+ [connection setObject:[connectionController sshKeyLocation] forKey:@"ssh_keyLocation"];
+ if ([connectionController sshPort] && [[connectionController sshPort] length])
+ [connection setObject:[NSNumber numberWithInteger:[[connectionController sshPort] integerValue]] forKey:@"ssh_port"];
+ break;
+ default:
+ connectionType = @"SPTCPIPConnection";
+ }
+ [connection setObject:connectionType forKey:@"type"];
+
+ if ([[self keyChainID] length]) [connection setObject:[self keyChainID] forKey:@"kcid"];
+ [connection setObject:[self name] forKey:@"name"];
+ [connection setObject:[self host] forKey:@"host"];
+ [connection setObject:[self user] forKey:@"user"];
+ if([connectionController port] && [[connectionController port] length])
+ [connection setObject:[NSNumber numberWithInteger:[[connectionController port] integerValue]] forKey:@"port"];
+ if([[self database] length])
+ [connection setObject:[self database] forKey:@"database"];
+
+ if (includePasswords) {
+ NSString *pw = [self keychainPasswordForConnection:nil];
+ if (![pw length]) pw = [connectionController password];
+ if (pw)
+ [connection setObject:pw forKey:@"password"];
+ else
+ [connection setObject:@"" forKey:@"password"];
+
+ if ([connectionController type] == SPSSHTunnelConnection) {
+ NSString *sshpw = [self keychainPasswordForSSHConnection:nil];
+ if(![sshpw length]) sshpw = [connectionController sshPassword];
+ if (sshpw)
+ [connection setObject:sshpw forKey:@"ssh_password"];
+ else
+ [connection setObject:@"" forKey:@"ssh_password"];
+ }
+ }
+
+ [connection setObject:[NSNumber numberWithInt:[connectionController useSSL]] forKey:@"useSSL"];
+ [connection setObject:[NSNumber numberWithInt:[connectionController sslKeyFileLocationEnabled]] forKey:@"sslKeyFileLocationEnabled"];
+ [connection setObject:[connectionController sslKeyFileLocation] forKey:@"sslKeyFileLocation"];
+ [connection setObject:[NSNumber numberWithInt:[connectionController sslCertificateFileLocationEnabled]] forKey:@"sslCertificateFileLocationEnabled"];
+ [connection setObject:[connectionController sslCertificateFileLocation] forKey:@"sslCertificateFileLocation"];
+ [connection setObject:[NSNumber numberWithInt:[connectionController sslCACertFileLocationEnabled]] forKey:@"sslCACertFileLocationEnabled"];
+ [connection setObject:[connectionController sslCACertFileLocation] forKey:@"sslCACertFileLocation"];
+
+ [stateDetails setObject:[NSDictionary dictionaryWithDictionary:connection] forKey:@"connection"];
+ }
+
+ // Add document-specific saved settings
+ if (returnHistory) {
+ [stateDetails setObject:[[SPQueryController sharedQueryController] favoritesForFileURL:[self fileURL]] forKey:SPQueryFavorites];
+ [stateDetails setObject:[[SPQueryController sharedQueryController] historyForFileURL:[self fileURL]] forKey:SPQueryHistory];
+ [stateDetails setObject:[[SPQueryController sharedQueryController] contentFilterForFileURL:[self fileURL]] forKey:SPContentFilters];
+ }
+
+ // Set up a session state dictionary for either state or custom query
+ NSMutableDictionary *sessionState = [NSMutableDictionary dictionary];
+
+ // Store session state if appropriate
+ if (returnSession) {
+
+ if ([[self table] length])
+ [sessionState setObject:[self table] forKey:@"table"];
+
+ NSString *currentlySelectedViewName;
+ switch ([spHistoryControllerInstance currentlySelectedView]) {
+ case SPTableViewStructure:
+ currentlySelectedViewName = @"SP_VIEW_STRUCTURE";
+ break;
+ case SPTableViewContent:
+ currentlySelectedViewName = @"SP_VIEW_CONTENT";
+ break;
+ case SPTableViewCustomQuery:
+ currentlySelectedViewName = @"SP_VIEW_CUSTOMQUERY";
+ break;
+ case SPTableViewStatus:
+ currentlySelectedViewName = @"SP_VIEW_STATUS";
+ break;
+ case SPTableViewRelations:
+ currentlySelectedViewName = @"SP_VIEW_RELATIONS";
+ break;
+ case SPTableViewTriggers:
+ currentlySelectedViewName = @"SP_VIEW_TRIGGERS";
+ break;
+ default:
+ currentlySelectedViewName = @"SP_VIEW_STRUCTURE";
+ }
+ [sessionState setObject:currentlySelectedViewName forKey:@"view"];
+
+ [sessionState setObject:[mySQLConnection encoding] forKey:@"connectionEncoding"];
+
+ [sessionState setObject:[NSNumber numberWithBool:[[parentWindow toolbar] isVisible]] forKey:@"isToolbarVisible"];
+ [sessionState setObject:[NSNumber numberWithFloat:[tableContentInstance tablesListWidth]] forKey:@"windowVerticalDividerPosition"];
+
+ if ([tableContentInstance sortColumnName])
+ [sessionState setObject:[tableContentInstance sortColumnName] forKey:@"contentSortCol"];
+ [sessionState setObject:[NSNumber numberWithBool:[tableContentInstance sortColumnIsAscending]] forKey:@"contentSortColIsAsc"];
+ [sessionState setObject:[NSNumber numberWithInteger:[tableContentInstance pageNumber]] forKey:@"contentPageNumber"];
+ [sessionState setObject:NSStringFromRect([tableContentInstance viewport]) forKey:@"contentViewport"];
+ if ([tableContentInstance filterSettings])
+ [sessionState setObject:[tableContentInstance filterSettings] forKey:@"contentFilter"];
+
+ NSIndexSet *contentSelectedIndexSet = [tableContentInstance selectedRowIndexes];
+ if (contentSelectedIndexSet && [contentSelectedIndexSet count]) {
+ NSMutableArray *indices = [NSMutableArray array];
+ NSUInteger indexBuffer[[contentSelectedIndexSet count]];
+ NSUInteger limit = [contentSelectedIndexSet getIndexes:indexBuffer maxCount:[contentSelectedIndexSet count] inIndexRange:NULL];
+ NSUInteger idx;
+ for (idx = 0; idx < limit; idx++) {
+ [indices addObject:[NSNumber numberWithInteger:indexBuffer[idx]]];
+ }
+ [sessionState setObject:indices forKey:@"contentSelectedIndexSet"];
+ }
+ }
+
+ // Add the custom query editor content if appropriate
+ if (returnQuery) {
+ NSString *queryString = [[[customQueryInstance valueForKeyPath:@"textView"] textStorage] string];
+ if ([[detailsToReturn objectForKey:@"enablecompression"] boolValue] && [queryString length] > 50000) {
+ [sessionState setObject:[[queryString dataUsingEncoding:NSUTF8StringEncoding] compress] forKey:@"queries"];
+ } else {
+ [sessionState setObject:queryString forKey:@"queries"];
+ }
+ }
+
+ // Store the session state dictionary if either state or custom queries were saved
+ if ([sessionState count])
+ [stateDetails setObject:[NSDictionary dictionaryWithDictionary:sessionState] forKey:@"session"];
+
+ return stateDetails;
+}
+
+/**
+ * Set the state of the document to the supplied dictionary, which should
+ * at least contain a "connection" dictionary of details.
+ * Returns whether the state was set successfully.
+ */
+- (BOOL)setState:(NSDictionary *)stateDetails
+{
+ NSDictionary *connection = nil;
+ NSInteger connectionType = -1;
+
+ // If this document already has a connection, don't proceed.
+ if (mySQLConnection) return NO;
+
+ // Load the connection data from the state dictionary
+ connection = [NSDictionary dictionaryWithDictionary:[stateDetails objectForKey:@"connection"]];
+ if (!connection) return NO;
+
+ [self updateWindowTitle:self];
+
+ // Deselect all favorites on the connection controller
+ [[connectionController valueForKeyPath:@"favoritesTable"] deselectAll:connectionController];
+
+ // Suppress the possibility to choose an other connection from the favorites
+ // if a connection should initialized by SPF file. Otherwise it could happen
+ // that the SPF file runs out of sync.
+ [[connectionController valueForKeyPath:@"favoritesTable"] setEnabled:NO];
+
+ // Ensure the connection controller is set to a blank slate
+ [connectionController setName:@""];
+ [connectionController setUser:@""];
+ [connectionController setHost:@""];
+ [connectionController setPort:@""];
+ [connectionController setSocket:@""];
+ [connectionController setUseSSL:NSOffState];
+ [connectionController setSslKeyFileLocationEnabled:NSOffState];
+ [connectionController setSslKeyFileLocation:nil];
+ [connectionController setSslCertificateFileLocationEnabled:NSOffState];
+ [connectionController setSslCertificateFileLocation:nil];
+ [connectionController setSslCACertFileLocationEnabled:NSOffState];
+ [connectionController setSslCACertFileLocation:nil];
+ [connectionController setSshHost:@""];
+ [connectionController setSshUser:@""];
+ [connectionController setSshKeyLocationEnabled:NSOffState];
+ [connectionController setSshKeyLocation:nil];
+ [connectionController setSshPort:@""];
+ [connectionController setDatabase:@""];
+ [connectionController setPassword:nil];
+ [connectionController setSshPassword:nil];
+
+ // Set the correct connection type
+ if ([connection objectForKey:@"type"]) {
+ if ([[connection objectForKey:@"type"] isEqualToString:@"SPTCPIPConnection"])
+ connectionType = SPTCPIPConnection;
+ else if ([[connection objectForKey:@"type"] isEqualToString:@"SPSocketConnection"])
+ connectionType = SPSocketConnection;
+ else if ([[connection objectForKey:@"type"] isEqualToString:@"SPSSHTunnelConnection"])
+ connectionType = SPSSHTunnelConnection;
+ else
+ connectionType = SPTCPIPConnection;
+
+ [connectionController setType:connectionType];
+ [connectionController resizeTabViewToConnectionType:connectionType animating:NO];
+ }
+
+ // Set basic details
+ if ([connection objectForKey:@"name"])
+ [connectionController setName:[connection objectForKey:@"name"]];
+ if ([connection objectForKey:@"user"])
+ [connectionController setUser:[connection objectForKey:@"user"]];
+ if ([connection objectForKey:@"host"])
+ [connectionController setHost:[connection objectForKey:@"host"]];
+ if ([connection objectForKey:@"port"])
+ [connectionController setPort:[NSString stringWithFormat:@"%ld", (long)[[connection objectForKey:@"port"] integerValue]]];
+
+ // Set SSL details
+ if ([connection objectForKey:@"useSSL"])
+ [connectionController setUseSSL:[[connection objectForKey:@"useSSL"] intValue]];
+ if ([connection objectForKey:@"sslKeyFileLocationEnabled"])
+ [connectionController setSslKeyFileLocationEnabled:[[connection objectForKey:@"sslKeyFileLocationEnabled"] intValue]];
+ if ([connection objectForKey:@"sslKeyFileLocation"])
+ [connectionController setSslKeyFileLocation:[connection objectForKey:@"sslKeyFileLocation"]];
+ if ([connection objectForKey:@"sslCertificateFileLocationEnabled"])
+ [connectionController setSslCertificateFileLocationEnabled:[[connection objectForKey:@"sslCertificateFileLocationEnabled"] intValue]];
+ if ([connection objectForKey:@"sslCertificateFileLocation"])
+ [connectionController setSslCertificateFileLocation:[connection objectForKey:@"sslCertificateFileLocation"]];
+ if ([connection objectForKey:@"sslCACertFileLocationEnabled"])
+ [connectionController setSslCACertFileLocationEnabled:[[connection objectForKey:@"sslCACertFileLocationEnabled"] intValue]];
+ if ([connection objectForKey:@"sslCACertFileLocation"])
+ [connectionController setSslCACertFileLocation:[connection objectForKey:@"sslCACertFileLocation"]];
+
+ // Set the keychain details if available
+ if ([connection objectForKey:@"kcid"] && [(NSString *)[connection objectForKey:@"kcid"] length])
+ [self setKeychainID:[connection objectForKey:@"kcid"]];
+
+ // Set password - if not in SPF file try to get it via the KeyChain
+ if ([connection objectForKey:@"password"])
+ [connectionController setPassword:[connection objectForKey:@"password"]];
+ else {
+ NSString *pw = [self keychainPasswordForConnection:nil];
+ if (pw)
+ [connectionController setPassword:pw];
+ }
+
+ // Set the socket details, whether or not the type is a socket
+ if ([connection objectForKey:@"socket"])
+ [connectionController setSocket:[connection objectForKey:@"socket"]];
+
+ // Set SSH details if available, whether or not the SSH type is currently active (to allow fallback on failure)
+ if ([connection objectForKey:@"ssh_host"])
+ [connectionController setSshHost:[connection objectForKey:@"ssh_host"]];
+ if ([connection objectForKey:@"ssh_user"])
+ [connectionController setSshUser:[connection objectForKey:@"ssh_user"]];
+ if ([connection objectForKey:@"ssh_keyLocationEnabled"])
+ [connectionController setSshKeyLocationEnabled:[[connection objectForKey:@"ssh_keyLocationEnabled"] intValue]];
+ if ([connection objectForKey:@"ssh_keyLocation"])
+ [connectionController setSshKeyLocation:[connection objectForKey:@"ssh_keyLocation"]];
+ if ([connection objectForKey:@"ssh_port"])
+ [connectionController setSshPort:[NSString stringWithFormat:@"%ld", (long)[[connection objectForKey:@"ssh_port"] integerValue]]];
+
+ // Set the SSH password - if not in SPF file try to get it via the KeyChain
+ if ([connection objectForKey:@"ssh_password"])
+ [connectionController setSshPassword:[connection objectForKey:@"ssh_password"]];
+ else {
+ NSString *sshpw = [self keychainPasswordForSSHConnection:nil];
+ if(sshpw)
+ [connectionController setSshPassword:sshpw];
+ }
+
+ // Restore the selected database if saved
+ if ([connection objectForKey:@"database"])
+ [connectionController setDatabase:[connection objectForKey:@"database"]];
+
+ // Store session details - if provided - for later setting once the connection is established
+ if ([stateDetails objectForKey:@"session"]) {
+ spfSession = [[NSDictionary dictionaryWithDictionary:[stateDetails objectForKey:@"session"]] retain];
+ }
+
+ // Restore favourites and history
+ if ([stateDetails objectForKey:SPQueryFavorites])
+ [spfPreferences setObject:[stateDetails objectForKey:SPQueryFavorites] forKey:SPQueryFavorites];
+ if ([stateDetails objectForKey:SPQueryHistory])
+ [spfPreferences setObject:[stateDetails objectForKey:SPQueryHistory] forKey:SPQueryHistory];
+ if ([stateDetails objectForKey:SPContentFilters])
+ [spfPreferences setObject:[stateDetails objectForKey:SPContentFilters] forKey:SPContentFilters];
+
+ [connectionController updateSSLInterface:self];
+
+ // Autoconnect if appropriate
+ if ([stateDetails objectForKey:@"auto_connect"] && [[stateDetails valueForKey:@"auto_connect"] boolValue]) {
+ [connectionController initiateConnection:self];
+ }
+}
+
+/**
+ * Initialise the document with the connection file at the supplied path.
+ */
+- (void)setStateFromConnectionFile:(NSString *)path
+{
+ NSError *readError = nil;
+ NSString *convError = nil;
+ NSPropertyListFormat format;
+
+ NSString *encryptpw = nil;
+ NSMutableDictionary *data = nil;
+ NSDictionary *spf = nil;
+
+
+ // Read the property list data, and unserialize it.
+ NSData *pData = [NSData dataWithContentsOfFile:path options:NSUncachedRead error:&readError];
+
+ spf = [[NSPropertyListSerialization propertyListFromData:pData
+ mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain];
+
+ if (!spf || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
+ NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading connection data file", @"error while reading connection data file")]
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:NSLocalizedString(@"Connection data file couldn't be read.", @"error while reading connection data file")];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+ [alert runModal];
+ if (spf) [spf release];
+ [self closeAndDisconnect];
+ return;
+ }
+
+ // If the .spf format is unhandled, error.
+ if (![[spf objectForKey:@"format"] isEqualToString:@"connection"]) {
+ NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Warning", @"warning")]
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"The chosen file “%@” contains ‘%@’ data.", @"message while reading a spf file which matches non-supported formats."), path, [spf objectForKey:@"format"]]];
+
+ [alert setAlertStyle:NSWarningAlertStyle];
+ [spf release];
+ [self closeAndDisconnect];
+ [alert runModal];
+ return;
+ }
+
+ // Error if the expected data source wasn't present in the file
+ if (![spf objectForKey:@"data"]) {
+ NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading connection data file", @"error while reading connection data file")]
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:NSLocalizedString(@"No data found.", @"no data found")];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+ [alert runModal];
+ [spf release];
+ [self closeAndDisconnect];
+ return;
+ }
+
+ // Ask for a password if SPF file passwords were encrypted, via a sheet
+ if ([spf objectForKey:@"encrypted"] && [[spf valueForKey:@"encrypted"] boolValue]) {
+ if([self isSaveInBundle] && [[[NSApp delegate] spfSessionDocData] objectForKey:@"e_string"]) {
+ encryptpw = [[[NSApp delegate] spfSessionDocData] objectForKey:@"e_string"];
+ } else {
+ [inputTextWindowHeader setStringValue:NSLocalizedString(@"Connection file is encrypted", @"Connection file is encrypted")];
+ [inputTextWindowMessage setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Please enter the password for ‘%@’:", @"Please enter the password"), ([self isSaveInBundle]) ? [[[[NSApp delegate] sessionURL] absoluteString] lastPathComponent] : [path lastPathComponent]]];
+ [inputTextWindowSecureTextField setStringValue:@""];
+ [inputTextWindowSecureTextField selectText:nil];
+
+ [NSApp beginSheet:inputTextWindow modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
+
+ // wait for encryption password
+ NSModalSession session = [NSApp beginModalSessionForWindow:inputTextWindow];
+ for (;;) {
+
+ // Execute code on DefaultRunLoop
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
+ beforeDate:[NSDate distantFuture]];
+
+ // Break the run loop if editSheet was closed
+ if ([NSApp runModalSession:session] != NSRunContinuesResponse
+ || ![inputTextWindow isVisible])
+ break;
+
+ // Execute code on DefaultRunLoop
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
+ beforeDate:[NSDate distantFuture]];
+
+ }
+ [NSApp endModalSession:session];
+ [inputTextWindow orderOut:nil];
+ [NSApp endSheet:inputTextWindow];
+
+ if (passwordSheetReturnCode) {
+ encryptpw = [inputTextWindowSecureTextField stringValue];
+ if ([self isSaveInBundle]) {
+ NSMutableDictionary *spfSessionData = [NSMutableDictionary dictionary];
+ [spfSessionData addEntriesFromDictionary:[[NSApp delegate] spfSessionDocData]];
+ [spfSessionData setObject:encryptpw forKey:@"e_string"];
+ [[NSApp delegate] setSpfSessionDocData:spfSessionData];
+ }
+ } else {
+ [self closeAndDisconnect];
+ [spf release];
+ return;
+ }
+ }
+ }
+
+ if ([[spf objectForKey:@"data"] isKindOfClass:[NSDictionary class]])
+ data = [NSMutableDictionary dictionaryWithDictionary:[spf objectForKey:@"data"]];
+ else if ([[spf objectForKey:@"data"] isKindOfClass:[NSData class]]) {
+ NSData *decryptdata = nil;
+ decryptdata = [[[NSMutableData alloc] initWithData:[(NSData *)[spf objectForKey:@"data"] dataDecryptedWithPassword:encryptpw]] autorelease];
+ if (decryptdata != nil && [decryptdata length]) {
+ NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:decryptdata] autorelease];
+ data = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary *)[unarchiver decodeObjectForKey:@"data"]];
+ [unarchiver finishDecoding];
+ }
+ if (data == nil) {
+ NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading connection data file", @"error while reading connection data file")]
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:NSLocalizedString(@"Wrong data format or password.", @"wrong data format or password")];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+ [alert runModal];
+ [self closeAndDisconnect];
+ [spf release];
+ return;
+ }
+ }
+
+ // Ensure the data was read correctly, and has connection details
+ if (!data || ![data objectForKey:@"connection"]) {
+ NSString *informativeText;
+ if (!data) {
+ informativeText = NSLocalizedString(@"Wrong data format.", @"wrong data format");
+ } else {
+ informativeText = NSLocalizedString(@"No connection data found.", @"no connection data found");
+ }
+ NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading connection data file", @"error while reading connection data file")]
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:informativeText];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+ [alert runModal];
+ [self closeAndDisconnect];
+ [spf release];
+ return;
+ }
+
+ // Move favourites and history into the data dictionary to pass to setState:
+ [data setObject:[spf objectForKey:SPQueryFavorites] forKey:SPQueryFavorites];
+ [data setObject:[spf objectForKey:SPQueryHistory] forKey:SPQueryHistory];
+ [data setObject:[spf objectForKey:SPContentFilters] forKey:SPContentFilters];
+
+ // Ensure the encryption status is stored in the spfDocData store for future saves
+ [spfDocData setObject:[NSNumber numberWithBool:NO] forKey:@"encrypted"];
+ if (encryptpw != nil) {
+ [spfDocData setObject:[NSNumber numberWithBool:YES] forKey:@"encrypted"];
+ [spfDocData setObject:encryptpw forKey:@"e_string"];
+ }
+ encryptpw = nil;
+
+ // If session data is available, ensure it is marked for save
+ if ([data objectForKey:@"session"]) {
+ [spfDocData setObject:[NSNumber numberWithBool:YES] forKey:@"include_session"];
+ }
+
+ if (![self isSaveInBundle]) {
+ [self setFileURL:[NSURL fileURLWithPath:path]];
+ [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:[NSURL fileURLWithPath:path]];
+ }
+
+ [spfDocData setObject:[NSNumber numberWithBool:([[data objectForKey:@"connection"] objectForKey:@"password"]) ? YES : NO] forKey:@"save_password"];
+
+ [spfDocData setObject:[NSNumber numberWithBool:NO] forKey:@"auto_connect"];
+
+ if([spf objectForKey:@"auto_connect"] && [[spf valueForKey:@"auto_connect"] boolValue]) {
+ [spfDocData setObject:[NSNumber numberWithBool:YES] forKey:@"auto_connect"];
+ [data setObject:[NSNumber numberWithBool:YES] forKey:@"auto_connect"];
+ }
+
+ // Set the state dictionary, triggering an autoconnect if appropriate
+ [self setState:data];
+
+ [spf release];
+}
+
+/**
+ * Restore session from SPF file if given
+ */
+- (void)restoreSession
+{
+ NSAutoreleasePool *taskPool = [[NSAutoreleasePool alloc] init];
+
+ // Check and set the table
+ NSArray *tables = [tablesListInstance tables];
+
+ BOOL isSelectedTableDefined = YES;
+
+ if([tables indexOfObject:[spfSession objectForKey:@"table"]] == NSNotFound) {
+ isSelectedTableDefined = NO;
+ }
+
+ // Restore toolbar setting
+ if([spfSession objectForKey:@"isToolbarVisible"])
+ [mainToolbar setVisible:[[spfSession objectForKey:@"isToolbarVisible"] boolValue]];
+
+ // Reset database view encoding if differs from default
+ if([spfSession objectForKey:@"connectionEncoding"] && ![[mySQLConnection encoding] isEqualToString:[spfSession objectForKey:@"connectionEncoding"]])
+ [self setConnectionEncoding:[spfSession objectForKey:@"connectionEncoding"] reloadingViews:YES];
+
+ if(isSelectedTableDefined) {
+ // Set table content details for restore
+ if([spfSession objectForKey:@"contentSortCol"])
+ [tableContentInstance setSortColumnNameToRestore:[spfSession objectForKey:@"contentSortCol"] isAscending:[[spfSession objectForKey:@"contentSortColIsAsc"] boolValue]];
+ if([spfSession objectForKey:@"contentPageNumber"])
+ [tableContentInstance setPageToRestore:[[spfSession objectForKey:@"pageNumber"] integerValue]];
+ if([spfSession objectForKey:@"contentViewport"])
+ [tableContentInstance setViewportToRestore:NSRectFromString([spfSession objectForKey:@"contentViewport"])];
+ if([spfSession objectForKey:@"contentFilter"])
+ [tableContentInstance setFiltersToRestore:[spfSession objectForKey:@"contentFilter"]];
+
+ // Select table
+ [tablesListInstance selectTableAtIndex:[NSNumber numberWithInteger:[tables indexOfObject:[spfSession objectForKey:@"table"]]]];
+
+ // Restore table selection indexes
+ if([spfSession objectForKey:@"contentSelectedIndexSet"]) {
+ NSMutableIndexSet *anIndexSet = [NSMutableIndexSet indexSet];
+ NSArray *items = [spfSession objectForKey:@"contentSelectedIndexSet"];
+ NSUInteger i;
+ for(i=0; i<[items count]; i++)
+ [anIndexSet addIndex:[NSArrayObjectAtIndex(items, i) integerValue]];
+
+ [tableContentInstance setSelectedRowIndexesToRestore:anIndexSet];
+ }
+
+ [[tablesListInstance valueForKeyPath:@"tablesListView"] scrollRowToVisible:[tables indexOfObject:[spfSession objectForKey:@"selectedTable"]]];
+
+ }
+
+ // Select view
+ if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_STRUCTURE"])
+ [self viewStructure:self];
+ else if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_CONTENT"])
+ [self viewContent:self];
+ else if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_CUSTOMQUERY"])
+ [self viewQuery:self];
+ else if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_STATUS"])
+ [self viewStatus:self];
+ else if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_RELATIONS"])
+ [self viewRelations:self];
+ else if([[spfSession objectForKey:@"view"] isEqualToString:@"SP_VIEW_TRIGGERS"])
+ [self viewTriggers:self];
+
+ [self updateWindowTitle:self];
+
+ // dealloc spfSession data
+ [spfSession release];
+ spfSession = nil;
+
+ // End the task
+ [self endTask];
+ [taskPool drain];
+}
+
+#pragma mark -
#pragma mark Connection controller delegate methods
/**
diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m
index 2f0cec21..5b7f4a4b 100644
--- a/Source/SPTableContent.m
+++ b/Source/SPTableContent.m
@@ -284,6 +284,10 @@
BOOL enableInteraction = ![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:SPMainToolbarTableContent] || ![tableDocumentInstance isWorking];
if (!tableDetails) {
+
+ // If no table is currently selected, no action required - return.
+ if (!selectedTable) return;
+
newTableName = nil;
} else {
newTableName = [tableDetails objectForKey:@"name"];
diff --git a/Source/SPTablesList.h b/Source/SPTablesList.h
index 56711217..8487358d 100644
--- a/Source/SPTablesList.h
+++ b/Source/SPTablesList.h
@@ -65,7 +65,6 @@
IBOutlet id toolbarReloadButton;
IBOutlet id addTableButton;
IBOutlet id truncateTableButton;
- IBOutlet id truncateTableContextButton;
IBOutlet NSSplitView *tableListSplitView;
IBOutlet NSSplitView *tableListFilterSplitView;
IBOutlet NSButton *tableInfoCollapseButton;
@@ -76,6 +75,7 @@
IBOutlet NSMenuItem *removeTableMenuItem;
IBOutlet NSMenuItem *duplicateTableMenuItem;
IBOutlet NSMenuItem *renameTableMenuItem;
+ IBOutlet NSMenuItem *openTableInNewTabMenuItem;
IBOutlet NSMenuItem *separatorTableMenuItem;
IBOutlet NSMenuItem *showCreateSyntaxMenuItem;
IBOutlet NSMenuItem *separatorTableMenuItem2;
@@ -85,7 +85,9 @@
// Table list context menu items
IBOutlet NSMenuItem *removeTableContextMenuItem;
IBOutlet NSMenuItem *duplicateTableContextMenuItem;
+ IBOutlet NSMenuItem *truncateTableContextMenuItem;
IBOutlet NSMenuItem *renameTableContextMenuItem;
+ IBOutlet NSMenuItem *openTableInNewTabContextMenuItem;
IBOutlet NSMenuItem *separatorTableContextMenuItem;
IBOutlet NSMenuItem *showCreateSyntaxContextMenuItem;
IBOutlet NSMenuItem *separatorTableContextMenuItem2;
@@ -111,6 +113,7 @@
- (IBAction)copyTable:(id)sender;
- (IBAction)renameTable:(id)sender;
- (IBAction)truncateTable:(id)sender;
+- (IBAction)openTableInNewTab:(id)sender;
- (IBAction)togglePaneCollapse:(id)sender;
// Additional methods
diff --git a/Source/SPTablesList.m b/Source/SPTablesList.m
index 6fdca8c0..41a528d3 100644
--- a/Source/SPTablesList.m
+++ b/Source/SPTablesList.m
@@ -558,6 +558,32 @@
}
/**
+ * Open the table in a new tab.
+ */
+- (IBAction)openTableInNewTab:(id)sender
+{
+
+ // Add a new tab to the window
+ [[[tableDocumentInstance parentWindow] windowController] addNewConnection:self];
+
+ // Get the state of the document
+ NSDictionary *allStateDetails = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:YES], @"connection",
+ [NSNumber numberWithBool:YES], @"history",
+ [NSNumber numberWithBool:YES], @"session",
+ [NSNumber numberWithBool:YES], @"query",
+ [NSNumber numberWithBool:YES], @"password",
+ nil];
+ NSMutableDictionary *documentState = [NSMutableDictionary dictionaryWithDictionary:[tableDocumentInstance stateIncludingDetails:allStateDetails]];
+
+ // Ensure it's set to autoconnect
+ [documentState setObject:[NSNumber numberWithBool:YES] forKey:@"auto_connect"];
+
+ // Set the connection on the new tab
+ [[[NSApp delegate] frontDocument] setState:documentState];
+}
+
+/**
* Toggle whether the splitview is collapsed.
*/
- (IBAction)togglePaneCollapse:(id)sender
@@ -701,27 +727,27 @@
[removeTableMenuItem setTitle:NSLocalizedString(@"Delete Tables", @"delete tables menu title")];
[truncateTableButton setTitle:NSLocalizedString(@"Truncate Tables", @"truncate tables menu item")];
[removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Tables", @"delete tables menu title")];
- [truncateTableContextButton setTitle:NSLocalizedString(@"Truncate Tables", @"truncate tables menu item")];
+ [truncateTableContextMenuItem setTitle:NSLocalizedString(@"Truncate Tables", @"truncate tables menu item")];
[truncateTableButton setHidden:NO];
- [truncateTableContextButton setHidden:NO];
+ [truncateTableContextMenuItem setHidden:NO];
break;
case SPTableTypeView:
[removeTableMenuItem setTitle:NSLocalizedString(@"Delete Views", @"delete views menu title")];
[removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Views", @"delete views menu title")];
[truncateTableButton setHidden:YES];
- [truncateTableContextButton setHidden:YES];
+ [truncateTableContextMenuItem setHidden:YES];
break;
case SPTableTypeProc:
[removeTableMenuItem setTitle:NSLocalizedString(@"Delete Procedures", @"delete procedures menu title")];
[removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Procedures", @"delete procedures menu title")];
[truncateTableButton setHidden:YES];
- [truncateTableContextButton setHidden:YES];
+ [truncateTableContextMenuItem setHidden:YES];
break;
case SPTableTypeFunc:
[removeTableMenuItem setTitle:NSLocalizedString(@"Delete Functions", @"delete functions menu title")];
[removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Functions", @"delete functions menu title")];
[truncateTableButton setHidden:YES];
- [truncateTableContextButton setHidden:YES];
+ [truncateTableContextMenuItem setHidden:YES];
break;
}
@@ -729,12 +755,14 @@
[removeTableMenuItem setTitle:NSLocalizedString(@"Delete Items", @"delete items menu title")];
[removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Items", @"delete items menu title")];
[truncateTableButton setHidden:YES];
- [truncateTableContextButton setHidden:YES];
+ [truncateTableContextMenuItem setHidden:YES];
}
+
}
// Context menu
[renameTableContextMenuItem setHidden:YES];
+ [openTableInNewTabContextMenuItem setHidden:YES];
[duplicateTableContextMenuItem setHidden:YES];
[separatorTableContextMenuItem setHidden:YES];
[separatorTableContextMenuItem2 setHidden:NO];
@@ -743,6 +771,7 @@
// 'Gear' menu
[renameTableMenuItem setHidden:YES];
+ [openTableInNewTabMenuItem setHidden:YES];
[duplicateTableMenuItem setHidden:YES];
[separatorTableMenuItem setHidden:YES];
[separatorTableMenuItem2 setHidden:NO];
@@ -828,6 +857,8 @@
[duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate View...", @"duplicate view menu title")];
[truncateTableButton setHidden:YES];
[removeTableMenuItem setTitle:NSLocalizedString(@"Delete View", @"delete view menu title")];
+ [openTableInNewTabMenuItem setHidden:NO];
+ [openTableInNewTabMenuItem setTitle:NSLocalizedString(@"Open View in New Tab", @"open view in new table title")];
[showCreateSyntaxMenuItem setHidden:NO];
[showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create View Syntax...", @"show create view syntax menu item")];
@@ -835,8 +866,10 @@
[renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename View...", @"rename view menu title")];
[duplicateTableContextMenuItem setHidden:NO];
[duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate View...", @"duplicate view menu title")];
- [truncateTableContextButton setHidden:YES];
+ [truncateTableContextMenuItem setHidden:YES];
[removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete View", @"delete view menu title")];
+ [openTableInNewTabContextMenuItem setHidden:NO];
+ [openTableInNewTabContextMenuItem setTitle:NSLocalizedString(@"Open View in New Tab", @"open view in new table title")];
[showCreateSyntaxContextMenuItem setHidden:NO];
[showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create View Syntax...", @"show create view syntax menu item")];
}
@@ -865,6 +898,8 @@
[truncateTableButton setHidden:NO];
[truncateTableButton setTitle:NSLocalizedString(@"Truncate Table", @"truncate table menu title")];
[removeTableMenuItem setTitle:NSLocalizedString(@"Delete Table", @"delete table menu title")];
+ [openTableInNewTabMenuItem setHidden:NO];
+ [openTableInNewTabMenuItem setTitle:NSLocalizedString(@"Open Table in New Tab", @"open table in new table title")];
[showCreateSyntaxMenuItem setHidden:NO];
[showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create Table Syntax...", @"show create table syntax menu item")];
@@ -872,9 +907,11 @@
[renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename Table...", @"rename table menu title")];
[duplicateTableContextMenuItem setHidden:NO];
[duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate Table...", @"duplicate table menu title")];
- [truncateTableContextButton setHidden:NO];
- [truncateTableContextButton setTitle:NSLocalizedString(@"Truncate Table", @"truncate table menu title")];
+ [truncateTableContextMenuItem setHidden:NO];
+ [truncateTableContextMenuItem setTitle:NSLocalizedString(@"Truncate Table", @"truncate table menu title")];
[removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Table", @"delete table menu title")];
+ [openTableInNewTabContextMenuItem setHidden:NO];
+ [openTableInNewTabContextMenuItem setTitle:NSLocalizedString(@"Open Table in New Tab", @"open table in new table title")];
[showCreateSyntaxContextMenuItem setHidden:NO];
[showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create Table Syntax...", @"show create table syntax menu item")];
}
@@ -896,6 +933,8 @@
[duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate Procedure...", @"duplicate proc menu title")];
[truncateTableButton setHidden:YES];
[removeTableMenuItem setTitle:NSLocalizedString(@"Delete Procedure", @"delete proc menu title")];
+ [openTableInNewTabMenuItem setHidden:NO];
+ [openTableInNewTabMenuItem setTitle:NSLocalizedString(@"Open Procedure in New Tab", @"open procedure in new table title")];
[showCreateSyntaxMenuItem setHidden:NO];
[showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create Procedure Syntax...", @"show create proc syntax menu item")];
@@ -903,8 +942,10 @@
[renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename Procedure...", @"rename proc menu title")];
[duplicateTableContextMenuItem setHidden:NO];
[duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate Procedure...", @"duplicate proc menu title")];
- [truncateTableContextButton setHidden:YES];
+ [truncateTableContextMenuItem setHidden:YES];
[removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Procedure", @"delete proc menu title")];
+ [openTableInNewTabContextMenuItem setHidden:NO];
+ [openTableInNewTabContextMenuItem setTitle:NSLocalizedString(@"Open Procedure in New Tab", @"open procedure in new table title")];
[showCreateSyntaxContextMenuItem setHidden:NO];
[showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create Procedure Syntax...", @"show create proc syntax menu item")];
}
@@ -926,6 +967,8 @@
[duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate Function...", @"duplicate func menu title")];
[truncateTableButton setHidden:YES];
[removeTableMenuItem setTitle:NSLocalizedString(@"Delete Function", @"delete func menu title")];
+ [openTableInNewTabMenuItem setHidden:NO];
+ [openTableInNewTabMenuItem setTitle:NSLocalizedString(@"Open Function in New Tab", @"open function in new table title")];
[showCreateSyntaxMenuItem setHidden:NO];
[showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create Function Syntax...", @"show create func syntax menu item")];
@@ -933,8 +976,10 @@
[renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename Function...", @"rename func menu title")];
[duplicateTableContextMenuItem setHidden:NO];
[duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate Function...", @"duplicate func menu title")];
- [truncateTableContextButton setHidden:YES];
+ [truncateTableContextMenuItem setHidden:YES];
[removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Function", @"delete func menu title")];
+ [openTableInNewTabContextMenuItem setHidden:NO];
+ [openTableInNewTabContextMenuItem setTitle:NSLocalizedString(@"Open Function in New Tab", @"open function in new table title")];
[showCreateSyntaxContextMenuItem setHidden:NO];
[showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create Function Syntax...", @"show create func syntax menu item")];
}
@@ -1506,7 +1551,7 @@
return ([tablesListView numberOfSelectedRows] > 0);
}
- if ([menuItem action] == @selector(renameTable:)) {
+ if ([menuItem action] == @selector(renameTable:) || [menuItem action] == @selector(openTableInNewTab:)) {
return (([tablesListView numberOfSelectedRows] == 1) && [[self tableName] length]);
}