From 4144a5e1b78480fd86994a9c255c9a0fb98db48b Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Sun, 9 May 2010 01:00:23 +0000 Subject: Rework alert sheets: - Change MCPConnection.m to no longer use a reference to tableWindow to attach sheets - instead use a delate error display method if available - Rework TableSource and TableContent sheetDidEnd methods into per-task methods rather than overloading contextInfo - Rework SPAlertSheets to perform actions on the main thread, with the loss of (unused) support for a didDismissSelector. This addresses a number of crashes logged by the crash reporter --- Source/CMTextView.m | 2 +- Source/CustomQuery.m | 10 +- Source/SPAlertSheets.h | 1 - Source/SPAlertSheets.m | 50 +- Source/SPConnectionController.h | 1 - Source/SPConnectionController.m | 13 +- Source/SPConnectionDelegate.m | 13 +- Source/SPDatabaseCopy.m | 4 +- Source/SPDatabaseInfo.m | 2 +- Source/SPDatabaseRename.m | 8 +- Source/SPExtendedTableInfo.m | 8 +- Source/SPFieldMapperController.m | 2 +- Source/SPKeychain.m | 7 +- Source/SPProcessListController.m | 4 +- Source/SPSSHTunnel.m | 1 + Source/SPTableCopy.m | 8 +- Source/SPTableData.m | 10 +- Source/SPTableRelations.m | 4 +- Source/SPTableTriggers.m | 4 +- Source/TableContent.h | 1 - Source/TableContent.m | 1234 +++++++++++++++++++------------------- Source/TableDocument.m | 18 +- Source/TableDump.m | 26 +- Source/TableSource.m | 242 ++++---- Source/TablesList.m | 17 +- 25 files changed, 881 insertions(+), 809 deletions(-) (limited to 'Source') diff --git a/Source/CMTextView.m b/Source/CMTextView.m index e936e801..18059059 100644 --- a/Source/CMTextView.m +++ b/Source/CMTextView.m @@ -1841,7 +1841,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) return [stdout autorelease]; } else { NSString *error = [[[NSString alloc] initWithData:errdata encoding:NSUTF8StringEncoding] autorelease]; - SPBeginAlertSheet(NSLocalizedString(@"BASH Error", @"bash error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"BASH Error", @"bash error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, [NSString stringWithFormat:@"%@ “%@”:\n%@", NSLocalizedString(@"Error for", @"error for message"), command, [error description]]); [stdout release]; NSBeep(); diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m index bc71d6ee..79f55545 100644 --- a/Source/CustomQuery.m +++ b/Source/CustomQuery.m @@ -168,7 +168,7 @@ // This should never evaluate to true as we are now performing menu validation, meaning the 'Save Query to Favorites' menu item will // only be enabled if the query text view has at least one character present. if ([[textView string] isEqualToString:@""]) { - SPBeginAlertSheet(NSLocalizedString(@"Empty query", @"empty query message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Empty query", @"empty query message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Cannot save an empty query.", @"empty query informative message")); return; } @@ -186,7 +186,7 @@ // This should never evaluate to true as we are now performing menu validation, meaning the 'Save Query to Favorites' menu item will // only be enabled if the query text view has at least one character present. if ([[textView string] isEqualToString:@""]) { - SPBeginAlertSheet(NSLocalizedString(@"Empty query", @"empty query message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Empty query", @"empty query message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Cannot save an empty query.", @"empty query informative message")); return; } @@ -1673,7 +1673,7 @@ // Check for errors while UPDATE if ([mySQLConnection queryErrored]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't write field.\nMySQL said: %@", @"message of panel when error while updating field to db"), [mySQLConnection getLastErrorMessage]]); return; @@ -1683,7 +1683,7 @@ // This shouldn't happen – for safety reasons if ( ![mySQLConnection affectedRows] ) { if ( [prefs boolForKey:SPShowNoAffectedRowsError] ) { - SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"The row was not written to the MySQL database. You probably haven't changed anything.\nReload the table to be sure that the row exists and use a primary key for your table.\n(This error can be turned off in the preferences.)", @"message of panel when no rows have been affected after writing to the db")); } else { NSBeep(); @@ -1698,7 +1698,7 @@ [self performQueries:[NSArray arrayWithObject:lastExecutedQuery] withCallback:NULL]; } else { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Updating field content failed. Couldn't identify field origin unambiguously (%ld match%@). It's very likely that while editing this field the table `%@` was changed by an other user.", @"message of panel when error while updating field to db after enabling it"), (long)numberOfPossibleUpdateRows, (numberOfPossibleUpdateRows>1)?@"es":@"", tableForColumn]); diff --git a/Source/SPAlertSheets.h b/Source/SPAlertSheets.h index bdcfdd64..d0f64f5b 100644 --- a/Source/SPAlertSheets.h +++ b/Source/SPAlertSheets.h @@ -30,7 +30,6 @@ void SPBeginAlertSheet( NSWindow *docWindow, id modalDelegate, SEL didEndSelector, - SEL didDismissSelector, void *contextInfo, NSString *msg ); diff --git a/Source/SPAlertSheets.m b/Source/SPAlertSheets.m index ccc8f337..67fb396e 100644 --- a/Source/SPAlertSheets.m +++ b/Source/SPAlertSheets.m @@ -25,10 +25,13 @@ #import "SPMainThreadTrampoline.h" /** - * Provide a very simple alias of NSBeginAlertSheet with one difference: - * printf-type format strings are no longer supported within the "msg" - * message text argument, preventing access of random stack areas for - * error text which contains inadvertant printf formatting. + * Provide a simple alias of NSBeginAlertSheet, with a few differences: + * - printf-type format strings are no longer supported within the "msg" + * message text argument, preventing access of random stack areas for + * error text which contains inadvertant printf formatting. + * - The didDismissSelector is no longer supported + * - The sheet no longer needs to be orderOut:ed after use + * - The alert is alays shown on the main thread. */ void SPBeginAlertSheet( NSString *title, @@ -38,22 +41,33 @@ void SPBeginAlertSheet( NSWindow *docWindow, id modalDelegate, SEL didEndSelector, - SEL didDismissSelector, void *contextInfo, NSString *msg ) { - NSBeginAlertSheet( - title, - defaultButton, - alternateButton, - otherButton, - docWindow, - modalDelegate, - didEndSelector, - didDismissSelector, - contextInfo, - [msg stringByReplacingOccurrencesOfString:@"%" withString:@"%%"] - ); - + NSButton *aButton; + + // Set up an NSAlert with the supplied details + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:title]; + aButton = [alert addButtonWithTitle:defaultButton]; + [aButton setTag:NSAlertDefaultReturn]; + + // Add 'alternate' and 'other' buttons as appropriate + if (alternateButton) { + aButton = [alert addButtonWithTitle:alternateButton]; + [aButton setTag:NSAlertAlternateReturn]; + } + if (otherButton) { + aButton = [alert addButtonWithTitle:otherButton]; + [aButton setTag:NSAlertOtherReturn]; + } + + // Set the informative message if supplied + if (msg) [alert setInformativeText:msg]; + + // Run the alert on the main thread + [[alert onMainThread] beginSheetModalForWindow:docWindow modalDelegate:modalDelegate didEndSelector:didEndSelector contextInfo:contextInfo]; + + // Ensure the alerting window is frontmost [[docWindow onMainThread] makeKeyWindow]; } diff --git a/Source/SPConnectionController.h b/Source/SPConnectionController.h index b1cc541e..7e518e26 100644 --- a/Source/SPConnectionController.h +++ b/Source/SPConnectionController.h @@ -134,7 +134,6 @@ - (void)initiateMySQLConnection; - (void)cancelConnection; - (void)failConnectionWithTitle:(NSString *)theTitle errorMessage:(NSString *)theErrorMessage detail:(NSString *)errorDetail; -- (void)errorSheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo; - (void)addConnectionToDocument; // Interface interaction diff --git a/Source/SPConnectionController.m b/Source/SPConnectionController.m index f2d5fcdd..933accfb 100644 --- a/Source/SPConnectionController.m +++ b/Source/SPConnectionController.m @@ -153,13 +153,13 @@ { // Ensure that host is not empty if this is a TCP/IP or SSH connection if (([self type] == SPTCPIPConnection || [self type] == SPSSHTunnelConnection) && ![[self host] length]) { - SPBeginAlertSheet(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, documentWindow, self, nil, nil, nil, NSLocalizedString(@"Insufficient details provided to establish a connection. Please enter at least the hostname.", @"insufficient details informative message")); + SPBeginAlertSheet(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, documentWindow, self, nil, nil, NSLocalizedString(@"Insufficient details provided to establish a connection. Please enter at least the hostname.", @"insufficient details informative message")); return; } // If SSH is enabled, ensure that the SSH host is not nil if ([self type] == SPSSHTunnelConnection && ![[self sshHost] length]) { - SPBeginAlertSheet(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, documentWindow, self, nil, nil, nil, NSLocalizedString(@"Insufficient details provided to establish a connection. Please enter the hostname for the SSH Tunnel, or disable the SSH Tunnel.", @"insufficient SSH tunnel details informative message")); + SPBeginAlertSheet(NSLocalizedString(@"Insufficient connection details", @"insufficient details message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, documentWindow, self, nil, nil, NSLocalizedString(@"Insufficient details provided to establish a connection. Please enter the hostname for the SSH Tunnel, or disable the SSH Tunnel.", @"insufficient SSH tunnel details informative message")); return; } @@ -438,16 +438,15 @@ // Only display the connection error message if there is a window visible if ([documentWindow isVisible]) { - SPBeginAlertSheet(theTitle, NSLocalizedString(@"OK", @"OK button"), (errorDetail) ? NSLocalizedString(@"Show Detail", @"Show detail button") : nil, (isSSHTunnelBindError) ? NSLocalizedString(@"Use Standard Connection", @"use standard connection button") : nil, documentWindow, self, nil, @selector(errorSheetDidEnd:returnCode:contextInfo:), @"connect", theErrorMessage); + SPBeginAlertSheet(theTitle, NSLocalizedString(@"OK", @"OK button"), (errorDetail) ? NSLocalizedString(@"Show Detail", @"Show detail button") : nil, (isSSHTunnelBindError) ? NSLocalizedString(@"Use Standard Connection", @"use standard connection button") : nil, documentWindow, self, @selector(connectionFailureSheetDidEnd:returnCode:contextInfo:), @"connect", theErrorMessage); } } /** * Alert sheet callback method - invoked when an error sheet is closed. */ -- (void)errorSheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo +- (void)connectionFailureSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - [sheet orderOut:self]; // Restore the passwords from keychain for editing if appropriate if (connectionKeychainItemName) { @@ -632,7 +631,6 @@ documentWindow, // Window to attach to self, // Modal delegate @selector(localhostErrorSheetDidEnd:returnCode:contextInfo:), // Did end selector - nil, // Did dismiss selector nil, // Contextual info for selectors NSLocalizedString(@"To MySQL, 'localhost' is a special host and means that a socket connection should be used.\n\nDid you mean to use a socket connection, or to connect to the local machine via a port? If you meant to connect via a port, '127.0.0.1' should be used instead of 'localhost'.", @"message of error when using 'localhost' for a network connection")); return NO; @@ -644,9 +642,8 @@ /** * Alert sheet callback method - invoked when the error sheet is closed. */ -- (void)localhostErrorSheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo +- (void)localhostErrorSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - [sheet orderOut:self]; if (returnCode == NSAlertAlternateReturn) { [self setType:SPSocketConnection]; [self setHost:@""]; diff --git a/Source/SPConnectionDelegate.m b/Source/SPConnectionDelegate.m index ae67697c..258b4402 100644 --- a/Source/SPConnectionDelegate.m +++ b/Source/SPConnectionDelegate.m @@ -28,6 +28,7 @@ #import "SPQueryController.h" #import "SPKeychain.h" #import "SPConstants.h" +#import "SPAlertSheets.h" @implementation TableDocument (SPConnectionDelegate) @@ -102,7 +103,7 @@ */ - (void)noConnectionAvailable:(id)connection { - NSBeginAlertSheet(NSLocalizedString(@"No connection available", @"no connection available message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"An error has occured and there doesn't seem to be a connection available.", @"no connection available informatie message")); + SPBeginAlertSheet(NSLocalizedString(@"No connection available", @"no connection available message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"An error has occured and there doesn't seem to be a connection available.", @"no connection available informatie message")); } /** @@ -131,6 +132,16 @@ return connectionErrorCode; } +/** + * Invoke to display an informative but non-fatal error directly to the user. + */ +- (void)showErrorWithTitle:(NSString *)theTitle message:(NSString *)theMessage +{ + if ([tableWindow isVisible]) { + SPBeginAlertSheet(theTitle, NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, theMessage); + } +} + /** * Invoked when user dismisses the error sheet displayed as a result of the current connection being lost. */ diff --git a/Source/SPDatabaseCopy.m b/Source/SPDatabaseCopy.m index 2d5f92c3..04b801b3 100644 --- a/Source/SPDatabaseCopy.m +++ b/Source/SPDatabaseCopy.m @@ -54,7 +54,7 @@ tables = [connection listTablesFromDB:sourceDatabaseName]; } else { SPBeginAlertSheet(NSLocalizedString(@"Cannot create existing database", @"create database exists error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to create the target database.\n\nDatabase %@ already exists.", @"create database error informative message"), targetDatabaseName]); @@ -86,7 +86,7 @@ if ([connection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Failed to create database", @"create database error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to create the target database.\n\nMySQL said: %@", @"create database error informative message"), [connection getLastErrorMessage]]); diff --git a/Source/SPDatabaseInfo.m b/Source/SPDatabaseInfo.m index 883ebef2..551a1c77 100644 --- a/Source/SPDatabaseInfo.m +++ b/Source/SPDatabaseInfo.m @@ -50,7 +50,7 @@ if ([connection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Failed to retrieve databases list", @"database list error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to retrieve a list of databases.\n\nMySQL said: %@", @"database list error informative message"), [connection getLastErrorMessage]]); diff --git a/Source/SPDatabaseRename.m b/Source/SPDatabaseRename.m index 26a983e5..abae5867 100644 --- a/Source/SPDatabaseRename.m +++ b/Source/SPDatabaseRename.m @@ -55,7 +55,7 @@ tables = [connection listTablesFromDB:sourceDatabaseName]; } else { SPBeginAlertSheet(NSLocalizedString(@"Cannot create existing database", @"create database exists error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to create the target database.\n\nDatabase %@ already exists.", @"create database error informative message"), targetDatabaseName]); @@ -80,7 +80,7 @@ [self dropDatabase:sourceDatabaseName]; } else { SPBeginAlertSheet(NSLocalizedString(@"Failed to delete database", @"delete database error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Database %@ not empty, skipping drop database.", @"delete database not empty error informative message"), sourceDatabaseName]); @@ -94,7 +94,7 @@ [connection queryString:createStatement]; if ([connection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Failed to create database", @"create database error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to create a database.\n\nMySQL said: %@", @"create database error informative message"), [connection getLastErrorMessage]]); @@ -110,7 +110,7 @@ [connection queryString:dropStatement]; if ([connection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Failed to drop database", @"drop database error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to drop a database.\n\nMySQL said: %@", @"drop database error informative message"), [connection getLastErrorMessage]]); diff --git a/Source/SPExtendedTableInfo.m b/Source/SPExtendedTableInfo.m index f272a3e5..d6b20d9a 100644 --- a/Source/SPExtendedTableInfo.m +++ b/Source/SPExtendedTableInfo.m @@ -110,7 +110,7 @@ [sender selectItemWithTitle:currentType]; SPBeginAlertSheet(NSLocalizedString(@"Error changing table type", @"error changing table type message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred when trying to change the table type to '%@'.\n\nMySQL said: %@", @"error changing table type informative message"), newType, [connection getLastErrorMessage]]); } } @@ -137,7 +137,7 @@ [sender selectItemWithTitle:currentEncoding]; SPBeginAlertSheet(NSLocalizedString(@"Error changing table encoding", @"error changing table encoding message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred when trying to change the table encoding to '%@'.\n\nMySQL said: %@", @"error changing table encoding informative message"), newEncoding, [connection getLastErrorMessage]]); } } @@ -164,7 +164,7 @@ [sender selectItemWithTitle:currentCollation]; SPBeginAlertSheet(NSLocalizedString(@"Error changing table collation", @"error changing table collation message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred when trying to change the table collation to '%@'.\n\nMySQL said: %@", @"error changing table collation informative message"), newCollation, [connection getLastErrorMessage]]); } } @@ -454,7 +454,7 @@ } else { SPBeginAlertSheet(NSLocalizedString(@"Error changing table comment", @"error changing table comment message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred when trying to change the table's comment to '%@'.\n\nMySQL said: %@", @"error changing table comment informative message"), newComment, [connection getLastErrorMessage]]); } } diff --git a/Source/SPFieldMapperController.m b/Source/SPFieldMapperController.m index a3d17f1c..290040a8 100644 --- a/Source/SPFieldMapperController.m +++ b/Source/SPFieldMapperController.m @@ -785,7 +785,7 @@ - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - [sheet orderOut:self]; + if ([sheet respondsToSelector:@selector(orderOut:)]) [sheet orderOut:nil]; if (sheet == globalValuesSheet) [self updateFieldMappingButtonCell]; } diff --git a/Source/SPKeychain.m b/Source/SPKeychain.m index 688091eb..335c0a0b 100644 --- a/Source/SPKeychain.m +++ b/Source/SPKeychain.m @@ -24,6 +24,7 @@ // More info at #import "SPKeychain.h" +#import "SPAlertSheets.h" #import #import @@ -101,10 +102,10 @@ if (status != noErr) { NSLog(@"Error (%i) while trying to add password for name: %@ account: %@", status, name, account); - NSBeginAlertSheet(NSLocalizedString(@"Error adding password to Keychain", @"error adding password to keychain message"), + SPBeginAlertSheet(NSLocalizedString(@"Error adding password to Keychain", @"error adding password to keychain message"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], self, nil, nil, nil, - NSLocalizedString(@"An error occured while trying to add the password to your Keychain. Repairing your Keychain might resolve this, but if it doesn't please report it to the Sequel Pro team, supplying the error code %i.", @"error adding password to keychain informative message"), status); + nil, nil, [NSApp mainWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to add the password to your Keychain. Repairing your Keychain might resolve this, but if it doesn't please report it to the Sequel Pro team, supplying the error code %i.", @"error adding password to keychain informative message"), status]); } } } diff --git a/Source/SPProcessListController.m b/Source/SPProcessListController.m index 07c67247..dad68d18 100644 --- a/Source/SPProcessListController.m +++ b/Source/SPProcessListController.m @@ -655,7 +655,7 @@ // Check for errors if ([connection queryErrored]) { - SPBeginAlertSheet(NSLocalizedString(@"Unable to kill query", @"error killing query message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Unable to kill query", @"error killing query message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while attempting to kill the query associated with connection %lu.\n\nMySQL said: %@", @"error killing query informative message"), (unsigned long)processId, [connection getLastErrorMessage]]); } @@ -673,7 +673,7 @@ // Check for errors if ([connection queryErrored]) { - SPBeginAlertSheet(NSLocalizedString(@"Unable to kill connection", @"error killing connection message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Unable to kill connection", @"error killing connection message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while attempting to kill connection %lu.\n\nMySQL said: %@", @"error killing query informative message"), (unsigned long)processId, [connection getLastErrorMessage]]); } diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m index 2b7c31fb..6d9e64ca 100644 --- a/Source/SPSSHTunnel.m +++ b/Source/SPSSHTunnel.m @@ -28,6 +28,7 @@ #import "SPKeychain.h" #import "SPConstants.h" #import "SPMainThreadTrampoline.h" +#import "SPAlertSheets.h" #import diff --git a/Source/SPTableCopy.m b/Source/SPTableCopy.m index ab485b60..d868c9ad 100644 --- a/Source/SPTableCopy.m +++ b/Source/SPTableCopy.m @@ -36,7 +36,7 @@ if ([connection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Failed to show create table statement", @"show create table error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to retrieve the create table statement for a table.\n\nMySQL said: %@", @"show create table error informative message"), [connection getLastErrorMessage]]); @@ -74,7 +74,7 @@ if ([connection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Failed to copy table", @"copy table error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, messageWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to copy a table.\n\nMySQL said: %@", @"copy table error informative message"), [connection getLastErrorMessage]]); @@ -99,7 +99,7 @@ if ([connection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Failed to copy table data", @"copy table data error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, [self getTableWindow], self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, [self getTableWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to copy a table's data.\n\nMySQL said: %@", @"copy table data error informative message"), [connection getLastErrorMessage]]); @@ -126,7 +126,7 @@ if ([connection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Failed to move table", @"move table error message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, [self getTableWindow], self, nil, nil, nil, + NSLocalizedString(@"OK", @"OK button"), nil, nil, [self getTableWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to move a table.\n\nMySQL said: %@", @"move table error informative message"), [connection getLastErrorMessage]]); diff --git a/Source/SPTableData.m b/Source/SPTableData.m index ab1857f6..feb77848 100644 --- a/Source/SPTableData.m +++ b/Source/SPTableData.m @@ -352,7 +352,7 @@ if ([mySQLConnection queryErrored]) { if ([mySQLConnection isConnected]) { SPBeginAlertSheet(NSLocalizedString(@"Error retrieving table information", @"error retrieving table information message"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], self, nil, nil, nil, + nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred while retrieving the information for table '%@'. Please try again.\n\nMySQL said: %@", @"error retrieving table information informative message"), tableName, [mySQLConnection getLastErrorMessage]]); // If the current table doesn't exist anymore reload table list @@ -702,7 +702,7 @@ if ([mySQLConnection queryErrored]) { if ([mySQLConnection isConnected]) { SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], self, nil, nil, nil, + nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred while retrieving information.\nMySQL said: %@", @"message of panel when retrieving information failed"), [mySQLConnection getLastErrorMessage]]); } @@ -734,7 +734,7 @@ if ([mySQLConnection queryErrored]) { if ([mySQLConnection isConnected]) { SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], self, nil, nil, nil, + nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred while retrieving information.\nMySQL said: %@", @"message of panel when retrieving information failed"), [mySQLConnection getLastErrorMessage]]); } @@ -832,7 +832,7 @@ if ([mySQLConnection queryErrored]) { if ([mySQLConnection isConnected]) { SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], self, nil, nil, nil, + nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while retrieving status data.\nMySQL said: %@", @"message of panel when retrieving view information failed"), [mySQLConnection getLastErrorMessage]]); } @@ -882,7 +882,7 @@ if ([mySQLConnection queryErrored]) { if ([mySQLConnection isConnected]) { SPBeginAlertSheet(NSLocalizedString(@"Error retrieving trigger information", @"error retrieving trigger information message"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], self, nil, nil, nil, + nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred while retrieving the trigger information for table '%@'. Please try again.\n\nMySQL said: %@", @"error retrieving table information informative message"), [tableListInstance tableName], [mySQLConnection getLastErrorMessage]]); if (triggers) [triggers release], triggers = nil; diff --git a/Source/SPTableRelations.m b/Source/SPTableRelations.m index 7baa8702..586f341d 100644 --- a/Source/SPTableRelations.m +++ b/Source/SPTableRelations.m @@ -136,7 +136,7 @@ if ([connection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Error creating relation", @"error creating relation message"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], nil, nil, nil, nil, + nil, nil, [NSApp mainWindow], nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"The specified relation was unable to be created.\n\nMySQL said: %@", @"error creating relation informative message"), [connection getLastErrorMessage]]); } else { @@ -405,7 +405,7 @@ SPBeginAlertSheet(NSLocalizedString(@"Unable to delete relation", @"error deleting relation message"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], nil, nil, nil, nil, + nil, nil, [NSApp mainWindow], nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"The selected relation couldn't be deleted.\n\nMySQL said: %@", @"error deleting relation informative message"), [connection getLastErrorMessage]]); // Abort loop diff --git a/Source/SPTableTriggers.m b/Source/SPTableTriggers.m index c202e9c9..7a7cf8ac 100644 --- a/Source/SPTableTriggers.m +++ b/Source/SPTableTriggers.m @@ -166,7 +166,7 @@ if (([connection queryErrored])) { SPBeginAlertSheet(NSLocalizedString(@"Error creating trigger", @"error creating trigger message"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], nil, nil, nil, nil, + nil, nil, [NSApp mainWindow], nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"The specified trigger was unable to be created.\n\nMySQL said: %@", @"error creating trigger informative message"), [connection getLastErrorMessage]]); } else { @@ -322,7 +322,7 @@ SPBeginAlertSheet(NSLocalizedString(@"Unable to delete trigger", @"error deleting trigger message"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], nil, nil, nil, nil, + nil, nil, [NSApp mainWindow], nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"The selected trigger couldn't be deleted.\n\nMySQL said: %@", @"error deleting trigger informative message"), [connection getLastErrorMessage]]); // Abort loop diff --git a/Source/TableContent.h b/Source/TableContent.h index d6fbf422..9b2e953e 100644 --- a/Source/TableContent.h +++ b/Source/TableContent.h @@ -146,7 +146,6 @@ - (NSString *)argumentForRow:(NSInteger)row; - (BOOL)tableContainsBlobOrTextColumns; - (NSString *)fieldListForQuery; -- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo; - (void)updateNumberOfRows; - (NSInteger)fetchNumberOfRows; - (BOOL)saveRowOnDeselect; diff --git a/Source/TableContent.m b/Source/TableContent.m index 144f81de..096e18bb 100644 --- a/Source/TableContent.m +++ b/Source/TableContent.m @@ -1321,7 +1321,7 @@ if ( [tableContentView numberOfSelectedRows] < 1 ) return; if ( [tableContentView numberOfSelectedRows] > 1 ) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"You can only copy single rows.", @"message of panel when trying to copy multiple rows")); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"You can only copy single rows.", @"message of panel when trying to copy multiple rows")); return; } @@ -1427,211 +1427,504 @@ [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete the selected %ld rows from this table? This action cannot be undone.", @"delete rows informative message"), (long)[tableContentView numberOfSelectedRows]]]; } - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; -} - -// Accessors - -/** - * Returns the current result (as shown in table content view) as array, the first object containing the field - * names as array, the following objects containing the rows as array. - */ -- (NSArray *)currentDataResult -{ - NSArray *tableColumns; - NSEnumerator *enumerator; - id tableColumn; - NSMutableArray *currentResult = [NSMutableArray array]; - NSMutableArray *tempRow = [NSMutableArray array]; - NSUInteger i; - - //load table if not already done - if ( ![tablesListInstance contentLoaded] ) { - [self loadTable:[tablesListInstance tableName]]; - } - - tableColumns = [tableContentView tableColumns]; - enumerator = [tableColumns objectEnumerator]; - - //set field names as first line - while ( (tableColumn = [enumerator nextObject]) ) { - [tempRow addObject:[[tableColumn headerCell] stringValue]]; - } - [currentResult addObject:[NSArray arrayWithArray:tempRow]]; - - //add rows - for ( i = 0 ; i < [self numberOfRowsInTableView:nil] ; i++) { - [tempRow removeAllObjects]; - enumerator = [tableColumns objectEnumerator]; - while ( (tableColumn = [enumerator nextObject]) ) { - id o = SPDataStorageObjectAtRowAndColumn(tableValues, i, [[tableColumn identifier] integerValue]); - if([o isNSNull]) - [tempRow addObject:@"NULL"]; - else if ([o isSPNotLoaded]) - [tempRow addObject:NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields")]; - else if([o isKindOfClass:[NSString class]]) - [tempRow addObject:[o description]]; - else { - NSImage *image = [[NSImage alloc] initWithData:o]; - if(image) { - NSInteger imageWidth = [image size].width; - if (imageWidth > 100) imageWidth = 100; - [tempRow addObject:[NSString stringWithFormat: - @"", - (long)imageWidth, - [[image TIFFRepresentationUsingCompression:NSTIFFCompressionJPEG factor:0.01] base64EncodingWithLineLength:0]]]; - } else { - [tempRow addObject:@"<BLOB>"]; - } - if(image) [image release]; - } - } - [currentResult addObject:[NSArray arrayWithArray:tempRow]]; - } - return currentResult; -} - -/** - * Returns the current result (as shown in table content view) as array, the first object containing the field - * names as array, the following objects containing the rows as array. - */ -- (NSArray *)currentResult -{ - NSArray *tableColumns; - NSEnumerator *enumerator; - id tableColumn; - NSMutableArray *currentResult = [NSMutableArray array]; - NSMutableArray *tempRow = [NSMutableArray array]; - NSUInteger i; - - //load table if not already done - if ( ![tablesListInstance contentLoaded] ) { - [self loadTable:[tablesListInstance tableName]]; - } - - tableColumns = [tableContentView tableColumns]; - enumerator = [tableColumns objectEnumerator]; - - //set field names as first line - while ( (tableColumn = [enumerator nextObject]) ) { - [tempRow addObject:[[tableColumn headerCell] stringValue]]; - } - [currentResult addObject:[NSArray arrayWithArray:tempRow]]; - - //add rows - for ( i = 0 ; i < [self numberOfRowsInTableView:nil] ; i++) { - [tempRow removeAllObjects]; - enumerator = [tableColumns objectEnumerator]; - while ( (tableColumn = [enumerator nextObject]) ) { - [tempRow addObject:[self tableView:nil objectValueForTableColumn:tableColumn row:i]]; - } - [currentResult addObject:[NSArray arrayWithArray:tempRow]]; - } - return currentResult; -} - -// Additional methods - -/** - * Sets the connection (received from TableDocument) and makes things that have to be done only once - */ -- (void)setConnection:(MCPConnection *)theConnection -{ - mySQLConnection = theConnection; - - [tableContentView setVerticalMotionCanBeginDrag:NO]; + [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(removeRowSheetDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; } /** - * Performs the requested action - switching to another table - * with the appropriate filter settings - when a link arrow is - * selected. + * Perform the requested row deletion action. */ -- (void)clickLinkArrow:(SPTextAndLinkCell *)theArrowCell +- (void)removeRowSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - if ([tableDocumentInstance isWorking]) return; - - if ([theArrowCell getClickedColumn] == NSNotFound || [theArrowCell getClickedRow] == NSNotFound) return; - // Check whether a save of the current row is required. - if ( ![self saveRowOnDeselect] ) return; + NSMutableIndexSet *selectedRows = [NSMutableIndexSet indexSet]; + NSString *wherePart; + NSInteger i, errors; + BOOL consoleUpdateStatus; + BOOL reloadAfterRemovingRow = [prefs boolForKey:SPReloadAfterRemovingRow]; - // If on the main thread, fire up a thread to perform the load while keeping the modification flag - [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Loading reference...", @"Loading referece task string")]; - if ([NSThread isMainThread]) { - [NSThread detachNewThreadSelector:@selector(clickLinkArrowTask:) toTarget:self withObject:theArrowCell]; - } else { - [self clickLinkArrowTask:theArrowCell]; - } -} -- (void)clickLinkArrowTask:(SPTextAndLinkCell *)theArrowCell -{ - NSAutoreleasePool *linkPool = [[NSAutoreleasePool alloc] init]; - NSUInteger dataColumnIndex = [[[[tableContentView tableColumns] objectAtIndex:[theArrowCell getClickedColumn]] identifier] integerValue]; - BOOL tableFilterRequired = NO; + // Order out current sheet to suppress overlapping of sheets + [[alert window] orderOut:nil]; - // Ensure the clicked cell has foreign key details available - NSDictionary *refDictionary = [[dataColumns objectAtIndex:dataColumnIndex] objectForKey:@"foreignkeyreference"]; - if (!refDictionary) { - [linkPool release]; - return; - } + if ( [contextInfo isEqualToString:@"removeallrows"] ) { + if ( returnCode == NSAlertDefaultReturn ) { + //check if the user is currently editing a row + if (isEditingRow) { + //cancel the edit + isEditingRow = NO; + // in case the delete fails, make sure we at least stay in a somewhat consistent state + [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow]; + currentlyEditingRow = -1; + } + + [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM %@", [selectedTable backtickQuotedString]]]; + if ( ![mySQLConnection queryErrored] ) { - // Save existing scroll position and details and mark that state is being modified - [spHistoryControllerInstance updateHistoryEntries]; - [spHistoryControllerInstance setModifyingState:YES]; + // Reset auto increment if suppression button was ticked + if([[alert suppressionButton] state] == NSOnState) + [tableSourceInstance setAutoIncrementTo:@"1"]; - NSString *targetFilterValue = [tableValues cellDataAtRow:[theArrowCell getClickedRow] column:dataColumnIndex]; + [self reloadTable:self]; - // If the link is within the current table, apply filter settings manually - if ([[refDictionary objectForKey:@"table"] isEqualToString:selectedTable]) { - [fieldField selectItemWithTitle:[refDictionary objectForKey:@"column"]]; - [self setCompareTypes:self]; - if ([targetFilterValue isNSNull]) { - [compareField selectItemWithTitle:@"IS NULL"]; - } else { - [argumentField setStringValue:targetFilterValue]; + } else { + [self performSelector:@selector(showErrorSheetWith:) + withObject:[NSArray arrayWithObjects:NSLocalizedString(@"Error", @"error"), + [NSString stringWithFormat:NSLocalizedString(@"Couldn't delete rows.\n\nMySQL said: %@", @"message when deleteing all rows failed"), + [mySQLConnection getLastErrorMessage]], + nil] + afterDelay:0.3]; + } } - tableFilterRequired = YES; - } else { + } else if ( [contextInfo isEqualToString:@"removerow"] ) { + if ( returnCode == NSAlertDefaultReturn ) { + [selectedRows addIndexes:[tableContentView selectedRowIndexes]]; - // Store the filter details to use when loading the target table - NSDictionary *filterSettings = [NSDictionary dictionaryWithObjectsAndKeys: - [refDictionary objectForKey:@"column"], @"filterField", - targetFilterValue, @"filterValue", - ([targetFilterValue isNSNull]?@"IS NULL":nil), @"filterComparison", - nil]; - [self setFiltersToRestore:filterSettings]; + //check if the user is currently editing a row + if (isEditingRow) { + //make sure that only one row is selected. This should never happen + if ([selectedRows count]!=1) { + NSLog(@"Expected only one selected row, but found %d",[selectedRows count]); + } + // this code is pretty much taken from the escape key handler + if ( isEditingNewRow ) { + // since the user is currently editing a new row, we don't actually have to delete any rows from the database + // we just have to remove the row from the view (and the store) + isEditingRow = NO; + isEditingNewRow = NO; + tableRowsCount--; + [tableValues removeRowAtIndex:currentlyEditingRow]; + currentlyEditingRow = -1; + [self updateCountText]; + [tableContentView reloadData]; + + //deselect the row + [tableContentView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; - // Attempt to switch to the target table - if (![tablesListInstance selectItemWithName:[refDictionary objectForKey:@"table"]]) { - NSBeep(); - [self setFiltersToRestore:nil]; - } - } + // we also don't have to reload the table, since no query went to the database + return; + } else { + //cancel the edit + isEditingRow = NO; + // in case the delete fails, make sure we at least stay in a somewhat consistent state + [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow]; + currentlyEditingRow = -1; + } - // End state and ensure a new history entry - [spHistoryControllerInstance setModifyingState:NO]; - [spHistoryControllerInstance updateHistoryEntries]; + } + [tableContentView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; + + errors = 0; - // End the task - [tableDocumentInstance endTask]; + // Disable updating of the Console Log window for large number of queries + // to speed the deletion + consoleUpdateStatus = [[SPQueryController sharedQueryController] allowConsoleUpdate]; + if([selectedRows count] > 10) + [[SPQueryController sharedQueryController] setAllowConsoleUpdate:NO]; - // If the same table is the target, trigger a filter task on the main thread - if (tableFilterRequired) - [self performSelectorOnMainThread:@selector(filterTable:) withObject:self waitUntilDone:NO]; - - // Empty the loading pool and exit the thread - [linkPool drain]; -} + NSUInteger index = [selectedRows firstIndex]; -/** - * Sets the compare types for the filter and the appropriate formatter for the textField - */ -- (IBAction)setCompareTypes:(id)sender -{ + NSArray *primaryKeyFieldNames = [tableDataInstance primaryKeyColumnNames]; - if(contentFilters == nil + // If no PRIMARY KEY is found and numberOfSelectedRows > 3 then + // check for uniqueness of rows via combining all column values; + // if unique then use the all columns as 'primary keys' + if([selectedRows count] >3 && primaryKeyFieldNames == nil) { + primaryKeyFieldNames = [tableDataInstance columnNames]; + + NSInteger numberOfRows = 0; + // Get the number of rows in the table + MCPResult *r; + r = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@", [selectedTable backtickQuotedString]]]; + if (![mySQLConnection queryErrored]) { + NSArray *a = [r fetchRowAsArray]; + if([a count]) + numberOfRows = [[a objectAtIndex:0] integerValue]; + } + // Check for uniqueness via LIMIT numberOfRows-1,numberOfRows for speed + if(numberOfRows > 0) { + [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM %@ GROUP BY %@ LIMIT %ld,%ld", [selectedTable backtickQuotedString], [primaryKeyFieldNames componentsJoinedAndBacktickQuoted], (long)(numberOfRows-1), (long)numberOfRows]]; + if([mySQLConnection affectedRows] == 0) + primaryKeyFieldNames = nil; + } else { + primaryKeyFieldNames = nil; + } + } + + if(primaryKeyFieldNames == nil) { + // delete row by row + while (index != NSNotFound) { + + wherePart = [NSString stringWithString:[self argumentForRow:index]]; + + //argumentForRow might return empty query, in which case we shouldn't execute the partial query + if([wherePart length]) { + [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@", [selectedTable backtickQuotedString], wherePart]]; + + // Check for errors + if ( ![mySQLConnection affectedRows] || [mySQLConnection queryErrored]) { + // If error delete that index from selectedRows for reloading table if + // "ReloadAfterRemovingRow" is disbaled + if(!reloadAfterRemovingRow) + [selectedRows removeIndex:index]; + errors++; + } + } else { + if(!reloadAfterRemovingRow) + [selectedRows removeIndex:index]; + errors++; + } + index = [selectedRows indexGreaterThanIndex:index]; + } + } else if ([primaryKeyFieldNames count] == 1) { + // if table has only one PRIMARY KEY + // delete the fast way by using the PRIMARY KEY in an IN clause + NSMutableString *deleteQuery = [NSMutableString string]; + NSInteger affectedRows = 0; + + [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ IN (", [selectedTable backtickQuotedString], [NSArrayObjectAtIndex(primaryKeyFieldNames,0) backtickQuotedString]]]; + + while (index != NSNotFound) { + + id keyValue = [tableValues cellDataAtRow:index column:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(primaryKeyFieldNames,0)] objectForKey:@"datacolumnindex"] integerValue]]; + + if([keyValue isKindOfClass:[NSData class]]) + [deleteQuery appendString:[NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:keyValue]]]; + else + [deleteQuery appendString:[NSString stringWithFormat:@"'%@'", [keyValue description]]]; + + // Split deletion query into 256k chunks + if([deleteQuery length] > 256000) { + [deleteQuery appendString:@")"]; + [mySQLConnection queryString:deleteQuery]; + // Remember affected rows for error checking + affectedRows += [mySQLConnection affectedRows]; + // Reinit a new deletion query + [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ IN (", [selectedTable backtickQuotedString], [NSArrayObjectAtIndex(primaryKeyFieldNames,0) backtickQuotedString]]]; + } else { + [deleteQuery appendString:@","]; + } + + index = [selectedRows indexGreaterThanIndex:index]; + } + + // Check if deleteQuery's maximal length was reached for the last index + // if yes omit the empty query + if(![deleteQuery hasSuffix:@"("]) { + // Replace final , by ) and delete the remaining rows + [deleteQuery setString:[NSString stringWithFormat:@"%@)", [deleteQuery substringToIndex:([deleteQuery length]-1)]]]; + [mySQLConnection queryString:deleteQuery]; + // Remember affected rows for error checking + affectedRows += [mySQLConnection affectedRows]; + } + + errors = (affectedRows > 0) ? [selectedRows count] - affectedRows : [selectedRows count]; + } else { + // if table has more than one PRIMARY KEY + // delete the row by using all PRIMARY KEYs in an OR clause + NSMutableString *deleteQuery = [NSMutableString string]; + NSInteger affectedRows = 0; + + [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE ", [selectedTable backtickQuotedString]]]; + + while (index != NSNotFound) { + + // Build the AND clause of PRIMARY KEYS + [deleteQuery appendString:@"("]; + for(NSString *primaryKeyFieldName in primaryKeyFieldNames) { + + id keyValue = [tableValues cellDataAtRow:index column:[[[tableDataInstance columnWithName:primaryKeyFieldName] objectForKey:@"datacolumnindex"] integerValue]]; + + [deleteQuery appendString:[primaryKeyFieldName backtickQuotedString]]; + if ([keyValue isKindOfClass:[NSData class]]) { + [deleteQuery appendString:@"=X'"]; + [deleteQuery appendString:[mySQLConnection prepareBinaryData:keyValue]]; + } else { + [deleteQuery appendString:@"='"]; + [deleteQuery appendString:[mySQLConnection prepareString:[keyValue description]]]; + } + [deleteQuery appendString:@"' AND "]; + } + + // Remove the trailing AND and add the closing bracket + [deleteQuery deleteCharactersInRange:NSMakeRange([deleteQuery length]-5, 5)]; + [deleteQuery appendString:@")"]; + + // Split deletion query into 64k chunks + if([deleteQuery length] > 64000) { + [mySQLConnection queryString:deleteQuery]; + // Remember affected rows for error checking + affectedRows += [mySQLConnection affectedRows]; + // Reinit a new deletion query + [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE ", [selectedTable backtickQuotedString]]]; + } else { + [deleteQuery appendString:@" OR "]; + } + + index = [selectedRows indexGreaterThanIndex:index]; + } + + // Check if deleteQuery's maximal length was reached for the last index + // if yes omit the empty query + if(![deleteQuery hasSuffix:@"WHERE "]) { + // Remove final ' OR ' and delete the remaining rows + [deleteQuery setString:[deleteQuery substringToIndex:([deleteQuery length]-4)]]; + [mySQLConnection queryString:deleteQuery]; + // Remember affected rows for error checking + affectedRows += [mySQLConnection affectedRows]; + } + + errors = (affectedRows > 0) ? [selectedRows count] - affectedRows : [selectedRows count]; + } + + // Restore Console Log window's updating bahaviour + [[SPQueryController sharedQueryController] setAllowConsoleUpdate:consoleUpdateStatus]; + + if (errors) { + NSArray *message; + //TODO: The following three messages are NOT localisable! + if (errors < 0) { + message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"), + [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ more %@ deleted! Please check the Console and inform the Sequel Pro team!", @"message of panel when more rows were deleted"), (long)(errors*-1), ((errors*-1)>1)?@"s":@"", (errors>1)?@"were":@"was"], + nil]; + } + else { + if (primaryKeyFieldNames == nil) + message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"), + [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ ha%@ not been deleted. Reload the table to be sure that the rows exist and use a primary key for your table.", @"message of panel when not all selected fields have been deleted"), (long)errors, (errors>1)?@"s":@"", (errors>1)?@"ve":@"s"], + nil]; + else + message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"), + [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ ha%@ not been deleted. Reload the table to be sure that the rows exist and check the Console for possible errors inside the primary key%@ for your table.", @"message of panel when not all selected fields have been deleted by using primary keys"), (long)errors, (errors>1)?@"s":@"", (errors>1)?@"ve":@"s", (errors>1)?@"s":@""], + nil]; + } + + [self performSelector:@selector(showErrorSheetWith:) + withObject:message + afterDelay:0.3]; + } + + // Refresh table content + if ( errors || reloadAfterRemovingRow ) { + previousTableRowsCount = tableRowsCount; + [self loadTableValues]; + } else { + for ( i = tableRowsCount - 1 ; i >= 0 ; i-- ) { + if ([selectedRows containsIndex:i]) [tableValues removeRowAtIndex:i]; + } + tableRowsCount = [tableValues count]; + [tableContentView reloadData]; + } + [tableContentView deselectAll:self]; + } else { + // The user clicked cancel in the "sure you wanna delete" message + // restore editing or whatever + } + + } +} + + +// Accessors + +/** + * Returns the current result (as shown in table content view) as array, the first object containing the field + * names as array, the following objects containing the rows as array. + */ +- (NSArray *)currentDataResult +{ + NSArray *tableColumns; + NSEnumerator *enumerator; + id tableColumn; + NSMutableArray *currentResult = [NSMutableArray array]; + NSMutableArray *tempRow = [NSMutableArray array]; + NSUInteger i; + + //load table if not already done + if ( ![tablesListInstance contentLoaded] ) { + [self loadTable:[tablesListInstance tableName]]; + } + + tableColumns = [tableContentView tableColumns]; + enumerator = [tableColumns objectEnumerator]; + + //set field names as first line + while ( (tableColumn = [enumerator nextObject]) ) { + [tempRow addObject:[[tableColumn headerCell] stringValue]]; + } + [currentResult addObject:[NSArray arrayWithArray:tempRow]]; + + //add rows + for ( i = 0 ; i < [self numberOfRowsInTableView:nil] ; i++) { + [tempRow removeAllObjects]; + enumerator = [tableColumns objectEnumerator]; + while ( (tableColumn = [enumerator nextObject]) ) { + id o = SPDataStorageObjectAtRowAndColumn(tableValues, i, [[tableColumn identifier] integerValue]); + if([o isNSNull]) + [tempRow addObject:@"NULL"]; + else if ([o isSPNotLoaded]) + [tempRow addObject:NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields")]; + else if([o isKindOfClass:[NSString class]]) + [tempRow addObject:[o description]]; + else { + NSImage *image = [[NSImage alloc] initWithData:o]; + if(image) { + NSInteger imageWidth = [image size].width; + if (imageWidth > 100) imageWidth = 100; + [tempRow addObject:[NSString stringWithFormat: + @"", + (long)imageWidth, + [[image TIFFRepresentationUsingCompression:NSTIFFCompressionJPEG factor:0.01] base64EncodingWithLineLength:0]]]; + } else { + [tempRow addObject:@"<BLOB>"]; + } + if(image) [image release]; + } + } + [currentResult addObject:[NSArray arrayWithArray:tempRow]]; + } + return currentResult; +} + +/** + * Returns the current result (as shown in table content view) as array, the first object containing the field + * names as array, the following objects containing the rows as array. + */ +- (NSArray *)currentResult +{ + NSArray *tableColumns; + NSEnumerator *enumerator; + id tableColumn; + NSMutableArray *currentResult = [NSMutableArray array]; + NSMutableArray *tempRow = [NSMutableArray array]; + NSUInteger i; + + //load table if not already done + if ( ![tablesListInstance contentLoaded] ) { + [self loadTable:[tablesListInstance tableName]]; + } + + tableColumns = [tableContentView tableColumns]; + enumerator = [tableColumns objectEnumerator]; + + //set field names as first line + while ( (tableColumn = [enumerator nextObject]) ) { + [tempRow addObject:[[tableColumn headerCell] stringValue]]; + } + [currentResult addObject:[NSArray arrayWithArray:tempRow]]; + + //add rows + for ( i = 0 ; i < [self numberOfRowsInTableView:nil] ; i++) { + [tempRow removeAllObjects]; + enumerator = [tableColumns objectEnumerator]; + while ( (tableColumn = [enumerator nextObject]) ) { + [tempRow addObject:[self tableView:nil objectValueForTableColumn:tableColumn row:i]]; + } + [currentResult addObject:[NSArray arrayWithArray:tempRow]]; + } + return currentResult; +} + +// Additional methods + +/** + * Sets the connection (received from TableDocument) and makes things that have to be done only once + */ +- (void)setConnection:(MCPConnection *)theConnection +{ + mySQLConnection = theConnection; + + [tableContentView setVerticalMotionCanBeginDrag:NO]; +} + +/** + * Performs the requested action - switching to another table + * with the appropriate filter settings - when a link arrow is + * selected. + */ +- (void)clickLinkArrow:(SPTextAndLinkCell *)theArrowCell +{ + if ([tableDocumentInstance isWorking]) return; + + if ([theArrowCell getClickedColumn] == NSNotFound || [theArrowCell getClickedRow] == NSNotFound) return; + + // Check whether a save of the current row is required. + if ( ![self saveRowOnDeselect] ) return; + + // If on the main thread, fire up a thread to perform the load while keeping the modification flag + [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Loading reference...", @"Loading referece task string")]; + if ([NSThread isMainThread]) { + [NSThread detachNewThreadSelector:@selector(clickLinkArrowTask:) toTarget:self withObject:theArrowCell]; + } else { + [self clickLinkArrowTask:theArrowCell]; + } +} +- (void)clickLinkArrowTask:(SPTextAndLinkCell *)theArrowCell +{ + NSAutoreleasePool *linkPool = [[NSAutoreleasePool alloc] init]; + NSUInteger dataColumnIndex = [[[[tableContentView tableColumns] objectAtIndex:[theArrowCell getClickedColumn]] identifier] integerValue]; + BOOL tableFilterRequired = NO; + + // Ensure the clicked cell has foreign key details available + NSDictionary *refDictionary = [[dataColumns objectAtIndex:dataColumnIndex] objectForKey:@"foreignkeyreference"]; + if (!refDictionary) { + [linkPool release]; + return; + } + + // Save existing scroll position and details and mark that state is being modified + [spHistoryControllerInstance updateHistoryEntries]; + [spHistoryControllerInstance setModifyingState:YES]; + + NSString *targetFilterValue = [tableValues cellDataAtRow:[theArrowCell getClickedRow] column:dataColumnIndex]; + + // If the link is within the current table, apply filter settings manually + if ([[refDictionary objectForKey:@"table"] isEqualToString:selectedTable]) { + [fieldField selectItemWithTitle:[refDictionary objectForKey:@"column"]]; + [self setCompareTypes:self]; + if ([targetFilterValue isNSNull]) { + [compareField selectItemWithTitle:@"IS NULL"]; + } else { + [argumentField setStringValue:targetFilterValue]; + } + tableFilterRequired = YES; + } else { + + // Store the filter details to use when loading the target table + NSDictionary *filterSettings = [NSDictionary dictionaryWithObjectsAndKeys: + [refDictionary objectForKey:@"column"], @"filterField", + targetFilterValue, @"filterValue", + ([targetFilterValue isNSNull]?@"IS NULL":nil), @"filterComparison", + nil]; + [self setFiltersToRestore:filterSettings]; + + // Attempt to switch to the target table + if (![tablesListInstance selectItemWithName:[refDictionary objectForKey:@"table"]]) { + NSBeep(); + [self setFiltersToRestore:nil]; + } + } + + // End state and ensure a new history entry + [spHistoryControllerInstance setModifyingState:NO]; + [spHistoryControllerInstance updateHistoryEntries]; + + // End the task + [tableDocumentInstance endTask]; + + // If the same table is the target, trigger a filter task on the main thread + if (tableFilterRequired) + [self performSelectorOnMainThread:@selector(filterTable:) withObject:self waitUntilDone:NO]; + + // Empty the loading pool and exit the thread + [linkPool drain]; +} + +/** + * Sets the compare types for the filter and the appropriate formatter for the textField + */ +- (IBAction)setCompareTypes:(id)sender +{ + + if(contentFilters == nil || ![contentFilters objectForKey:@"number"] || ![contentFilters objectForKey:@"string"] || ![contentFilters objectForKey:@"date"]) { @@ -1908,7 +2201,7 @@ // If no rows have been changed, show error if appropriate. if ( ![mySQLConnection affectedRows] && ![mySQLConnection queryErrored] ) { if ( [prefs boolForKey:SPShowNoAffectedRowsError] ) { - SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"The row was not written to the MySQL database. You probably haven't changed anything.\nReload the table to be sure that the row exists and use a primary key for your table.\n(This error can be turned off in the preferences.)", @"message of panel when no rows have been affected after writing to the db")); } else { NSBeep(); @@ -1955,15 +2248,43 @@ return YES; - // Report errors which have occurred + // Report errors which have occurred + } else { + SPBeginAlertSheet(NSLocalizedString(@"Couldn't write row", @"Couldn't write row error"), NSLocalizedString(@"Edit row", @"Edit row button"), NSLocalizedString(@"Discard changes", @"discard changes button"), nil, tableWindow, self, @selector(addRowErrorSheetDidEnd:returnCode:contextInfo:), nil, + [NSString stringWithFormat:NSLocalizedString(@"MySQL said:\n\n%@", @"message of panel when error while adding row to db"), [mySQLConnection getLastErrorMessage]]); + return NO; + } +} + +/* + * Handle the user decision as a result of an addRow error. + */ +- (void) addRowErrorSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +{ + // Order out current sheet to suppress overlapping of sheets + [[alert window] orderOut:nil]; + + // Edit row selected - reselect the row, and start editing. + if ( returnCode == NSAlertDefaultReturn ) { + [tableContentView selectRowIndexes:[NSIndexSet indexSetWithIndex:currentlyEditingRow] byExtendingSelection:NO]; + [tableContentView performSelector:@selector(keyDown:) withObject:[NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[tableWindow windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x24] afterDelay:0.0]; + + // Discard changes selected } else { - SPBeginAlertSheet(NSLocalizedString(@"Couldn't write row", @"Couldn't write row error"), NSLocalizedString(@"Edit row", @"Edit row button"), NSLocalizedString(@"Discard changes", @"discard changes button"), nil, tableWindow, self, @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addrow", - [NSString stringWithFormat:NSLocalizedString(@"MySQL said:\n\n%@", @"message of panel when error while adding row to db"), [mySQLConnection getLastErrorMessage]]); - return NO; + if ( !isEditingNewRow ) { + [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow]; + isEditingRow = NO; + } else { + tableRowsCount--; + [tableValues removeRowAtIndex:currentlyEditingRow]; + isEditingRow = NO; + isEditingNewRow = NO; + } + currentlyEditingRow = -1; } + [tableContentView reloadData]; } - /* * A method to be called whenever the table selection changes; checks whether the current * row is being edited, and if so attempts to save it. Returns YES if no save was necessary @@ -2011,436 +2332,131 @@ // Retrieve the field names for this table from the data cache. This is used when requesting all data as part // of the fieldListForQuery method, and also to decide whether or not to preserve the current filter/sort settings. - columnNames = [tableDataInstance columnNames]; - - // Get the primary key if there is one - if ( !keys ) { - setLimit = NO; - keys = [[NSMutableArray alloc] init]; - theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [selectedTable backtickQuotedString]]]; - [theResult setReturnDataAsStrings:YES]; - if ([theResult numOfRows]) [theResult dataSeek:0]; - for ( i = 0 ; i < [theResult numOfRows] ; i++ ) { - theRow = [theResult fetchRowAsDictionary]; - if ( [[theRow objectForKey:@"Key"] isEqualToString:@"PRI"] ) { - [keys addObject:[theRow objectForKey:@"Field"]]; - } - } - } - - // If there is no primary key, all the fields are used in the argument. - if ( ![keys count] ) { - [keys setArray:columnNames]; - setLimit = YES; - - // When the option to not show blob or text options is set, we have a problem - we don't have - // the right values to use in the WHERE statement. Throw an error if this is the case. - if ( [prefs boolForKey:SPLoadBlobsAsNeeded] && [self tableContainsBlobOrTextColumns] ) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, - NSLocalizedString(@"You can't hide blob and text fields when working with tables without index.", @"message of panel when trying to edit tables without index and with hidden blob/text fields")); - [keys removeAllObjects]; - [tableContentView deselectAll:self]; - return @""; - } - } - - // Walk through the keys list constructing the argument list - for ( i = 0 ; i < [keys count] ; i++ ) { - if ( i ) - [argument appendString:@" AND "]; - - // Use the selected row if appropriate - if ( row >= 0 ) { - tempValue = [tableValues cellDataAtRow:row column:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(keys, i)] objectForKey:@"datacolumnindex"] integerValue]]; - - // Otherwise use the oldRow - } else { - tempValue = [oldRow objectAtIndex:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(keys, i)] objectForKey:@"datacolumnindex"] integerValue]]; - } - - if ( [tempValue isNSNull] ) { - [argument appendString:[NSString stringWithFormat:@"%@ IS NULL", [NSArrayObjectAtIndex(keys, i) backtickQuotedString]]]; - } else if ( [tempValue isSPNotLoaded] ) { - NSLog(@"Exceptional case: SPNotLoaded object found for method “argumentForRow:”!"); - return @""; - } else { - - if ( [tempValue isKindOfClass:[NSData class]] ) - [value setString:[NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:tempValue]]]; - else - [value setString:[NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:tempValue]]]; - - [argument appendString:[NSString stringWithFormat:@"%@ = %@", [NSArrayObjectAtIndex(keys, i) backtickQuotedString], value]]; - } - } - - if (setLimit) [argument appendString:@" LIMIT 1"]; - - return argument; -} - - -/* - * Returns YES if the table contains any columns which are of any of the blob or text types, - * NO otherwise. - */ -- (BOOL)tableContainsBlobOrTextColumns -{ - NSInteger i; - - for ( i = 0 ; i < [dataColumns count]; i++ ) { - if ( [tableDataInstance columnIsBlobOrText:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]] ) { - return YES; - } - } - - return NO; -} - -/* - * Returns a string controlling which fields to retrieve for a query. Returns * (all fields) if the preferences - * option dontShowBlob isn't set; otherwise, returns a comma-separated list of all non-blob/text fields. - */ -- (NSString *)fieldListForQuery -{ - NSInteger i; - NSMutableArray *fields = [NSMutableArray array]; - - if (([prefs boolForKey:SPLoadBlobsAsNeeded]) && ([dataColumns count] > 0)) { - - NSArray *columnNames = [tableDataInstance columnNames]; - - for (i = 0 ; i < [columnNames count]; i++) - { - if (![tableDataInstance columnIsBlobOrText:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]] ) { - [fields addObject:[NSArrayObjectAtIndex(columnNames, i) backtickQuotedString]]; - } - else { - // For blob/text fields, select a null placeholder so the column count is still correct - [fields addObject:@"NULL"]; - } - } - - return [fields componentsJoinedByString:@","]; - } - else { - return @"*"; - } -} - -- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo -/* - if contextInfo == addrow: remain in edit-mode if user hits OK, otherwise cancel editing - if contextInfo == removerow: removes row if user hits OK - */ -{ - - NSMutableIndexSet *selectedRows = [NSMutableIndexSet indexSet]; - NSString *wherePart; - NSInteger i, errors; - BOOL consoleUpdateStatus; - BOOL reloadAfterRemovingRow = [prefs boolForKey:SPReloadAfterRemovingRow]; - - if([sheet respondsToSelector:@selector(orderOut:)]) - [sheet orderOut:self]; - else if([sheet window] && [[sheet window] respondsToSelector:@selector(orderOut:)]) - [[sheet window] orderOut:self]; - - if ( [contextInfo isEqualToString:@"addrow"] ) { - - if ( returnCode == NSAlertDefaultReturn ) { - [tableContentView selectRowIndexes:[NSIndexSet indexSetWithIndex:currentlyEditingRow] byExtendingSelection:NO]; - [tableContentView performSelector:@selector(keyDown:) withObject:[NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[tableWindow windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x24] afterDelay:0.0]; - } else { - if ( !isEditingNewRow ) { - [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow]; - isEditingRow = NO; - } else { - tableRowsCount--; - [tableValues removeRowAtIndex:currentlyEditingRow]; - isEditingRow = NO; - isEditingNewRow = NO; - } - currentlyEditingRow = -1; - } - [tableContentView reloadData]; - } else if ( [contextInfo isEqualToString:@"removeallrows"] ) { - if ( returnCode == NSAlertDefaultReturn ) { - //check if the user is currently editing a row - if (isEditingRow) { - //cancel the edit - isEditingRow = NO; - // in case the delete fails, make sure we at least stay in a somewhat consistent state - [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow]; - currentlyEditingRow = -1; - } - - [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM %@", [selectedTable backtickQuotedString]]]; - if ( ![mySQLConnection queryErrored] ) { - - // Reset auto increment if suppression button was ticked - if([[sheet suppressionButton] state] == NSOnState) - [tableSourceInstance setAutoIncrementTo:@"1"]; - - [self reloadTable:self]; - - } else { - [self performSelector:@selector(showErrorSheetWith:) - withObject:[NSArray arrayWithObjects:NSLocalizedString(@"Error", @"error"), - [NSString stringWithFormat:NSLocalizedString(@"Couldn't delete rows.\n\nMySQL said: %@", @"message when deleteing all rows failed"), - [mySQLConnection getLastErrorMessage]], - nil] - afterDelay:0.3]; - } - } - } else if ( [contextInfo isEqualToString:@"removerow"] ) { - if ( returnCode == NSAlertDefaultReturn ) { - [selectedRows addIndexes:[tableContentView selectedRowIndexes]]; - - //check if the user is currently editing a row - if (isEditingRow) { - //make sure that only one row is selected. This should never happen - if ([selectedRows count]!=1) { - NSLog(@"Expected only one selected row, but found %d",[selectedRows count]); - } - // this code is pretty much taken from the escape key handler - if ( isEditingNewRow ) { - // since the user is currently editing a new row, we don't actually have to delete any rows from the database - // we just have to remove the row from the view (and the store) - isEditingRow = NO; - isEditingNewRow = NO; - tableRowsCount--; - [tableValues removeRowAtIndex:currentlyEditingRow]; - currentlyEditingRow = -1; - [self updateCountText]; - [tableContentView reloadData]; - - //deselect the row - [tableContentView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; - - // we also don't have to reload the table, since no query went to the database - return; - } else { - //cancel the edit - isEditingRow = NO; - // in case the delete fails, make sure we at least stay in a somewhat consistent state - [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow]; - currentlyEditingRow = -1; - } - - } - [tableContentView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; - - errors = 0; - - // Disable updating of the Console Log window for large number of queries - // to speed the deletion - consoleUpdateStatus = [[SPQueryController sharedQueryController] allowConsoleUpdate]; - if([selectedRows count] > 10) - [[SPQueryController sharedQueryController] setAllowConsoleUpdate:NO]; - - NSUInteger index = [selectedRows firstIndex]; - - NSArray *primaryKeyFieldNames = [tableDataInstance primaryKeyColumnNames]; - - // If no PRIMARY KEY is found and numberOfSelectedRows > 3 then - // check for uniqueness of rows via combining all column values; - // if unique then use the all columns as 'primary keys' - if([selectedRows count] >3 && primaryKeyFieldNames == nil) { - primaryKeyFieldNames = [tableDataInstance columnNames]; - - NSInteger numberOfRows = 0; - // Get the number of rows in the table - MCPResult *r; - r = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@", [selectedTable backtickQuotedString]]]; - if (![mySQLConnection queryErrored]) { - NSArray *a = [r fetchRowAsArray]; - if([a count]) - numberOfRows = [[a objectAtIndex:0] integerValue]; - } - // Check for uniqueness via LIMIT numberOfRows-1,numberOfRows for speed - if(numberOfRows > 0) { - [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM %@ GROUP BY %@ LIMIT %ld,%ld", [selectedTable backtickQuotedString], [primaryKeyFieldNames componentsJoinedAndBacktickQuoted], (long)(numberOfRows-1), (long)numberOfRows]]; - if([mySQLConnection affectedRows] == 0) - primaryKeyFieldNames = nil; - } else { - primaryKeyFieldNames = nil; - } - } - - if(primaryKeyFieldNames == nil) { - // delete row by row - while (index != NSNotFound) { - - wherePart = [NSString stringWithString:[self argumentForRow:index]]; + columnNames = [tableDataInstance columnNames]; - //argumentForRow might return empty query, in which case we shouldn't execute the partial query - if([wherePart length]) { - [mySQLConnection queryString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@", [selectedTable backtickQuotedString], wherePart]]; + // Get the primary key if there is one + if ( !keys ) { + setLimit = NO; + keys = [[NSMutableArray alloc] init]; + theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW COLUMNS FROM %@", [selectedTable backtickQuotedString]]]; + [theResult setReturnDataAsStrings:YES]; + if ([theResult numOfRows]) [theResult dataSeek:0]; + for ( i = 0 ; i < [theResult numOfRows] ; i++ ) { + theRow = [theResult fetchRowAsDictionary]; + if ( [[theRow objectForKey:@"Key"] isEqualToString:@"PRI"] ) { + [keys addObject:[theRow objectForKey:@"Field"]]; + } + } + } + + // If there is no primary key, all the fields are used in the argument. + if ( ![keys count] ) { + [keys setArray:columnNames]; + setLimit = YES; + + // When the option to not show blob or text options is set, we have a problem - we don't have + // the right values to use in the WHERE statement. Throw an error if this is the case. + if ( [prefs boolForKey:SPLoadBlobsAsNeeded] && [self tableContainsBlobOrTextColumns] ) { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, + NSLocalizedString(@"You can't hide blob and text fields when working with tables without index.", @"message of panel when trying to edit tables without index and with hidden blob/text fields")); + [keys removeAllObjects]; + [tableContentView deselectAll:self]; + return @""; + } + } - // Check for errors - if ( ![mySQLConnection affectedRows] || [mySQLConnection queryErrored]) { - // If error delete that index from selectedRows for reloading table if - // "ReloadAfterRemovingRow" is disbaled - if(!reloadAfterRemovingRow) - [selectedRows removeIndex:index]; - errors++; - } - } else { - if(!reloadAfterRemovingRow) - [selectedRows removeIndex:index]; - errors++; - } - index = [selectedRows indexGreaterThanIndex:index]; - } - } else if ([primaryKeyFieldNames count] == 1) { - // if table has only one PRIMARY KEY - // delete the fast way by using the PRIMARY KEY in an IN clause - NSMutableString *deleteQuery = [NSMutableString string]; - NSInteger affectedRows = 0; + // Walk through the keys list constructing the argument list + for ( i = 0 ; i < [keys count] ; i++ ) { + if ( i ) + [argument appendString:@" AND "]; - [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ IN (", [selectedTable backtickQuotedString], [NSArrayObjectAtIndex(primaryKeyFieldNames,0) backtickQuotedString]]]; + // Use the selected row if appropriate + if ( row >= 0 ) { + tempValue = [tableValues cellDataAtRow:row column:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(keys, i)] objectForKey:@"datacolumnindex"] integerValue]]; - while (index != NSNotFound) { + // Otherwise use the oldRow + } else { + tempValue = [oldRow objectAtIndex:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(keys, i)] objectForKey:@"datacolumnindex"] integerValue]]; + } - id keyValue = [tableValues cellDataAtRow:index column:[[[tableDataInstance columnWithName:NSArrayObjectAtIndex(primaryKeyFieldNames,0)] objectForKey:@"datacolumnindex"] integerValue]]; + if ( [tempValue isNSNull] ) { + [argument appendString:[NSString stringWithFormat:@"%@ IS NULL", [NSArrayObjectAtIndex(keys, i) backtickQuotedString]]]; + } else if ( [tempValue isSPNotLoaded] ) { + NSLog(@"Exceptional case: SPNotLoaded object found for method “argumentForRow:”!"); + return @""; + } else { - if([keyValue isKindOfClass:[NSData class]]) - [deleteQuery appendString:[NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:keyValue]]]; - else - [deleteQuery appendString:[NSString stringWithFormat:@"'%@'", [keyValue description]]]; + if ( [tempValue isKindOfClass:[NSData class]] ) + [value setString:[NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:tempValue]]]; + else + [value setString:[NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:tempValue]]]; + + [argument appendString:[NSString stringWithFormat:@"%@ = %@", [NSArrayObjectAtIndex(keys, i) backtickQuotedString], value]]; + } + } + + if (setLimit) [argument appendString:@" LIMIT 1"]; + + return argument; +} - // Split deletion query into 256k chunks - if([deleteQuery length] > 256000) { - [deleteQuery appendString:@")"]; - [mySQLConnection queryString:deleteQuery]; - // Remember affected rows for error checking - affectedRows += [mySQLConnection affectedRows]; - // Reinit a new deletion query - [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ IN (", [selectedTable backtickQuotedString], [NSArrayObjectAtIndex(primaryKeyFieldNames,0) backtickQuotedString]]]; - } else { - [deleteQuery appendString:@","]; - } - index = [selectedRows indexGreaterThanIndex:index]; - } +/* + * Returns YES if the table contains any columns which are of any of the blob or text types, + * NO otherwise. + */ +- (BOOL)tableContainsBlobOrTextColumns +{ + NSInteger i; - // Check if deleteQuery's maximal length was reached for the last index - // if yes omit the empty query - if(![deleteQuery hasSuffix:@"("]) { - // Replace final , by ) and delete the remaining rows - [deleteQuery setString:[NSString stringWithFormat:@"%@)", [deleteQuery substringToIndex:([deleteQuery length]-1)]]]; - [mySQLConnection queryString:deleteQuery]; - // Remember affected rows for error checking - affectedRows += [mySQLConnection affectedRows]; - } - - errors = (affectedRows > 0) ? [selectedRows count] - affectedRows : [selectedRows count]; - } else { - // if table has more than one PRIMARY KEY - // delete the row by using all PRIMARY KEYs in an OR clause - NSMutableString *deleteQuery = [NSMutableString string]; - NSInteger affectedRows = 0; - - [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE ", [selectedTable backtickQuotedString]]]; - - while (index != NSNotFound) { - - // Build the AND clause of PRIMARY KEYS - [deleteQuery appendString:@"("]; - for(NSString *primaryKeyFieldName in primaryKeyFieldNames) { + for ( i = 0 ; i < [dataColumns count]; i++ ) { + if ( [tableDataInstance columnIsBlobOrText:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]] ) { + return YES; + } + } - id keyValue = [tableValues cellDataAtRow:index column:[[[tableDataInstance columnWithName:primaryKeyFieldName] objectForKey:@"datacolumnindex"] integerValue]]; - - [deleteQuery appendString:[primaryKeyFieldName backtickQuotedString]]; - if ([keyValue isKindOfClass:[NSData class]]) { - [deleteQuery appendString:@"=X'"]; - [deleteQuery appendString:[mySQLConnection prepareBinaryData:keyValue]]; - } else { - [deleteQuery appendString:@"='"]; - [deleteQuery appendString:[mySQLConnection prepareString:[keyValue description]]]; - } - [deleteQuery appendString:@"' AND "]; - } - - // Remove the trailing AND and add the closing bracket - [deleteQuery deleteCharactersInRange:NSMakeRange([deleteQuery length]-5, 5)]; - [deleteQuery appendString:@")"]; - - // Split deletion query into 64k chunks - if([deleteQuery length] > 64000) { - [mySQLConnection queryString:deleteQuery]; - // Remember affected rows for error checking - affectedRows += [mySQLConnection affectedRows]; - // Reinit a new deletion query - [deleteQuery setString:[NSString stringWithFormat:@"DELETE FROM %@ WHERE ", [selectedTable backtickQuotedString]]]; - } else { - [deleteQuery appendString:@" OR "]; - } - - index = [selectedRows indexGreaterThanIndex:index]; - } - - // Check if deleteQuery's maximal length was reached for the last index - // if yes omit the empty query - if(![deleteQuery hasSuffix:@"WHERE "]) { - // Remove final ' OR ' and delete the remaining rows - [deleteQuery setString:[deleteQuery substringToIndex:([deleteQuery length]-4)]]; - [mySQLConnection queryString:deleteQuery]; - // Remember affected rows for error checking - affectedRows += [mySQLConnection affectedRows]; - } - - errors = (affectedRows > 0) ? [selectedRows count] - affectedRows : [selectedRows count]; - } - - // Restore Console Log window's updating bahaviour - [[SPQueryController sharedQueryController] setAllowConsoleUpdate:consoleUpdateStatus]; - - if (errors) { - NSArray *message; - //TODO: The following three messages are NOT localisable! - if (errors < 0) { - message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"), - [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ more %@ deleted! Please check the Console and inform the Sequel Pro team!", @"message of panel when more rows were deleted"), (long)(errors*-1), ((errors*-1)>1)?@"s":@"", (errors>1)?@"were":@"was"], - nil]; - } - else { - if (primaryKeyFieldNames == nil) - message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"), - [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ ha%@ not been deleted. Reload the table to be sure that the rows exist and use a primary key for your table.", @"message of panel when not all selected fields have been deleted"), (long)errors, (errors>1)?@"s":@"", (errors>1)?@"ve":@"s"], - nil]; - else - message = [NSArray arrayWithObjects:NSLocalizedString(@"Warning", @"warning"), - [NSString stringWithFormat:NSLocalizedString(@"%ld row%@ ha%@ not been deleted. Reload the table to be sure that the rows exist and check the Console for possible errors inside the primary key%@ for your table.", @"message of panel when not all selected fields have been deleted by using primary keys"), (long)errors, (errors>1)?@"s":@"", (errors>1)?@"ve":@"s", (errors>1)?@"s":@""], - nil]; - } - - [self performSelector:@selector(showErrorSheetWith:) - withObject:message - afterDelay:0.3]; - } + return NO; +} - // Refresh table content - if ( errors || reloadAfterRemovingRow ) { - previousTableRowsCount = tableRowsCount; - [self loadTableValues]; - } else { - for ( i = tableRowsCount - 1 ; i >= 0 ; i-- ) { - if ([selectedRows containsIndex:i]) [tableValues removeRowAtIndex:i]; - } - tableRowsCount = [tableValues count]; - [tableContentView reloadData]; +/* + * Returns a string controlling which fields to retrieve for a query. Returns * (all fields) if the preferences + * option dontShowBlob isn't set; otherwise, returns a comma-separated list of all non-blob/text fields. + */ +- (NSString *)fieldListForQuery +{ + NSInteger i; + NSMutableArray *fields = [NSMutableArray array]; + + if (([prefs boolForKey:SPLoadBlobsAsNeeded]) && ([dataColumns count] > 0)) { + + NSArray *columnNames = [tableDataInstance columnNames]; + + for (i = 0 ; i < [columnNames count]; i++) + { + if (![tableDataInstance columnIsBlobOrText:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]] ) { + [fields addObject:[NSArrayObjectAtIndex(columnNames, i) backtickQuotedString]]; + } + else { + // For blob/text fields, select a null placeholder so the column count is still correct + [fields addObject:@"NULL"]; } - [tableContentView deselectAll:self]; - } else { - // The user clicked cancel in the "sure you wanna delete" message - // restore editing or whatever } + return [fields componentsJoinedByString:@","]; + } + else { + return @"*"; } } +/* + * Close an open sheet. + */ +- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo +{ + [sheet orderOut:self]; +} + /** * Show Error sheet (can be called from inside of a endSheet selector) * via [self performSelector:@selector(showErrorSheetWithTitle:) withObject: afterDelay:] @@ -2449,7 +2465,7 @@ { // error := first object is the title , second the message, only one button OK SPBeginAlertSheet([error objectAtIndex:0], NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, nil, + nil, nil, tableWindow, self, nil, nil, [error objectAtIndex:1]); } @@ -2926,7 +2942,7 @@ [self loadTableValues]; if ([mySQLConnection queryErrored]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't sort table. MySQL said: %@", @"message of panel when sorting of table failed"), [mySQLConnection getLastErrorMessage]]); [tableDocumentInstance endTask]; [sortPool drain]; @@ -3021,7 +3037,7 @@ MCPResult *tempResult = [mySQLConnection queryString:query]; if (![tempResult numOfRows]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Couldn't load the row. Reload the table to be sure that the row exists and use a primary key for your table.", @"message of panel when loading of row failed")); return NO; } diff --git a/Source/TableDocument.m b/Source/TableDocument.m index 3f94aa01..724e9c60 100644 --- a/Source/TableDocument.m +++ b/Source/TableDocument.m @@ -1108,7 +1108,7 @@ { // error := first object is the title , second the message, only one button OK SPBeginAlertSheet([error objectAtIndex:0], NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, nil, + nil, nil, tableWindow, self, nil, nil, [error objectAtIndex:1]); } @@ -2405,10 +2405,10 @@ if (![mySQLConnection queryErrored]) { //flushed privileges without errors - SPBeginAlertSheet(NSLocalizedString(@"Flushed Privileges", @"title of panel when successfully flushed privs"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Successfully flushed privileges.", @"message of panel when successfully flushed privs")); + SPBeginAlertSheet(NSLocalizedString(@"Flushed Privileges", @"title of panel when successfully flushed privs"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Successfully flushed privileges.", @"message of panel when successfully flushed privs")); } else { //error while flushing privileges - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't flush privileges.\nMySQL said: %@", @"message of panel when flushing privs failed"), + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't flush privileges.\nMySQL said: %@", @"message of panel when flushing privs failed"), [mySQLConnection getLastErrorMessage]]); } } @@ -4153,7 +4153,7 @@ - (void)_copyDatabase { if ([[databaseCopyNameField stringValue] isEqualToString:@""]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given")); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given")); return; } SPDatabaseCopy *dbActionCopy = [[SPDatabaseCopy alloc] init]; @@ -4174,7 +4174,7 @@ - (void)_renameDatabase { if ([[databaseRenameNameField stringValue] isEqualToString:@""]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given")); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given")); return; } SPDatabaseRename *dbActionRename = [[SPDatabaseRename alloc] init]; @@ -4198,7 +4198,7 @@ // This check is not necessary anymore as the add database button is now only enabled if the name field // has a length greater than zero. We'll leave it in just in case. if ([[databaseNameField stringValue] isEqualToString:@""]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given")); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given")); return; } @@ -4214,14 +4214,14 @@ if ([mySQLConnection queryErrored]) { // An error occurred - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't create database.\nMySQL said: %@", @"message of panel when creation of db failed"), [mySQLConnection getLastErrorMessage]]); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't create database.\nMySQL said: %@", @"message of panel when creation of db failed"), [mySQLConnection getLastErrorMessage]]); return; } // Error while selecting the new database (is this even possible?) if (![mySQLConnection selectDB:[databaseNameField stringValue]] ) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to database %@.\nBe sure that you have the necessary privileges.", @"message of panel when connection to db failed after selecting from popupbutton"), [databaseNameField stringValue]]); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to database %@.\nBe sure that you have the necessary privileges.", @"message of panel when connection to db failed after selecting from popupbutton"), [databaseNameField stringValue]]); [self setDatabases:self]; @@ -4304,7 +4304,7 @@ || ![mySQLConnection selectDB:targetDatabaseName]) { if ( [mySQLConnection isConnected] ) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to database %@.\nBe sure that you have the necessary privileges.", @"message of panel when connection to db failed after selecting from popupbutton"), targetDatabaseName]); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to database %@.\nBe sure that you have the necessary privileges.", @"message of panel when connection to db failed after selecting from popupbutton"), targetDatabaseName]); // Update the database list [self setDatabases:self]; diff --git a/Source/TableDump.m b/Source/TableDump.m index 004a8e61..6dd3833c 100644 --- a/Source/TableDump.m +++ b/Source/TableDump.m @@ -324,7 +324,7 @@ if ( [[NSFileManager defaultManager] fileExistsAtPath:exportFile] ) { if ( ![[NSFileManager defaultManager] isWritableFileAtPath:exportFile] || !(fileHandle = [SPFileHandle fileHandleForWritingAtPath:exportFile]) ) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Couldn't replace the file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be replaced")); [pool release]; return; @@ -333,7 +333,7 @@ // Otherwise attempt to create a file } else { if ( ![[NSFileManager defaultManager] createFileAtPath:exportFile contents:[NSData data] attributes:nil] ) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); [pool release]; return; @@ -343,7 +343,7 @@ fileHandle = [SPFileHandle fileHandleForWritingAtPath:exportFile]; if ( !fileHandle ) { [[NSFileManager defaultManager] removeFileAtPath:exportFile handler:nil]; - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); [pool release]; return; @@ -474,7 +474,7 @@ // Display error message on problems if ( !progressCancelled && !success ) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); } @@ -623,7 +623,7 @@ if (!sqlFileHandle) { SPBeginAlertSheet(NSLocalizedString(@"Import Error title", @"Import Error"), NSLocalizedString(@"OK button label", @"OK button"), - nil, nil, tableWindow, self, nil, nil, nil, + nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"SQL file open error", @"The SQL file you selected could not be found or read.")); if([filename hasPrefix:SPImportClipboardTempFileNamePrefix]) [[NSFileManager defaultManager] removeItemAtPath:filename error:nil]; @@ -667,7 +667,7 @@ [self closeAndStopProgressSheet]; SPBeginAlertSheet(NSLocalizedString(@"SQL read error title", @"File read error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, nil, + nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"SQL read error", @"An error occurred when reading the file.\n\nOnly %ld queries were executed.\n\n(%@)"), (long)queriesPerformed, [exception reason]]); [sqlParser release]; [sqlDataBuffer release]; @@ -717,7 +717,7 @@ [self closeAndStopProgressSheet]; SPBeginAlertSheet(NSLocalizedString(@"SQL read error title", @"File read error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, nil, + nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"SQL encoding read error", @"An error occurred when reading the file, as it could not be read in either UTF-8 or %@.\n\nOnly %ld queries were executed."), [[tableDocumentInstance connectionEncoding] UTF8String], (long)queriesPerformed]); [sqlParser release]; [sqlDataBuffer release]; @@ -886,7 +886,7 @@ if (!csvFileHandle) { SPBeginAlertSheet(NSLocalizedString(@"Import Error title", @"Import Error"), NSLocalizedString(@"OK button label", @"OK button"), - nil, nil, tableWindow, self, nil, nil, nil, + nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"CSV file open error", @"The CSV file you selected could not be found or read.")); if([filename hasPrefix:SPImportClipboardTempFileNamePrefix]) [[NSFileManager defaultManager] removeItemAtPath:filename error:nil]; @@ -946,7 +946,7 @@ [self closeAndStopProgressSheet]; SPBeginAlertSheet(NSLocalizedString(@"CSV read error title", @"File read error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, nil, + nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"CSV read error", @"An error occurred when reading the file.\n\nOnly %ld rows were imported.\n\n(%@)"), (long)rowsImported, [exception reason]]); [csvParser release]; [csvDataBuffer release]; @@ -995,7 +995,7 @@ [self closeAndStopProgressSheet]; SPBeginAlertSheet(NSLocalizedString(@"CSV read error title", @"File read error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, nil, + nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"CSV encoding read error", @"An error occurred when reading the file, as it could not be read using %@.\n\nOnly %ld rows were imported."), [[tableDocumentInstance connectionEncoding] UTF8String], (long)rowsImported]); [csvParser release]; [csvDataBuffer release]; @@ -1380,7 +1380,7 @@ NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, - nil, nil, nil, + nil, nil, NSLocalizedString(@"Could not parse file as CSV", @"Error when we can't parse/split file as CSV") ); return FALSE; @@ -1393,7 +1393,7 @@ NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, - nil, nil, nil, + nil, nil, NSLocalizedString(@"The CSV was read as containing more than 512 columns, more than the maximum columns permitted for speed reasons by Sequel Pro.\n\nThis usually happens due to errors reading the CSV; please double-check the CSV to be imported and the line endings and escape characters at the bottom of the CSV selection dialog.", @"Error when CSV appears to have too many columns to import, probably due to line ending mismatch") ); return FALSE; @@ -1407,7 +1407,7 @@ NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, - nil, nil, nil, + nil, nil, NSLocalizedString(@"Can't import CSV data into a database without any tables!", @"error text when trying to import csv data, but we have no tables in the db") ); return FALSE; diff --git a/Source/TableSource.m b/Source/TableSource.m index d6c8fdcf..c3424c0a 100644 --- a/Source/TableSource.m +++ b/Source/TableSource.m @@ -80,7 +80,7 @@ if ([mySQLConnection isConnected]) { SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], self, nil, nil, nil, + nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred while retrieving information.\nMySQL said: %@", @"message of panel when retrieving information failed"), [mySQLConnection getLastErrorMessage]]); } @@ -102,7 +102,7 @@ if ([mySQLConnection isConnected]) { SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], self, nil, nil, nil, + nil, nil, [NSApp mainWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred while retrieving information.\nMySQL said: %@", @"message of panel when retrieving information failed"), [mySQLConnection getLastErrorMessage]]); } @@ -400,7 +400,32 @@ [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:(hasForeignKey) ? @"removeFieldAndForeignKey" : @"removeField"]; + [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(removeFieldSheetDidEnd:returnCode:contextInfo:) contextInfo:(hasForeignKey) ? @"removeFieldAndForeignKey" : @"removeField"]; +} + +/** + * Process the remove field sheet closing, performing the delete if the user + * confirmed the action. + */ +- (void)removeFieldSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +{ + // Order out current sheet to suppress overlapping of sheets + [[alert window] orderOut:nil]; + + if (returnCode == NSAlertDefaultReturn) { + [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Removing field...", @"removing field task status message")]; + + NSNumber *removeKey = [NSNumber numberWithBool:[contextInfo hasSuffix:@"AndForeignKey"]]; + + if ([NSThread isMainThread]) { + [NSThread detachNewThreadSelector:@selector(_removeFieldAndForeignKey:) toTarget:self withObject:removeKey]; + + [tableDocumentInstance enableTaskCancellationWithTitle:NSLocalizedString(@"Cancel", @"cancel button") callbackObject:self callbackFunction:NULL]; + } + else { + [self _removeFieldAndForeignKey:removeKey]; + } + } } /** @@ -452,7 +477,32 @@ [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:(hasForeignKey) ? @"removeIndexAndForeignKey" : @"removeIndex"]; + [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(removeIndexSheetDidEnd:returnCode:contextInfo:) contextInfo:(hasForeignKey) ? @"removeIndexAndForeignKey" : @"removeIndex"]; +} + +/** + * Process the remove index sheet closing, performing the delete if the user + * confirmed the action. + */ +- (void)removeIndexSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +{ + // Order out current sheet to suppress overlapping of sheets + [[alert window] orderOut:nil]; + + if (returnCode == NSAlertDefaultReturn) { + [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Removing index...", @"removing index task status message")]; + + NSNumber *removeKey = [NSNumber numberWithBool:[contextInfo hasSuffix:@"AndForeignKey"]]; + + if ([NSThread isMainThread]) { + [NSThread detachNewThreadSelector:@selector(_removeIndexAndForeignKey:) toTarget:self withObject:removeKey]; + + [tableDocumentInstance enableTaskCancellationWithTitle:NSLocalizedString(@"Cancel", @"cancel button") callbackObject:self callbackFunction:NULL]; + } + else { + [self _removeIndexAndForeignKey:removeKey]; + } + } } - (IBAction)resetAutoIncrement:(id)sender @@ -468,7 +518,7 @@ [NSApp beginSheet:resetAutoIncrementSheet modalForWindow:tableWindow modalDelegate:self - didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) + didEndSelector:@selector(resetAutoincrementSheetDidEnd:returnCode:contextInfo:) contextInfo:@"resetAutoIncrement"]; [resetAutoIncrementValue setStringValue:@"1"]; @@ -479,6 +529,20 @@ } +/** + * Process the autoincrement sheet closing, resetting if the user + * confirmed the action. + */ +- (void)resetAutoincrementSheetDidEnd:(NSWindow *)theSheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +{ + // Order out current sheet to suppress overlapping of sheets + [theSheet orderOut:nil]; + + if (returnCode == NSAlertDefaultReturn) { + [self setAutoIncrementTo:[[resetAutoIncrementValue stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]]; + } +} + #pragma mark - #pragma mark Index sheet methods @@ -515,10 +579,31 @@ [NSApp beginSheet:indexSheet modalForWindow:tableWindow modalDelegate:self - didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) + didEndSelector:@selector(addIndexSheetDidEnd:returnCode:contextInfo:) contextInfo:@"addIndex"]; } +/** + * Process the new index sheet closing, adding the index if appropriate + */ +- (void)addIndexSheetDidEnd:(NSWindow *)theSheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +{ + [theSheet orderOut:nil]; + + if (returnCode == NSOKButton) { + [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Adding index...", @"adding index task status message")]; + + if ([NSThread isMainThread]) { + [NSThread detachNewThreadSelector:@selector(_addIndex) toTarget:self withObject:nil]; + + [tableDocumentInstance enableTaskCancellationWithTitle:NSLocalizedString(@"Cancel", @"cancel button") callbackObject:self callbackFunction:NULL]; + } + else { + [self _addIndex]; + } + } +} + /** * Closes the current sheet and stops the modal session */ @@ -606,7 +691,7 @@ closes the keySheet if ([mySQLConnection queryErrored]) { SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, [NSApp mainWindow], nil, nil, nil, nil, + nil, nil, [NSApp mainWindow], nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred while trying to reset AUTO_INCREMENT of table '%@'.\n\nMySQL said: %@", @"error resetting auto_increment informative message"), selTable, [mySQLConnection getLastErrorMessage]]); } @@ -895,7 +980,7 @@ closes the keySheet if([mySQLConnection getLastErrorID] == 1146) { // If the current table doesn't exist anymore SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, @selector(sheetDidEnd:returnCode:contextInfo:), nil, nil, + nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred while trying to alter table '%@'.\n\nMySQL said: %@", @"error while trying to alter table message"), selectedTable, [mySQLConnection getLastErrorMessage]]); @@ -919,14 +1004,14 @@ closes the keySheet if (isEditingNewRow) { SPBeginAlertSheet(NSLocalizedString(@"Error adding field", @"error adding field message"), NSLocalizedString(@"Edit row", @"Edit row button"), - NSLocalizedString(@"Discard changes", @"discard changes button"), nil, tableWindow, self, @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addrow", + NSLocalizedString(@"Discard changes", @"discard changes button"), nil, tableWindow, self, @selector(addRowErrorSheetDidEnd:returnCode:contextInfo:), nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred when trying to add the field '%@'.\n\nMySQL said: %@", @"error adding field informative message"), [theRow objectForKey:@"Field"], [mySQLConnection getLastErrorMessage]]); } else { SPBeginAlertSheet(NSLocalizedString(@"Error changing field", @"error changing field message"), NSLocalizedString(@"Edit row", @"Edit row button"), - NSLocalizedString(@"Discard changes", @"discard changes button"), nil, tableWindow, self, @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addrow", + NSLocalizedString(@"Discard changes", @"discard changes button"), nil, tableWindow, self, @selector(addRowErrorSheetDidEnd:returnCode:contextInfo:), nil, [NSString stringWithFormat:NSLocalizedString(@"An error occurred when trying to change the field '%@'.\n\nMySQL said: %@", @"error changing field informative message"), [theRow objectForKey:@"Field"], [mySQLConnection getLastErrorMessage]]); } @@ -935,6 +1020,44 @@ closes the keySheet } } +/** + * Perform the action requested in the Add Row error sheet. + */ +- (void)addRowErrorSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +{ + + // Order out current sheet to suppress overlapping of sheets + [[alert window] orderOut:nil]; + + alertSheetOpened = NO; + + // Remain in edit mode - reselect the row and resume editing + if (returnCode == NSAlertDefaultReturn) { + + // Problem: reentering edit mode for first cell doesn't function + [tableSourceView selectRowIndexes:[NSIndexSet indexSetWithIndex:currentlyEditingRow] byExtendingSelection:NO]; + [tableSourceView performSelector:@selector(keyDown:) withObject:[NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[tableWindow windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x24] afterDelay:0.0]; + } + + // Discard changes and cancel editing + else { + if (!isEditingNewRow) { + [tableFields replaceObjectAtIndex:currentlyEditingRow + withObject:[NSMutableDictionary dictionaryWithDictionary:oldRow]]; + isEditingRow = NO; + } + else { + [tableFields removeObjectAtIndex:currentlyEditingRow]; + isEditingRow = NO; + isEditingNewRow = NO; + } + + currentlyEditingRow = -1; + } + + [tableSourceView reloadData]; +} + /** * A method to show an error sheet after a short delay, so that it can * be called from within an endSheet selector. This should be called on @@ -954,7 +1077,7 @@ closes the keySheet // Display the error sheet SPBeginAlertSheet([errorDictionary objectForKey:@"title"], NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, nil, + nil, nil, tableWindow, self, nil, nil, [errorDictionary objectForKey:@"message"]); } @@ -1023,104 +1146,17 @@ closes the keySheet /** * Called whenever a sheet is dismissed. - * - * if contextInfo == addrow: remain in edit-mode if user hits OK, otherwise cancel editing - * if contextInfo == removefield: removes row from mysql-db if user hits ok - * if contextInfo == removeindex: removes index from mysql-db if user hits ok - * if contextInfo == addIndex: adds and index to the mysql-db if user hits ok - * if contextInfo == cannotremovefield: do nothing */ - (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo { + // Order out current sheet to suppress overlapping of sheets if ([sheet respondsToSelector:@selector(orderOut:)]) [sheet orderOut:nil]; else if ([sheet respondsToSelector:@selector(window)]) [[sheet window] orderOut:nil]; - - if ([contextInfo isEqualToString:@"addrow"]) { - - alertSheetOpened = NO; - - if (returnCode == NSAlertDefaultReturn) { - - // Problem: reentering edit mode for first cell doesn't function - [tableSourceView selectRowIndexes:[NSIndexSet indexSetWithIndex:currentlyEditingRow] byExtendingSelection:NO]; - [tableSourceView performSelector:@selector(keyDown:) withObject:[NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[tableWindow windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x24] afterDelay:0.0]; - } - else { - if (!isEditingNewRow) { - [tableFields replaceObjectAtIndex:currentlyEditingRow - withObject:[NSMutableDictionary dictionaryWithDictionary:oldRow]]; - isEditingRow = NO; - } - else { - [tableFields removeObjectAtIndex:currentlyEditingRow]; - isEditingRow = NO; - isEditingNewRow = NO; - } - - currentlyEditingRow = -1; - } - - [tableSourceView reloadData]; - } - else if ([contextInfo isEqualToString:@"removeField"] || [contextInfo isEqualToString:@"removeFieldAndForeignKey"]) { - if (returnCode == NSAlertDefaultReturn) { - [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Removing field...", @"removing field task status message")]; - - NSNumber *removeKey = [NSNumber numberWithBool:[contextInfo hasSuffix:@"AndForeignKey"]]; - - if ([NSThread isMainThread]) { - [NSThread detachNewThreadSelector:@selector(_removeFieldAndForeignKey:) toTarget:self withObject:removeKey]; - - [tableDocumentInstance enableTaskCancellationWithTitle:NSLocalizedString(@"Cancel", @"cancel button") callbackObject:self callbackFunction:NULL]; - } - else { - [self _removeFieldAndForeignKey:removeKey]; - } - } - } - else if ([contextInfo isEqualToString:@"addIndex"]) { - if (returnCode == NSOKButton) { - [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Adding index...", @"adding index task status message")]; - - if ([NSThread isMainThread]) { - [NSThread detachNewThreadSelector:@selector(_addIndex) toTarget:self withObject:nil]; - - [tableDocumentInstance enableTaskCancellationWithTitle:NSLocalizedString(@"Cancel", @"cancel button") callbackObject:self callbackFunction:NULL]; - } - else { - [self _addIndex]; - } - } - } - else if ([contextInfo isEqualToString:@"removeIndex"] || [contextInfo isEqualToString:@"removeIndexAndForeignKey"]) { - if (returnCode == NSAlertDefaultReturn) { - [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Removing index...", @"removing index task status message")]; - - NSNumber *removeKey = [NSNumber numberWithBool:[contextInfo hasSuffix:@"AndForeignKey"]]; - - if ([NSThread isMainThread]) { - [NSThread detachNewThreadSelector:@selector(_removeIndexAndForeignKey:) toTarget:self withObject:removeKey]; - - [tableDocumentInstance enableTaskCancellationWithTitle:NSLocalizedString(@"Cancel", @"cancel button") callbackObject:self callbackFunction:NULL]; - } - else { - [self _removeIndexAndForeignKey:removeKey]; - } - } - } - else if ([contextInfo isEqualToString:@"cannotremovefield"]) { - ; - } - else if ([contextInfo isEqualToString:@"resetAutoIncrement"]) { - if (returnCode == NSAlertDefaultReturn) { - [self setAutoIncrementTo:[[resetAutoIncrementValue stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]]; - } - } - else - alertSheetOpened = NO; + + alertSheetOpened = NO; } #pragma mark - @@ -1481,7 +1517,7 @@ would result in a position change. // Run the query; report any errors, or reload the table on success [mySQLConnection queryString:queryString]; if ([mySQLConnection queryErrored]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't move field. MySQL said: %@", @"message of panel when field cannot be added in drag&drop operation"), [mySQLConnection getLastErrorMessage]]); } else { [tableDataInstance resetAllData]; @@ -1738,7 +1774,7 @@ would result in a position change. // Check for errors, but only if the query wasn't cancelled if ([mySQLConnection queryErrored] && ![mySQLConnection queryCancelled]) { - SPBeginAlertSheet(NSLocalizedString(@"Unable to add index", @"add index error message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Unable to add index", @"add index error message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while trying to add the index.\n\nMySQL said: %@", @"add index error informative message"), [mySQLConnection getLastErrorMessage]]); } else { diff --git a/Source/TablesList.m b/Source/TablesList.m index 15def773..aae23689 100644 --- a/Source/TablesList.m +++ b/Source/TablesList.m @@ -1334,7 +1334,7 @@ // Table has invalid name // Since we trimmed whitespace and checked for empty string, this means there is already a table with that name SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, - @selector(sheetDidEnd:returnCode:contextInfo:), nil, nil, + @selector(sheetDidEnd:returnCode:contextInfo:), nil, [NSString stringWithFormat: NSLocalizedString(@"The name '%@' is already used.", @"message when trying to rename a table/view/proc/etc to an already used name"), newTableName]); return; } @@ -1380,7 +1380,7 @@ } } @catch (NSException * myException) { - SPBeginAlertSheet( NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [myException reason]); + SPBeginAlertSheet( NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [myException reason]); } // Set window title to reflect the new table name @@ -2094,7 +2094,7 @@ SPBeginAlertSheet(NSLocalizedString(@"Error adding new table", @"error adding new table message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, - @selector(sheetDidEnd:returnCode:contextInfo:), nil, @"addRow", + @selector(sheetDidEnd:returnCode:contextInfo:), @"addRow", [NSString stringWithFormat:NSLocalizedString(@"An error occurred while trying to add the new table '%@'.\n\nMySQL said: %@", @"error adding new table informative message"), tableName, [mySQLConnection getLastErrorMessage]]); [tablesListView reloadData]; @@ -2112,7 +2112,7 @@ NSString *tableType = @""; if ([[copyTableNameField stringValue] isEqualToString:@""]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Table must have a name.", @"message of panel when no name is given for table")); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, NSLocalizedString(@"Table must have a name.", @"message of panel when no name is given for table")); return; } @@ -2148,7 +2148,7 @@ if ( ![queryResult numOfRows] ) { //error while getting table structure - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't get create syntax.\nMySQL said: %@", @"message of panel when table information cannot be retrieved"), [mySQLConnection getLastErrorMessage]]); } else { @@ -2194,7 +2194,7 @@ // Check for errors, only displaying if the connection hasn't been terminated if ([mySQLConnection queryErrored]) { if ([mySQLConnection isConnected]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"An error occured while retrieving the create syntax for '%@'.\nMySQL said: %@", @"message of panel when create syntax cannot be retrieved"), selectedTableName, [mySQLConnection getLastErrorMessage]]); } return; @@ -2207,7 +2207,7 @@ [mySQLConnection queryString:[tableSyntax stringByReplacingOccurrencesOfRegex:[NSString stringWithFormat:@"(?<=%@ )(`[^`]+?`)", [tableType uppercaseString]] withString:[[copyTableNameField stringValue] backtickQuotedString]]]; if ([mySQLConnection queryErrored]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't duplicate '%@'.\nMySQL said: %@", @"message of panel when an item cannot be renamed"), [copyTableNameField stringValue], [mySQLConnection getLastErrorMessage]]); } @@ -2215,7 +2215,7 @@ if ([mySQLConnection queryErrored]) { //error while creating new table - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't create '%@'.\nMySQL said: %@", @"message of panel when table cannot be created"), [copyTableNameField stringValue], [mySQLConnection getLastErrorMessage]]); } else { @@ -2237,7 +2237,6 @@ self, nil, nil, - nil, NSLocalizedString(@"There have been errors while copying table content. Please control the new table.", @"message of panel when table content cannot be copied") ); } -- cgit v1.2.3