From c661b409eaa0e29d9e012b79e7a66574a554817a Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Sun, 23 May 2010 21:44:59 +0000 Subject: Initial implementation of tabs: - Addition of PSMTabBar framework - Rework away from a document-based TableDocument - Support tabs throughout the application - Add menu items for creating tabs, and add support for dragging tabs to different windows --- Source/CMTextView.h | 6 +- Source/CMTextView.m | 84 ++--- Source/CustomQuery.h | 1 - Source/CustomQuery.m | 36 +-- Source/SPAppController.h | 9 +- Source/SPAppController.m | 244 +++++++++----- Source/SPConnectionController.h | 6 +- Source/SPConnectionController.m | 66 ++-- Source/SPConnectionDelegate.m | 36 ++- Source/SPConstants.h | 13 + Source/SPContentFilterManager.h | 3 +- Source/SPContentFilterManager.m | 7 +- Source/SPDocumentController.h | 32 ++ Source/SPDocumentController.m | 36 +++ Source/SPExportController.h | 5 +- Source/SPExportController.m | 18 +- Source/SPFieldEditorController.h | 2 +- Source/SPFieldEditorController.m | 4 +- Source/SPGrowlController.h | 6 +- Source/SPGrowlController.m | 41 ++- Source/SPHistoryController.m | 1 + Source/SPNavigatorController.m | 15 +- Source/SPPrintController.m | 8 +- Source/SPProcessListController.m | 4 +- Source/SPQueryController.m | 4 +- Source/SPQueryFavoriteManager.h | 3 +- Source/SPQueryFavoriteManager.m | 7 +- Source/SPSSHTunnel.m | 11 + Source/SPServerVariablesController.m | 2 +- Source/SPTableData.m | 7 +- Source/SPTableRelations.h | 1 - Source/SPTableRelations.m | 4 +- Source/SPTableTriggers.h | 1 - Source/SPTableTriggers.m | 4 +- Source/SPTableView.m | 12 +- Source/SPWindowController.h | 46 +++ Source/SPWindowController.m | 372 +++++++++++++++++++++ Source/TableContent.h | 2 +- Source/TableContent.m | 68 ++-- Source/TableDocument.h | 38 ++- Source/TableDocument.m | 610 +++++++++++++++++++++-------------- Source/TableDump.h | 4 +- Source/TableDump.m | 92 +++--- Source/TableSource.h | 1 - Source/TableSource.m | 28 +- Source/TablesList.h | 1 - Source/TablesList.m | 56 ++-- 47 files changed, 1433 insertions(+), 624 deletions(-) create mode 100644 Source/SPDocumentController.h create mode 100644 Source/SPDocumentController.m create mode 100644 Source/SPWindowController.h create mode 100644 Source/SPWindowController.m (limited to 'Source') diff --git a/Source/CMTextView.h b/Source/CMTextView.h index 7c09d827..51f2153d 100644 --- a/Source/CMTextView.h +++ b/Source/CMTextView.h @@ -31,10 +31,14 @@ #define SP_TEXT_SIZE_TRIGGER_FOR_PARTLY_PARSING 10000 -@class SPNarrowDownCompletion; +@class SPNarrowDownCompletion, TableDocument, TablesList, CustomQuery; @interface CMTextView : NSTextView { + IBOutlet TableDocument *tableDocumentInstance; + IBOutlet TablesList *tablesListInstance; + IBOutlet CustomQuery *customQueryInstance; + BOOL autoindentEnabled; BOOL autopairEnabled; BOOL autoindentIgnoresEnter; diff --git a/Source/CMTextView.m b/Source/CMTextView.m index 94e5a2bb..55c759a0 100644 --- a/Source/CMTextView.m +++ b/Source/CMTextView.m @@ -306,8 +306,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) // Add structural db/table/field data to completions list or fallback to gathering TablesList data NSString* connectionID; - if([[[self window] delegate] respondsToSelector:@selector(connectionID)]) - connectionID = [[[self window] delegate] connectionID]; + if(tableDocumentInstance) + connectionID = [tableDocumentInstance connectionID]; else connectionID = @"_"; @@ -325,14 +325,14 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) NSString *currentDb = nil; NSString *currentTable = nil; - if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"selectedDatabase"] != nil) - currentDb = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]]; - if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"tableName"] != nil) - currentTable = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"tableName"]; + if (tablesListInstance && [tablesListInstance selectedDatabase]) + currentDb = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, [tablesListInstance selectedDatabase]]; + if (tablesListInstance && [tablesListInstance tableName]) + currentTable = [tablesListInstance tableName]; // Put current selected db at the top - if(aTableName == nil && aDbName == nil && [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]) { - currentDb = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]]; + if(aTableName == nil && aDbName == nil && [tablesListInstance selectedDatabase]) { + currentDb = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, [tablesListInstance selectedDatabase]]; [sortedDbs removeObject:currentDb]; [sortedDbs insertObject:currentDb atIndex:0]; } @@ -481,34 +481,34 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) // [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:NSLocalizedString(@"fetching table data…", @"fetching table data for completion in progress message"), @"path", @"", @"noCompletion", nil]]; // Add all database names to completions list - for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allDatabaseNames"]) + for (id obj in [tablesListInstance allDatabaseNames]) [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"database-small", @"image", @"", @"isRef", nil]]; // Add all system database names to completions list - for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allSystemDatabaseNames"]) + for (id obj in [tablesListInstance allSystemDatabaseNames]) [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"database-small", @"image", @"", @"isRef", nil]]; // Add table names to completions list - for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allTableNames"]) + for (id obj in [tablesListInstance allTableNames]) [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"table-small-square", @"image", @"", @"isRef", nil]]; // Add view names to completions list - for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allViewNames"]) + for (id obj in [tablesListInstance allViewNames]) [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"table-view-small-square", @"image", @"", @"isRef", nil]]; // Add field names to completions list for currently selected table - if ([[[self window] delegate] table] != nil) - for (id obj in [[[[self window] delegate] valueForKeyPath:@"tableDataInstance"] valueForKey:@"columnNames"]) + if ([tableDocumentInstance table] != nil) + for (id obj in [[tableDocumentInstance valueForKeyPath:@"tableDataInstance"] valueForKey:@"columnNames"]) [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"field-small-square", @"image", @"", @"isRef", nil]]; // Add proc/func only for MySQL version 5 or higher if(mySQLmajorVersion > 4) { // Add all procedures to completions list for currently selected table - for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allProcedureNames"]) + for (id obj in [tablesListInstance allProcedureNames]) [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"proc-small", @"image", @"", @"isRef", nil]]; // Add all function to completions list for currently selected table - for (id obj in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allFunctionNames"]) + for (id obj in [tablesListInstance allFunctionNames]) [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:obj, @"display", @"func-small", @"image", @"", @"isRef", nil]]; } } @@ -620,8 +620,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) // Parse for leading db.table.field infos - if([[[self window] delegate] isKindOfClass:[TableDocument class]] && [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]) - currentDb = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]; + if(tablesListInstance && [tablesListInstance selectedDatabase]) + currentDb = [tablesListInstance selectedDatabase]; else currentDb = @""; @@ -947,7 +947,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) */ - (IBAction) showMySQLHelpForCurrentWord:(id)sender { - [[[[self window] delegate] valueForKeyPath:@"customQueryInstance"] showHelpForCurrentWord:self]; + [customQueryInstance showHelpForCurrentWord:self]; } /* @@ -1000,7 +1000,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) - (void) selectCurrentQuery { if([self isEditable]) - [[[[self window] delegate] valueForKeyPath:@"customQueryInstance"] selectCurrentQuery]; + [customQueryInstance selectCurrentQuery]; } /* @@ -1191,8 +1191,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) NSMutableArray *possibleCompletions = [[[NSMutableArray alloc] initWithCapacity:0] autorelease]; NSString *connectionID; - if([[[self window] delegate] respondsToSelector:@selector(connectionID)]) - connectionID = [[[self window] delegate] connectionID]; + if(tableDocumentInstance) + connectionID = [tableDocumentInstance connectionID]; else connectionID = @"_"; @@ -1200,8 +1200,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) if([kind isEqualToString:@"$SP_ASLIST_ALL_TABLES"]) { NSString *currentDb = nil; - if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"selectedDatabase"] != nil) - currentDb = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]; + if (tablesListInstance && [tablesListInstance selectedDatabase]) + currentDb = [tablesListInstance selectedDatabase]; NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[mySQLConnection getDbStructure] objectForKey:connectionID]]; @@ -1250,10 +1250,10 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) NSString *currentDb = nil; NSString *currentTable = nil; - if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"selectedDatabase"] != nil) - currentDb = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]; - if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"tableName"] != nil) - currentTable = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"tableName"]; + if (tablesListInstance && [tablesListInstance selectedDatabase]) + currentDb = [tablesListInstance selectedDatabase]; + if (tablesListInstance && [tablesListInstance tableName]) + currentTable = [tablesListInstance tableName]; NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[mySQLConnection getDbStructure] objectForKey:connectionID]]; if(currentDb != nil && currentTable != nil && dbs != nil && [dbs count] && [dbs objectForKey:currentDb] && [[dbs objectForKey:currentDb] objectForKey:currentTable]) { @@ -1291,7 +1291,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) } } } else { - arr = [NSArray arrayWithArray:[[[[self window] delegate] valueForKeyPath:@"tableDataInstance"] valueForKey:@"columnNames"]]; + arr = [NSArray arrayWithArray:[[tableDocumentInstance valueForKeyPath:@"tableDataInstance"] valueForKey:@"columnNames"]]; if(arr == nil) { arr = [NSArray array]; } @@ -1586,11 +1586,11 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) if([theHintString isMatchedByRegex:@"(? 0 && [(NSString*)[(NSDictionary*)[snippets objectAtIndex:0] objectForKey:@"query"] length]) { [self insertAsSnippet:[(NSDictionary*)[snippets objectAtIndex:0] objectForKey:@"query"] atRange:targetRange]; return; @@ -2427,14 +2427,14 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) // If selection show Help for it if([self selectedRange].length) { - [[[[self window] delegate] valueForKeyPath:@"customQueryInstance"] performSelector:@selector(showAutoHelpForCurrentWord:) withObject:self afterDelay:0.1]; + [customQueryInstance performSelector:@selector(showAutoHelpForCurrentWord:) withObject:self afterDelay:0.1]; return; } // Otherwise show Help if caret is not inside quotes NSUInteger cursorPosition = [self selectedRange].location; if (cursorPosition >= [[self string] length]) cursorPosition--; if(cursorPosition > -1 && (![[self textStorage] attribute:kQuote atIndex:cursorPosition effectiveRange:nil]||[[self textStorage] attribute:kSQLkeyword atIndex:cursorPosition effectiveRange:nil])) - [[[[self window] delegate] valueForKeyPath:@"customQueryInstance"] performSelector:@selector(showAutoHelpForCurrentWord:) withObject:self afterDelay:0.1]; + [customQueryInstance performSelector:@selector(showAutoHelpForCurrentWord:) withObject:self afterDelay:0.1]; } @@ -2856,7 +2856,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) // Hide "Select Active Query" if self is not editable [[menu itemAtIndex:4] setHidden:![self isEditable]]; - if([[[self window] delegate] valueForKeyPath:@"customQueryInstance"]) { + if(customQueryInstance) { [[menu itemAtIndex:5] setHidden:NO]; [[menu itemAtIndex:6] setHidden:NO]; } else { @@ -3129,10 +3129,10 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) NSString *currentDb = nil; NSString *currentTable = nil; - if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"selectedDatabase"] != nil) - currentDb = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]; - if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"tableName"] != nil) - currentTable = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"tableName"]; + if (tablesListInstance && [tablesListInstance selectedDatabase]) + currentDb = [tablesListInstance selectedDatabase]; + if (tablesListInstance && [tablesListInstance tableName]) + currentTable = [tablesListInstance tableName]; if(!currentDb) currentDb = @""; if(!currentTable) currentTable = @""; diff --git a/Source/CustomQuery.h b/Source/CustomQuery.h index 039f3a29..829083fe 100644 --- a/Source/CustomQuery.h +++ b/Source/CustomQuery.h @@ -53,7 +53,6 @@ { IBOutlet id tableDocumentInstance; IBOutlet id tablesListInstance; - IBOutlet id tableWindow; IBOutlet id queryFavoritesButton; IBOutlet NSMenuItem *queryFavoritesSearchMenuItem; diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m index 7df5615f..6020a255 100644 --- a/Source/CustomQuery.m +++ b/Source/CustomQuery.m @@ -172,14 +172,14 @@ // 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, + SPBeginAlertSheet(NSLocalizedString(@"Empty query", @"empty query message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, NSLocalizedString(@"Cannot save an empty query.", @"empty query informative message")); return; } if ([tableDocumentInstance isUntitled]) [saveQueryFavoriteGlobal setState:NSOnState]; [NSApp beginSheet:queryFavoritesSheet - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"addSelectionToNewQueryFavorite"]; @@ -190,14 +190,14 @@ // 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, + SPBeginAlertSheet(NSLocalizedString(@"Empty query", @"empty query message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, NSLocalizedString(@"Cannot save an empty query.", @"empty query informative message")); return; } if ([tableDocumentInstance isUntitled]) [saveQueryFavoriteGlobal setState:NSOnState]; [NSApp beginSheet:queryFavoritesSheet - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"addAllToNewQueryFavorite"]; @@ -211,7 +211,7 @@ // Open query favorite manager [NSApp beginSheet:[favoritesManager window] - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:favoritesManager didEndSelector:nil contextInfo:nil]; @@ -396,7 +396,7 @@ [encodingPopUp setEnabled:YES]; - [panel beginSheetForDirectory:nil file:@"history" modalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:@"saveHistory"]; + [panel beginSheetForDirectory:nil file:@"history" modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:@"saveHistory"]; } - (IBAction)copyQueryHistory:(id)sender @@ -435,7 +435,7 @@ [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - [alert beginSheetModalForWindow:tableWindow + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"clearHistory"]; @@ -652,14 +652,14 @@ // Reload table list if at least one query began with drop, alter, rename, or create if(tableListNeedsReload || databaseWasChanged) { // Build database pulldown menu - [[tableWindow delegate] setDatabases:self]; + [tableDocumentInstance setDatabases:self]; if (databaseWasChanged) // Reset the current database - [[tableWindow delegate] refreshCurrentDatabase]; + [tableDocumentInstance refreshCurrentDatabase]; // Reload table list - [[[tableWindow delegate] valueForKeyPath:@"tablesListInstance"] updateTables:self]; + [tablesListInstance updateTables:self]; } @@ -745,7 +745,7 @@ // Perform the Growl notification for query completion [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Query Finished" description:[NSString stringWithFormat:NSLocalizedString(@"%@",@"description for query finished growl notification"), [errorText stringValue]] - window:tableWindow + document:tableDocumentInstance notificationName:@"Query Finished"]; // Set up the callback if present @@ -799,7 +799,7 @@ // Query finished Growl notification [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Query Finished" description:[NSString stringWithFormat:NSLocalizedString(@"%@",@"description for query finished growl notification"), [errorText stringValue]] - window:tableWindow + document:tableDocumentInstance notificationName:@"Query Finished"]; // Set up the callback if present @@ -1677,7 +1677,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, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, [tableDocumentInstance parentWindow], 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; @@ -1687,7 +1687,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, + SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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(); @@ -1702,7 +1702,7 @@ [self performQueries:[NSArray arrayWithObject:lastExecutedQuery] withCallback:NULL]; } else { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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]); @@ -2039,7 +2039,7 @@ usingEncoding:[mySQLConnection encoding] isObjectBlob:isBlob isEditable:isFieldEditable - withWindow:tableWindow] retain]; + withWindow:[tableDocumentInstance parentWindow]] retain]; if ( editData ) [self tableView:aTableView setObjectValue:[editData copy] forTableColumn:aTableColumn row:rowIndex]; @@ -3196,9 +3196,9 @@ // Send moveDown/Up to the popup menu NSEvent *arrowEvent; if(commandSelector == @selector(moveDown:)) - arrowEvent = [NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[tableWindow windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x7D]; + arrowEvent = [NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[[tableDocumentInstance parentWindow] windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x7D]; else - arrowEvent = [NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[tableWindow windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x7E]; + arrowEvent = [NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[[tableDocumentInstance parentWindow] windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x7E]; [[NSApplication sharedApplication] postEvent:arrowEvent atStart:NO]; return YES; diff --git a/Source/SPAppController.h b/Source/SPAppController.h index 86b9db13..e1ed4291 100644 --- a/Source/SPAppController.h +++ b/Source/SPAppController.h @@ -26,7 +26,7 @@ #import #import -@class SPPreferenceController, SPAboutController; +@class SPPreferenceController, SPAboutController, TableDocument; @interface SPAppController : NSObject { @@ -38,6 +38,11 @@ id encodingPopUp; } +// Window management +- (IBAction)newWindow:(id)sender; +- (IBAction)newTab:(id)sender; +- (NSWindow *) frontDocumentWindow; + // IBAction methods - (IBAction)openAboutPanel:(id)sender; - (IBAction)openPreferences:(id)sender; @@ -56,6 +61,8 @@ // Getters - (SPPreferenceController *)preferenceController; +- (NSArray *) orderedDatabaseConnectionWindows; +- (TableDocument *) frontDocument; // Feedback controller delegate methods - (NSMutableDictionary*) anonymizePreferencesForFeedbackReport:(NSMutableDictionary *)preferences; diff --git a/Source/SPAppController.m b/Source/SPAppController.m index b80e0680..5adacb8b 100644 --- a/Source/SPAppController.m +++ b/Source/SPAppController.m @@ -31,13 +31,14 @@ #import "TableDump.h" #import "SPEncodingPopupAccessory.h" #import "SPConstants.h" +#import "SPWindowController.h" #import @implementation SPAppController /** - * Inialise the application's main controller, setting itself as the app delegate. + * Initialise the application's main controller, setting itself as the app delegate. */ - (id)init { @@ -96,14 +97,6 @@ */ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { - if ([menuItem action] == @selector(openConnectionSheet:) && [menuItem tag] == 0) - { - // Do not allow to open a sql/spf file if SP asks for connection details - if ([[[NSDocumentController sharedDocumentController] documents] count]) { - if(![[[[NSDocumentController sharedDocumentController] currentDocument] mySQLVersion] length]) - return NO; - } - } if ([menuItem action] == @selector(openCurrentConnectionInNewWindow:)) { [menuItem setTitle:NSLocalizedString(@"Open in New Window", @"menu item open in new window")]; @@ -160,13 +153,13 @@ // it will enabled if user selects a *.sql file [encodingPopUp setEnabled:NO]; - + // Check if at least one document exists, if so show a sheet - if ([[[NSDocumentController sharedDocumentController] documents] count]) { + if ([self frontDocumentWindow]) { [panel beginSheetForDirectory:nil file:@"" types:[NSArray arrayWithObjects:@"spf", @"sql", nil] - modalForWindow:[[[NSDocumentController sharedDocumentController] currentDocument] valueForKey:@"tableWindow"] + modalForWindow:[self frontDocumentWindow] modalDelegate:self didEndSelector:@selector(openConnectionPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; @@ -220,7 +213,7 @@ [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"cancel button")]; // Show 'Import' button only if there's a connection available - if ([[[NSDocumentController sharedDocumentController] documents] count]) + if ([self frontDocument]) [alert addButtonWithTitle:NSLocalizedString(@"Import", @"import button")]; @@ -237,49 +230,44 @@ if(returnCode == NSAlertSecondButtonReturn) return; // Cancel else if(returnCode == NSAlertThirdButtonReturn) { // Import // begin import process - [[[[NSDocumentController sharedDocumentController] currentDocument] valueForKeyPath:@"tableDumpInstance"] startSQLImportProcessWithFile:filename]; + [[[self frontDocument] valueForKeyPath:@"tableDumpInstance"] startSQLImportProcessWithFile:filename]; return; } } } } + // Attempt to open the file into a string. + NSString *sqlString = nil; + + // If the user came from an openPanel use the chosen encoding + if (encodingPopUp) { + NSError *error = nil; + sqlString = [NSString stringWithContentsOfFile:filename encoding:[[encodingPopUp selectedItem] tag] error:&error]; + if(error != nil) { + NSAlert *errorAlert = [NSAlert alertWithError:error]; + [errorAlert runModal]; + return; + } + + // Otherwise, read while attempting to autodetect the encoding + } else { + sqlString = [self contentOfFile:filename]; + } + // if encodingPopUp is defined the filename comes from an openPanel and // the encodingPopUp contains the chosen encoding; otherwise autodetect encoding if(encodingPopUp) [[NSUserDefaults standardUserDefaults] setInteger:[[encodingPopUp selectedItem] tag] forKey:SPLastSQLFileEncoding]; - // Check if at least one document exists - if (![[[NSDocumentController sharedDocumentController] documents] count]) { - - TableDocument *firstTableDocument; - - // Manually open a new document, setting SPAppController as sender to trigger autoconnection - if (firstTableDocument = [[NSDocumentController sharedDocumentController] makeUntitledDocumentOfType:@"Sequel Pro connection" error:nil]) { - [firstTableDocument setShouldAutomaticallyConnect:NO]; - - // user comes from a openPanel? if so use the chosen encoding - if(encodingPopUp) { - NSError *error = nil; - NSString *content = [NSString stringWithContentsOfFile:filename encoding:[[encodingPopUp selectedItem] tag] error:&error]; - if(error != nil) { - NSAlert *errorAlert = [NSAlert alertWithError:error]; - [errorAlert runModal]; - return; - } - [firstTableDocument initQueryEditorWithString:content]; - - } - else - [firstTableDocument initQueryEditorWithString:[self contentOfFile:filename]]; - - [[NSDocumentController sharedDocumentController] addDocument:firstTableDocument]; - [firstTableDocument makeWindowControllers]; - [firstTableDocument showWindows]; - } + // Check if at least one document exists. If not, open one. + if (![self frontDocument]) { + [self newWindow:self]; + [[self frontDocument] initQueryEditorWithString:sqlString]; } else { + // Pass query to the Query editor of the current document - [[[NSDocumentController sharedDocumentController] currentDocument] doPerformLoadQueryService:[self contentOfFile:filename]]; + [[self frontDocument] doPerformLoadQueryService:[self contentOfFile:filename]]; } break; // open only the first SQL file @@ -289,15 +277,12 @@ TableDocument *newTableDocument; - // Manually open a new document, setting SPAppController as sender to trigger autoconnection - if (newTableDocument = [[NSDocumentController sharedDocumentController] makeUntitledDocumentOfType:@"Sequel Pro connection" error:nil]) { - [newTableDocument setShouldAutomaticallyConnect:NO]; - [[NSDocumentController sharedDocumentController] addDocument:newTableDocument]; - [newTableDocument makeWindowControllers]; - [newTableDocument showWindows]; - [newTableDocument initWithConnectionFile:filename]; + // If the frontmost document isn't connected and hasn't been, open the connection file with it. + // Otherwise, manually open a new document, setting SPAppController as sender to trigger autoconnection + if ([[self frontDocument] mySQLVersion]) { + [self newWindow:self]; } - + [[self frontDocument] initWithConnectionFile:filename]; } else { NSLog(@"Only files with the extensions ‘spf’ or ‘sql’ are allowed."); @@ -305,6 +290,81 @@ } } +#pragma mark - +#pragma mark Window management + +/** + * Create a new window, containing a single tab. + */ +- (IBAction)newWindow:(id)sender +{ + static NSPoint cascadeLocation = {.x = 0, .y = 0}; + + // Create a new window controller, and set up a new connection view within it. + SPWindowController *newWindowController = [[SPWindowController alloc] initWithWindowNibName:@"MainWindow"]; + NSWindow *newWindow = [newWindowController window]; + [newWindow setReleasedWhenClosed:YES]; + + // Cascading defaults to on - retrieve the window origin automatically assigned by cascading, + // and convert to a top left point. + NSPoint topLeftPoint = [newWindow frame].origin; + topLeftPoint.y += [newWindow frame].size.height; + + // The first window should use autosaving; subsequent windows should cascade. + // So attempt to set the frame autosave name; this will succeed for the very + // first window, and fail for others. + BOOL usedAutosave = [newWindow setFrameAutosaveName:@"DBView"]; + if (!usedAutosave) { + [newWindow setFrameUsingName:@"DBView"]; + } + + // Cascade according to the statically stored cascade location. + cascadeLocation = [newWindow cascadeTopLeftFromPoint:cascadeLocation]; + + // Set the window controller as the window's delegate + [newWindow setDelegate:newWindowController]; + + // Show the window + [newWindowController showWindow:self]; +} + +/** + * Create a new tab in the frontmost window. + */ +- (IBAction)newTab:(id)sender +{ + SPWindowController *frontController = nil; + + for (NSWindow *aWindow in [self orderedWindows]) { + if ([[aWindow windowController] isMemberOfClass:[SPWindowController class]]) { + frontController = [aWindow windowController]; + break; + } + } + + // If no window was found, create a new one + if (!frontController) { + [self newWindow:self]; + } else { + if ([[frontController window] isMiniaturized]) [[frontController window] deminiaturize:self]; + [frontController addNewConnection:self]; + } +} + +/** + * Retrieve the frontmost document window; returns nil if not found. + */ +- (NSWindow *) frontDocumentWindow +{ + for (NSWindow *aWindow in [self orderedWindows]) { + if ([[aWindow windowController] isMemberOfClass:[SPWindowController class]]) { + return aWindow; + } + } + + return nil; +} + #pragma mark - #pragma mark IBAction methods @@ -337,11 +397,38 @@ return prefsController; } +/** + * Provide a method to retrieve an ordered list of the database + * connection windows currently open in the application. + */ +- (NSArray *) orderedDatabaseConnectionWindows +{ + NSMutableArray *orderedDatabaseConnectionWindows = [NSMutableArray array]; + for (NSWindow *aWindow in [NSApp orderedWindows]) { + if ([[aWindow windowController] isMemberOfClass:[SPWindowController class]]) [orderedDatabaseConnectionWindows addObject:aWindow]; + } + return orderedDatabaseConnectionWindows; +} + +/** + * Retrieve the frontmost document; returns nil if not found. + */ +- (TableDocument *) frontDocument +{ + for (NSWindow *aWindow in [self orderedWindows]) { + if ([[aWindow windowController] isMemberOfClass:[SPWindowController class]]) { + return [[aWindow windowController] selectedTableDocument]; + } + } + + return nil; +} + #pragma mark - #pragma mark Services menu methods /** - * Passes the query to the last created document + * Passes the query to the frontmost document */ - (void)doPerformQueryService:(NSPasteboard *)pboard userData:(NSString *)data error:(NSString **)error { @@ -356,14 +443,14 @@ } // Check if at least one document exists - if (![[[NSDocumentController sharedDocumentController] documents] count]) { + if (![self frontDocument]) { *error = @"No Documents open!"; return; } - // Pass query to last created document - [[[[NSDocumentController sharedDocumentController] documents] objectAtIndex:([[[NSDocumentController sharedDocumentController] documents] count] - 1)] doPerformQueryService:pboardString]; + // Pass query to front document + [[self frontDocument] doPerformQueryService:pboardString]; return; } @@ -446,21 +533,18 @@ #pragma mark Other methods /** - * Override the default open-blank-document methods to automatically connect automatically opened windows. + * Override the default open-blank-document methods to automatically connect automatically opened windows + * if the preference is set */ - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender { - TableDocument *firstTableDocument; - // Manually open a new document, setting SPAppController as sender to trigger autoconnection - if (firstTableDocument = [[NSDocumentController sharedDocumentController] makeUntitledDocumentOfType:@"Sequel Pro connection" error:nil]) { - if ([[NSUserDefaults standardUserDefaults] boolForKey:SPAutoConnectToDefault]) { - [firstTableDocument setShouldAutomaticallyConnect:YES]; - } - - [[NSDocumentController sharedDocumentController] addDocument:firstTableDocument]; - [firstTableDocument makeWindowControllers]; - [firstTableDocument showWindows]; + // Manually open a table document + [self newWindow:self]; + + // Set autoconnection if appropriate + if ([[NSUserDefaults standardUserDefaults] boolForKey:SPAutoConnectToDefault]) { + [[self frontDocument] connect]; } // Return NO to the automatic opening @@ -474,16 +558,8 @@ - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag { // Only create a new document (without auto-connect) when there are already no documents open. - if ([[[NSDocumentController sharedDocumentController] documents] count] == 0) { - TableDocument *firstTableDocument; - - // Manually open a new document, setting SPAppController as sender to trigger autoconnection - if (firstTableDocument = [[NSDocumentController sharedDocumentController] makeUntitledDocumentOfType:@"Sequel Pro connection" error:nil]) { - [[NSDocumentController sharedDocumentController] addDocument:firstTableDocument]; - [firstTableDocument makeWindowControllers]; - [firstTableDocument showWindows]; - } - + if (![self frontDocument]) { + [self newWindow:self]; return NO; } @@ -620,20 +696,30 @@ */ - (NSArray *)orderedDocuments { - return [[NSDocumentController sharedDocumentController] documents]; + NSMutableArray *orderedDocuments = [NSMutableArray array]; + + for (NSWindow *aWindow in [self orderedWindows]) { + if ([[aWindow windowController] isMemberOfClass:[SPWindowController class]]) { + [orderedDocuments addObjectsFromArray:[[aWindow windowController] documents]]; + } + } + + return orderedDocuments; } /** * Support for 'make new document'. + * TODO: following tab support this has been disabled - need to discuss reimplmenting vs syntax. */ - (void)insertInOrderedDocuments:(TableDocument *)doc { - if ([[NSUserDefaults standardUserDefaults] boolForKey:SPAutoConnectToDefault]) + [self newWindow:self]; +/* if ([[NSUserDefaults standardUserDefaults] boolForKey:SPAutoConnectToDefault]) [doc setShouldAutomaticallyConnect:YES]; [[NSDocumentController sharedDocumentController] addDocument:doc]; [doc makeWindowControllers]; - [doc showWindows]; + [doc showWindows];*/ } /** diff --git a/Source/SPConnectionController.h b/Source/SPConnectionController.h index 7e518e26..3e272abc 100644 --- a/Source/SPConnectionController.h +++ b/Source/SPConnectionController.h @@ -50,8 +50,8 @@ id delegate; TableDocument *tableDocument; - NSWindow *documentWindow; - NSSplitView *contentView; + NSView *databaseConnectionSuperview; + NSSplitView *databaseConnectionView; SPKeychain *keychain; NSUserDefaults *prefs; NSMutableArray *favorites; @@ -79,6 +79,8 @@ NSString *connectionSSHKeychainItemName; NSString *connectionSSHKeychainItemAccount; + NSMutableArray *nibObjectsToRelease; + IBOutlet NSView *connectionView; IBOutlet NSSplitView *connectionSplitView; IBOutlet BWAnchoredButtonBar *connectionSplitViewButtonBar; diff --git a/Source/SPConnectionController.m b/Source/SPConnectionController.m index 933accfb..582f80da 100644 --- a/Source/SPConnectionController.m +++ b/Source/SPConnectionController.m @@ -60,8 +60,8 @@ { if (self = [super init]) { tableDocument = theTableDocument; - documentWindow = [tableDocument valueForKey:@"tableWindow"]; - contentView = [tableDocument valueForKey:@"contentViewSplitter"]; + databaseConnectionSuperview = [tableDocument parentView]; + databaseConnectionView = [tableDocument valueForKey:@"contentViewSplitter"]; connectionKeychainItemName = nil; connectionKeychainItemAccount = nil; connectionSSHKeychainItemName = nil; @@ -70,20 +70,21 @@ sshTunnel = nil; cancellingConnection = NO; - // Load the connection nib - [NSBundle loadNibNamed:@"ConnectionView" owner:self]; + // Load the connection nib, keeping references to the top-level objects for later release + nibObjectsToRelease = [[NSMutableArray alloc] init]; + NSArray *connectionViewTopLevelObjects = nil; + NSNib *nibLoader = [[NSNib alloc] initWithNibNamed:@"ConnectionView" bundle:[NSBundle mainBundle]]; + [nibLoader instantiateNibWithOwner:self topLevelObjects:&connectionViewTopLevelObjects]; + [nibObjectsToRelease addObjectsFromArray:connectionViewTopLevelObjects]; + [nibLoader release]; // Hide the main view and position and display the connection view - [contentView setHidden:YES]; - [connectionView setFrame:[contentView frame]]; - [[documentWindow contentView] addSubview:connectionView]; + [databaseConnectionView setHidden:YES]; + [connectionView setFrame:[databaseConnectionView frame]]; + [databaseConnectionSuperview addSubview:connectionView]; [connectionSplitView setPosition:[[tableDocument valueForKey:@"dbTablesTableView"] frame].size.width ofDividerAtIndex:0]; [connectionSplitViewButtonBar setSplitViewDelegate:self]; - // Disable the toolbar icons - NSArray *toolbarItems = [[documentWindow toolbar] items]; - for (NSInteger i = 0; i < [toolbarItems count]; i++) [[toolbarItems objectAtIndex:i] setEnabled:NO]; - // Set up a keychain instance and preferences reference, and create the initial favorites list keychain = [[SPKeychain alloc] init]; prefs = [[NSUserDefaults standardUserDefaults] retain]; @@ -98,7 +99,7 @@ [favoritesTable setDoubleAction:@selector(initiateConnection:)]; // Set the focus to the favorites table and select the appropriate row - [documentWindow setInitialFirstResponder:favoritesTable]; + [[tableDocument parentWindow] setInitialFirstResponder:favoritesTable]; NSInteger tableRow; if ([prefs boolForKey:SPSelectLastFavoriteUsed] == YES) { tableRow = [prefs integerForKey:SPLastFavoriteIndex] + 1; @@ -114,11 +115,6 @@ previousType = SPTCPIPConnection; [self resizeTabViewToConnectionType:SPTCPIPConnection animating:NO]; } - - // If the document is set to automatically connect, do so. - if ([tableDocument shouldAutomaticallyConnect]) { - [self performSelector:@selector(initiateConnection:) withObject:self afterDelay:0.0]; - } } return self; @@ -129,6 +125,10 @@ [prefs removeObserver:self forKeyPath:SPFavorites]; [keychain release]; [prefs release]; + + for (id retainedObject in nibObjectsToRelease) [retainedObject release]; + [nibObjectsToRelease release]; + if (favorites) [favorites release]; if (mySQLConnection) [mySQLConnection release]; if (sshTunnel) [sshTunnel setConnectionStateChangeSelector:nil delegate:nil], [sshTunnel disconnect], [sshTunnel release]; @@ -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, 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, [tableDocument parentWindow], 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, 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, [tableDocument parentWindow], 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; } @@ -185,9 +185,9 @@ if (connectionKeychainItemName) { if ([[keychain getPasswordForName:connectionKeychainItemName account:connectionKeychainItemAccount] isEqualToString:[self password]]) { [self setPassword:[[NSString string] stringByPaddingToLength:[[self password] length] withString:@"sp" startingAtIndex:0]]; - [[tableDocument undoManager] removeAllActionsWithTarget:standardPasswordField]; - [[tableDocument undoManager] removeAllActionsWithTarget:socketPasswordField]; - [[tableDocument undoManager] removeAllActionsWithTarget:sshPasswordField]; + [[standardPasswordField undoManager] removeAllActionsWithTarget:standardPasswordField]; + [[socketPasswordField undoManager] removeAllActionsWithTarget:socketPasswordField]; + [[sshPasswordField undoManager] removeAllActionsWithTarget:sshPasswordField]; } else { [connectionKeychainItemName release], connectionKeychainItemName = nil; [connectionKeychainItemAccount release], connectionKeychainItemAccount = nil; @@ -196,7 +196,7 @@ if (connectionSSHKeychainItemName) { if ([[keychain getPasswordForName:connectionSSHKeychainItemName account:connectionSSHKeychainItemAccount] isEqualToString:[self sshPassword]]) { [self setSshPassword:[[NSString string] stringByPaddingToLength:[[self sshPassword] length] withString:@"sp" startingAtIndex:0]]; - [[tableDocument undoManager] removeAllActionsWithTarget:sshSSHPasswordField]; + [[sshSSHPasswordField undoManager] removeAllActionsWithTarget:sshSSHPasswordField]; } else { [connectionSSHKeychainItemName release], connectionSSHKeychainItemName = nil; [connectionSSHKeychainItemAccount release], connectionSSHKeychainItemAccount = nil; @@ -236,7 +236,7 @@ // Set up the tunnel details sshTunnel = [[SPSSHTunnel alloc] initToHost:[self sshHost] port:([[self sshPort] length]?[[self sshPort] integerValue]:22) login:[self sshUser] tunnellingToPort:([[self port] length]?[[self port] integerValue]:3306) onHost:[self host]]; - [sshTunnel setParentWindow:documentWindow]; + [sshTunnel setParentWindow:[tableDocument parentWindow]]; // Add keychain or plaintext password as appropriate - note the checks in initiateConnection. if (connectionSSHKeychainItemName) { @@ -437,8 +437,8 @@ } // 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, @selector(connectionFailureSheetDidEnd:returnCode:contextInfo:), @"connect", theErrorMessage); + if ([[tableDocument parentWindow] 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, [tableDocument parentWindow], self, @selector(connectionFailureSheetDidEnd:returnCode:contextInfo:), @"connect", theErrorMessage); } } @@ -491,10 +491,10 @@ // Hide the connection view and restore the main view [connectionView removeFromSuperviewWithoutNeedingDisplay]; - [contentView setHidden:NO]; + [databaseConnectionView setHidden:NO]; // Restore the toolbar icons - NSArray *toolbarItems = [[documentWindow toolbar] items]; + NSArray *toolbarItems = [[[tableDocument parentWindow] toolbar] items]; for (NSInteger i = 0; i < [toolbarItems count]; i++) [[toolbarItems objectAtIndex:i] setEnabled:YES]; // Set keychain id for saving SPF files @@ -628,7 +628,7 @@ NSLocalizedString(@"Use 127.0.0.1", @"Use 127.0.0.1 button"), // Main button NSLocalizedString(@"Connect via socket", @"Connect via socket button"), // Alternate button nil, // Other button - documentWindow, // Window to attach to + [tableDocument parentWindow], // Window to attach to self, // Modal delegate @selector(localhostErrorSheetDidEnd:returnCode:contextInfo:), // Did end selector nil, // Contextual info for selectors @@ -728,15 +728,15 @@ switch([self type]) { case SPTCPIPConnection: if(![[standardPasswordField stringValue] length]) - [documentWindow makeFirstResponder:standardPasswordField]; + [[tableDocument parentWindow] makeFirstResponder:standardPasswordField]; break; case SPSocketConnection: if(![[socketPasswordField stringValue] length]) - [documentWindow makeFirstResponder:socketPasswordField]; + [[tableDocument parentWindow] makeFirstResponder:socketPasswordField]; break; case SPSSHTunnelConnection: if(![[sshPasswordField stringValue] length]) - [documentWindow makeFirstResponder:sshPasswordField]; + [[tableDocument parentWindow] makeFirstResponder:sshPasswordField]; break; } } @@ -928,7 +928,7 @@ */ - (void) splitViewDidResizeSubviews:(NSNotification *)aNotification { - [contentView setPosition:[[[connectionSplitView subviews] objectAtIndex:0] frame].size.width ofDividerAtIndex:0]; + [databaseConnectionView setPosition:[[[connectionSplitView subviews] objectAtIndex:0] frame].size.width ofDividerAtIndex:0]; } /** diff --git a/Source/SPConnectionDelegate.m b/Source/SPConnectionDelegate.m index 258b4402..65ffa3f9 100644 --- a/Source/SPConnectionDelegate.m +++ b/Source/SPConnectionDelegate.m @@ -103,7 +103,7 @@ */ - (void)noConnectionAvailable:(id)connection { - 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")); + SPBeginAlertSheet(NSLocalizedString(@"No connection available", @"no connection available message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self parentWindow], self, nil, nil, NSLocalizedString(@"An error has occured and there doesn't seem to be a connection available.", @"no connection available informatie message")); } /** @@ -114,10 +114,13 @@ NSInteger connectionErrorCode = MCPConnectionCheckDisconnect; // Only display the reconnect dialog if the window is visible - if ([tableWindow isVisible]) { + if ([self parentWindow] && [[self parentWindow] isVisible]) { + + // Ensure the window and tab are frontmost + [self makeKeyDocument]; // Display the connection error dialog and wait for the return code - [NSApp beginSheet:connectionErrorDialog modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [NSApp beginSheet:connectionErrorDialog modalForWindow:[self parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; connectionErrorCode = [NSApp runModalForWindow:connectionErrorDialog]; [NSApp endSheet:connectionErrorDialog]; @@ -125,7 +128,7 @@ // If 'disconnect' was selected, trigger a window close. if (connectionErrorCode == MCPConnectionCheckDisconnect) { - [self performSelectorOnMainThread:@selector(closeDocumentWindowAndDisconnect) withObject:nil waitUntilDone:YES]; + [self performSelectorOnMainThread:@selector(closeAndDisconnect) withObject:nil waitUntilDone:YES]; } } @@ -137,8 +140,8 @@ */ - (void)showErrorWithTitle:(NSString *)theTitle message:(NSString *)theMessage { - if ([tableWindow isVisible]) { - SPBeginAlertSheet(theTitle, NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, theMessage); + if ([[self parentWindow] isVisible]) { + SPBeginAlertSheet(theTitle, NSLocalizedString(@"OK", @"OK button"), nil, nil, [self parentWindow], self, nil, nil, theMessage); } } @@ -152,17 +155,20 @@ /** * Close the connection - should be performed on the main thread. - * First hides the window to give code a little bit of time to clean - * everything up before it's all deallocated as a result of the close. - * Also sets alpha to fully transparent so accidental dialogs are hidden! */ -- (void) closeDocumentWindowAndDisconnect +- (void) closeAndDisconnect { - _isConnected = NO; - [self windowWillClose:nil]; - [tableWindow orderOut:self]; - [tableWindow setAlphaValue:0.0]; - [tableWindow performSelector:@selector(close) withObject:nil afterDelay:1.0]; + NSWindow *theParentWindow = [self parentWindow]; + _isConnected = NO; + if ([[[self parentTabViewItem] tabView] numberOfTabViewItems] == 1) { + [theParentWindow orderOut:self]; + [theParentWindow setAlphaValue:0.0]; + [theParentWindow performSelector:@selector(close) withObject:nil afterDelay:1.0]; + } else { + [[[self parentTabViewItem] tabView] performSelector:@selector(removeTabViewItem:) withObject:[self parentTabViewItem] afterDelay:0.5]; + [theParentWindow performSelector:@selector(makeKeyAndOrderFront:) withObject:nil afterDelay:0.6]; + } + [self parentTabDidClose]; } @end diff --git a/Source/SPConstants.h b/Source/SPConstants.h index a547d6c1..d893b825 100644 --- a/Source/SPConstants.h +++ b/Source/SPConstants.h @@ -109,6 +109,19 @@ typedef enum SPLinkDrawStateBackgroundHighlight = 2 } SPTextAndLinkCellDrawState; +// Menu tag constants +typedef enum +{ + SPMainMenuSequelPro = 0, + SPMainMenuFile = 1, + SPMainMenuEdit = 2, + SPMainMenuView = 3, + SPMainMenuDatabase = 4, + SPMainMenuTable = 5, + SPMainMenuWindow = 6, + SPMainMenuHelp = 7 +} SPMainMenuTags; + // Long running notification time for Growl messages extern const CGFloat SPLongRunningNotificationTime; diff --git a/Source/SPContentFilterManager.h b/Source/SPContentFilterManager.h index 7c6525ba..ba41a34f 100644 --- a/Source/SPContentFilterManager.h +++ b/Source/SPContentFilterManager.h @@ -25,7 +25,7 @@ #import -@class BWAnchoredButtonBar; +@class BWAnchoredButtonBar, TableDocument; @interface NSObject (SPContentFilterManagerDelegate) @@ -37,6 +37,7 @@ { NSUserDefaults *prefs; + TableDocument *tableDocumentInstance; NSURL *delegatesFileURL; IBOutlet id encodingPopUp; IBOutlet id contentFilterTableView; diff --git a/Source/SPContentFilterManager.m b/Source/SPContentFilterManager.m index 30defe7a..ac9bef86 100644 --- a/Source/SPContentFilterManager.m +++ b/Source/SPContentFilterManager.m @@ -58,7 +58,8 @@ NSLog(@"ContentFilterManager was called without a delegate."); return nil; } - delegatesFileURL = [[managerDelegate valueForKeyPath:@"tableDocumentInstance"] fileURL]; + tableDocumentInstance = [managerDelegate valueForKeyPath:@"tableDocumentInstance"]; + delegatesFileURL = [tableDocumentInstance fileURL]; filterType = [NSString stringWithString:compareType]; @@ -211,7 +212,7 @@ */ - (id)customQueryInstance { - return [[[NSApp mainWindow] delegate] valueForKey:@"customQueryInstance"]; + return [tableDocumentInstance valueForKey:@"customQueryInstance"]; } @@ -371,7 +372,7 @@ [cf release]; // Inform all opened documents to update the query favorites list - for(id doc in [[NSDocumentController sharedDocumentController] documents]) + for(id doc in [[NSApp delegate] orderedDocuments]) if([[doc valueForKeyPath:@"tableContentInstance"] respondsToSelector:@selector(setCompareTypes:)]) [[doc valueForKeyPath:@"tableContentInstance"] setCompareTypes:nil]; diff --git a/Source/SPDocumentController.h b/Source/SPDocumentController.h new file mode 100644 index 00000000..237e71a3 --- /dev/null +++ b/Source/SPDocumentController.h @@ -0,0 +1,32 @@ +// +// $Id$ +// +// SPDocumentController.h +// sequel-pro +// +// Created by Rowan Beentje on May 23, 2010 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import + + +@interface SPDocumentController : NSDocument { + +} + +@end diff --git a/Source/SPDocumentController.m b/Source/SPDocumentController.m new file mode 100644 index 00000000..baec776b --- /dev/null +++ b/Source/SPDocumentController.m @@ -0,0 +1,36 @@ +// +// $Id$ +// +// SPDocumentController.h +// sequel-pro +// +// Created by Rowan Beentje on May 23, 2010 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + + +#import "SPDocumentController.h" + + +@implementation SPDocumentController + +/** + * This is an empty, dummy implementation; this allows Sequel Pro to behave + * as if it were a document-based application. + */ + +@end diff --git a/Source/SPExportController.h b/Source/SPExportController.h index b1c50933..773b678d 100644 --- a/Source/SPExportController.h +++ b/Source/SPExportController.h @@ -52,7 +52,6 @@ typedef NSUInteger SPExportSource; { // Table document IBOutlet id tableDocumentInstance; - IBOutlet id tableWindow; // Tables list IBOutlet id tablesListInstance; @@ -122,7 +121,9 @@ typedef NSUInteger SPExportSource; // Table/export operation mapping NSMutableDictionary *tableExportMapping; - + + // Top-level nib objects that require releasing on dealloc + NSMutableArray *nibObjectsToRelease; } @property (readwrite, assign) BOOL exportCancelled; diff --git a/Source/SPExportController.m b/Source/SPExportController.m index dce7857b..559e8071 100644 --- a/Source/SPExportController.m +++ b/Source/SPExportController.m @@ -54,6 +54,7 @@ tables = [[NSMutableArray alloc] init]; operationQueue = [[NSOperationQueue alloc] init]; tableExportMapping = [NSMutableDictionary dictionary]; + nibObjectsToRelease = [[NSMutableArray alloc] init]; } return self; @@ -76,7 +77,16 @@ */ - (void)export { - if (!exportWindow) [NSBundle loadNibNamed:@"ExportDialog" owner:self]; + + // If the dialog hasn't been loaded yet, do so, retaining a reference to the top-level objects that need releasing. + if (!exportWindow) + { + NSArray *exportDialogTopLevelObjects = nil; + NSNib *nibLoader = [[NSNib alloc] initWithNibNamed:@"ExportDialog" bundle:[NSBundle mainBundle]]; + [nibLoader instantiateNibWithOwner:self topLevelObjects:&exportDialogTopLevelObjects]; + [nibObjectsToRelease addObjectsFromArray:exportDialogTopLevelObjects]; + [nibLoader release]; + } NSUInteger i; @@ -99,7 +109,7 @@ [exportPathField setStringValue:NSHomeDirectory()]; [NSApp beginSheet:exportWindow - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; @@ -277,6 +287,8 @@ { [tables release], tables = nil; [operationQueue release], operationQueue = nil; + for (id retainedObject in nibObjectsToRelease) [retainedObject release]; + [nibObjectsToRelease release], nibObjectsToRelease = nil; [super dealloc]; } @@ -364,7 +376,7 @@ // Open the progress sheet [NSApp beginSheet:exportProgressWindow - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; diff --git a/Source/SPFieldEditorController.h b/Source/SPFieldEditorController.h index d6a0be1e..6ba9018b 100644 --- a/Source/SPFieldEditorController.h +++ b/Source/SPFieldEditorController.h @@ -75,7 +75,7 @@ - (IBAction)dropImage:(id)sender; - (id)editWithObject:(id)data fieldName:(NSString*)fieldName usingEncoding:(NSStringEncoding)anEncoding - isObjectBlob:(BOOL)isFieldBlob isEditable:(BOOL)isEditable withWindow:(NSWindow *)tableWindow; + isObjectBlob:(BOOL)isFieldBlob isEditable:(BOOL)isEditable withWindow:(NSWindow *)theWindow; - (void)setTextMaxLength:(unsigned long long)length; diff --git a/Source/SPFieldEditorController.m b/Source/SPFieldEditorController.m index cb4a11f7..7bef2719 100644 --- a/Source/SPFieldEditorController.m +++ b/Source/SPFieldEditorController.m @@ -131,7 +131,7 @@ } - (id)editWithObject:(id)data fieldName:(NSString*)fieldName usingEncoding:(NSStringEncoding)anEncoding - isObjectBlob:(BOOL)isFieldBlob isEditable:(BOOL)isEditable withWindow:(NSWindow *)tableWindow + isObjectBlob:(BOOL)isFieldBlob isEditable:(BOOL)isEditable withWindow:(NSWindow *)theWindow { // If required, use monospaced fonts if (![prefs objectForKey:SPFieldEditorSheetFont]) { @@ -192,7 +192,7 @@ [editTextView setEditable:isEditable]; [editImage setEditable:isEditable]; - [NSApp beginSheet:editSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [NSApp beginSheet:editSheet modalForWindow:theWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [editSheetProgressBar startAnimation:self]; diff --git a/Source/SPGrowlController.h b/Source/SPGrowlController.h index 410ce914..e3f054a5 100644 --- a/Source/SPGrowlController.h +++ b/Source/SPGrowlController.h @@ -26,6 +26,8 @@ #import #import +@class TableDocument; + @interface SPGrowlController : NSObject { NSString *timingNotificationName; @@ -38,14 +40,14 @@ // Post notification - (void)notifyWithTitle:(NSString *)title description:(NSString *)description - window:(NSWindow *)window + document:(TableDocument *)document notificationName:(NSString *)name; - (void)notifyWithObject:(NSDictionary *)notificationDictionary; - (void)notifyWithTitle:(NSString *)title description:(NSString *)description - window:(NSWindow *)window + document:(TableDocument *)document notificationName:(NSString *)name iconData:(NSData *)data priority:(NSInteger)priority diff --git a/Source/SPGrowlController.m b/Source/SPGrowlController.m index b4f84780..50ca4906 100644 --- a/Source/SPGrowlController.m +++ b/Source/SPGrowlController.m @@ -31,6 +31,8 @@ static SPGrowlController *sharedGrowlController = nil; +@class SPWindowController; + @implementation SPGrowlController /** @@ -84,21 +86,21 @@ static SPGrowlController *sharedGrowlController = nil; * Calls the notification after a tiny delay to allow isKeyWindow to have updated * after tasks. */ -- (void)notifyWithTitle:(NSString *)title description:(NSString *)description window:(NSWindow *)window notificationName:(NSString *)name +- (void)notifyWithTitle:(NSString *)title description:(NSString *)description document:(TableDocument *)document notificationName:(NSString *)name { // Ensure that the delayed notification call is made on the main thread if (![NSThread isMainThread]) { - [[self onMainThread] notifyWithTitle:title description:description window:window notificationName:name]; + [[self onMainThread] notifyWithTitle:title description:description document:document notificationName:name]; return; } NSMutableDictionary *notificationDictionary = [NSMutableDictionary dictionary]; [notificationDictionary setObject:title forKey:@"title"]; [notificationDictionary setObject:description forKey:@"description"]; - [notificationDictionary setObject:window forKey:@"window"]; + [notificationDictionary setObject:document forKey:@"document"]; [notificationDictionary setObject:name forKey:@"name"]; - [notificationDictionary setObject:[NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:[window windowNumber]] forKey:@"notificationWindow"] forKey:@"clickContext"]; + [notificationDictionary setObject:[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInteger:[document hash]] forKey:@"notificationDocumentHash"] forKey:@"clickContext"]; [self performSelector:@selector(notifyWithObject:) withObject:notificationDictionary afterDelay:0.1]; } @@ -112,7 +114,7 @@ static SPGrowlController *sharedGrowlController = nil; { [self notifyWithTitle:[notificationDictionary objectForKey:@"title"] description:[notificationDictionary objectForKey:@"description"] - window:[notificationDictionary objectForKey:@"window"] + document:[notificationDictionary objectForKey:@"document"] notificationName:[notificationDictionary objectForKey:@"name"] iconData:nil priority:0 @@ -123,13 +125,17 @@ static SPGrowlController *sharedGrowlController = nil; /** * Posts a Growl notification using the supplied details and effectively ignoring the default values. */ -- (void)notifyWithTitle:(NSString *)title description:(NSString *)description window:(NSWindow *)window notificationName:(NSString *)name iconData:(NSData *)data priority:(NSInteger)priority isSticky:(BOOL)sticky clickContext:(id)clickContext +- (void)notifyWithTitle:(NSString *)title description:(NSString *)description document:(TableDocument *)document notificationName:(NSString *)name iconData:(NSData *)data priority:(NSInteger)priority isSticky:(BOOL)sticky clickContext:(id)clickContext { BOOL postNotification = YES; - // Don't post the notification if the notification window is key and order front + // Don't post the notification if the notification document is frontmost // as that suggests the user is already viewing the notification result. - if ([window isKeyWindow]) postNotification = NO; + if ([[document parentWindow] isKeyWindow] + && [[[document parentTabViewItem] tabView] selectedTabViewItem] == [document parentTabViewItem]) + { + postNotification = NO; + } // If a timing notification name exists, check to see if it matches the notification name; // if it does, and the time exceeds the threshold, display the notification even for @@ -158,11 +164,20 @@ static SPGrowlController *sharedGrowlController = nil; */ - (void)growlNotificationWasClicked:(NSDictionary *)clickContext { - if (clickContext && [clickContext objectForKey:@"notificationWindow"]) { - NSWindow *targetWindow = [NSApp windowWithWindowNumber:[[clickContext objectForKey:@"notificationWindow"] integerValue]]; - if (targetWindow) { - [NSApp activateIgnoringOtherApps:YES]; - [targetWindow makeKeyAndOrderFront:self]; + if (clickContext && [clickContext objectForKey:@"notificationDocumentHash"]) { + NSUInteger documentHash = [[clickContext objectForKey:@"notificationDocumentHash"] unsignedIntegerValue]; + + // Loop through the windows, looking for the document + for (NSWindow *eachWindow in [NSApp orderedWindows]) { + if ([[eachWindow windowController] isKindOfClass:[SPWindowController class]]) { + for (TableDocument *eachDocument in [[eachWindow windowController] documents]) { + if ([eachDocument hash] == documentHash) { + [NSApp activateIgnoringOtherApps:YES]; + [eachDocument makeKeyDocument]; + return; + } + } + } } } } diff --git a/Source/SPHistoryController.m b/Source/SPHistoryController.m index b8128a0f..421569e7 100644 --- a/Source/SPHistoryController.m +++ b/Source/SPHistoryController.m @@ -65,6 +65,7 @@ - (void) dealloc { +NSLog(@"history is dealloc'd"); [[NSNotificationCenter defaultCenter] removeObserver:self]; [tableContentStates release]; [history release]; diff --git a/Source/SPNavigatorController.m b/Source/SPNavigatorController.m index 8261ec2b..7e2ae14d 100644 --- a/Source/SPNavigatorController.m +++ b/Source/SPNavigatorController.m @@ -326,8 +326,8 @@ static SPNavigatorController *sharedNavigatorController = nil; // Detect if more than one connection windows with the connectionID are open. // If so, don't remove it. - if ([[[NSDocumentController sharedDocumentController] documents] count]) { - for(id doc in [[NSDocumentController sharedDocumentController] documents]) { + if ([[NSApp delegate] frontDocument]) { + for(id doc in [[NSApp delegate] orderedDocuments]) { if(![[doc valueForKeyPath:@"mySQLConnection"] isConnected]) continue; if([[doc connectionID] isEqualToString:connectionID]) docCounter++; @@ -384,7 +384,7 @@ static SPNavigatorController *sharedNavigatorController = nil; NSArray *pathArray = [[[parentKeys objectAtIndex:0] description] componentsSeparatedByString:SPUniqueSchemaDelimiter]; if([pathArray count] > 1) { - TableDocument *doc = [[NSDocumentController sharedDocumentController] currentDocument]; + TableDocument *doc = [[NSApp delegate] frontDocument]; if([doc isWorking]) { [SPTooltip showWithObject:NSLocalizedString(@"Active connection window is busy. Please wait and try again.", @"active connection window is busy. please wait and try again. tooltip") atLocation:pos @@ -432,7 +432,7 @@ static SPNavigatorController *sharedNavigatorController = nil; } - if (doc && [doc isKindOfClass:[TableDocument class]] && [[[NSDocumentController sharedDocumentController] documents] count]) { + if (doc && [doc isKindOfClass:[TableDocument class]]) { id theConnection = [doc valueForKeyPath:@"mySQLConnection"]; @@ -585,8 +585,7 @@ static SPNavigatorController *sharedNavigatorController = nil; { // Reset everything for current active doc connection - if (![[[NSDocumentController sharedDocumentController] documents] count]) return; - id doc = [[NSDocumentController sharedDocumentController] currentDocument]; + id doc = [[NSApp delegate] frontDocument]; if(!doc) return; NSString *connectionID = [doc connectionID]; if(!connectionID || [connectionID length] < 2) return; @@ -743,8 +742,8 @@ static SPNavigatorController *sharedNavigatorController = nil; [searchField setStringValue:@""]; } - if ([[[NSDocumentController sharedDocumentController] documents] count]) { - TableDocument *doc = [[NSDocumentController sharedDocumentController] currentDocument]; + TableDocument *doc = [[NSApp delegate] frontDocument]; + if (doc) { NSMutableString *key = [NSMutableString string]; [key setString:[doc connectionID]]; if([doc database] && [(NSString*)[doc database] length]){ diff --git a/Source/SPPrintController.m b/Source/SPPrintController.m index d12f764f..b8f323c4 100644 --- a/Source/SPPrintController.m +++ b/Source/SPPrintController.m @@ -44,7 +44,7 @@ - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { // Because we need the webFrame loaded (for preview), we've moved the actual printing here - NSPrintInfo *printInfo = [self printInfo]; + NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo]; NSSize paperSize = [printInfo paperSize]; NSRect printableRect = [printInfo imageablePageBounds]; @@ -89,7 +89,7 @@ [op setPrintPanel:printPanel]; - [op runOperationModalForWindow:tableWindow + [op runOperationModalForWindow:[self parentWindow] delegate:self didRunSelector:nil contextInfo:nil]; @@ -129,7 +129,7 @@ [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(printWarningDidEnd:returnCode:contextInfo:) contextInfo:NULL]; + [alert beginSheetModalForWindow:[self parentWindow] modalDelegate:self didEndSelector:@selector(printWarningDidEnd:returnCode:contextInfo:) contextInfo:NULL]; return; } @@ -196,7 +196,7 @@ - (void)generateHTMLForPrinting { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - + // Set up template engine with your chosen matcher MGTemplateEngine *engine = [MGTemplateEngine templateEngine]; diff --git a/Source/SPProcessListController.m b/Source/SPProcessListController.m index dad68d18..83b9ae54 100644 --- a/Source/SPProcessListController.m +++ b/Source/SPProcessListController.m @@ -74,7 +74,7 @@ */ - (void)awakeFromNib { - [[self window] setTitle:[NSString stringWithFormat:@"%@ %@", [[[NSDocumentController sharedDocumentController] currentDocument] name], NSLocalizedString(@"Server Processes", @"server processes window title")]]; + [[self window] setTitle:[NSString stringWithFormat:@"%@ %@", [[[NSApp delegate] frontDocument] name], NSLocalizedString(@"Server Processes", @"server processes window title")]]; [self setWindowFrameAutosaveName:@"ProcessList"]; @@ -367,7 +367,7 @@ { if (returnCode == NSOKButton) { if ([processesFiltered count] > 0) { - NSMutableString *processesString = [NSMutableString stringWithFormat:@"# MySQL server proceese for %@\n\n", [(TableDocument *)[[NSApp mainWindow] delegate] host]]; + NSMutableString *processesString = [NSMutableString stringWithFormat:@"# MySQL server proceese for %@\n\n", [[[NSApp delegate] frontDocument] host]]; for (NSDictionary *process in processesFiltered) { diff --git a/Source/SPQueryController.m b/Source/SPQueryController.m index c8906477..538e880b 100644 --- a/Source/SPQueryController.m +++ b/Source/SPQueryController.m @@ -594,7 +594,7 @@ static SPQueryController *sharedQueryController = nil; { // Check for multiple instance of the same document. // Remove it if only one instance was registerd. - NSArray *allDocs = [[NSDocumentController sharedDocumentController] documents]; + NSArray *allDocs = [[NSApp delegate] orderedDocuments]; NSMutableArray *allURLs = [NSMutableArray array]; for(id doc in allDocs) { if (![doc fileURL]) continue; @@ -635,7 +635,7 @@ static SPQueryController *sharedQueryController = nil; [historyContainer setObject:historyArray forKey:[fileURL absoluteString]]; // Inform all opened documents to update the history list - for(id doc in [[NSDocumentController sharedDocumentController] documents]) + for(id doc in [[NSApp delegate] orderedDocuments]) if([[doc valueForKeyPath:@"customQueryInstance"] respondsToSelector:@selector(historyItemsHaveBeenUpdated:)]) [[doc valueForKeyPath:@"customQueryInstance"] performSelectorOnMainThread:@selector(historyItemsHaveBeenUpdated:) withObject:self waitUntilDone:NO]; diff --git a/Source/SPQueryFavoriteManager.h b/Source/SPQueryFavoriteManager.h index c803e565..c3b93ab5 100644 --- a/Source/SPQueryFavoriteManager.h +++ b/Source/SPQueryFavoriteManager.h @@ -25,7 +25,7 @@ #import -@class BWAnchoredButtonBar, CMTextView; +@class BWAnchoredButtonBar, CMTextView, TableDocument; @interface NSObject (SPQueryFavoriteManagerDelegate) @@ -38,6 +38,7 @@ NSUserDefaults *prefs; NSURL *delegatesFileURL; + TableDocument *tableDocumentInstance; IBOutlet NSPopUpButton *encodingPopUp; IBOutlet NSTableView *favoritesTableView; IBOutlet NSTextField *favoriteNameTextField; diff --git a/Source/SPQueryFavoriteManager.m b/Source/SPQueryFavoriteManager.m index 91a8d90e..40648f91 100644 --- a/Source/SPQueryFavoriteManager.m +++ b/Source/SPQueryFavoriteManager.m @@ -59,7 +59,8 @@ NSLog(@"Query Favorite Manger was called without a delegate."); return nil; } - delegatesFileURL = [[managerDelegate valueForKeyPath:@"tableDocumentInstance"] fileURL]; + tableDocumentInstance = [managerDelegate valueForKeyPath:@"tableDocumentInstance"]; + delegatesFileURL = [tableDocumentInstance fileURL]; } return self; @@ -181,7 +182,7 @@ */ - (id)customQueryInstance { - return [[[NSApp mainWindow] delegate] valueForKey:@"customQueryInstance"]; + return [tableDocumentInstance valueForKey:@"customQueryInstance"]; } #pragma mark - @@ -382,7 +383,7 @@ [prefs setObject:[self queryFavoritesForFileURL:nil] forKey:SPQueryFavorites]; // Inform all opened documents to update the query favorites list - for(id doc in [[NSDocumentController sharedDocumentController] documents]) + for(id doc in [[NSApp delegate] orderedDocuments]) if([[doc valueForKeyPath:@"customQueryInstance"] respondsToSelector:@selector(queryFavoritesHaveBeenUpdated:)]) [[doc valueForKeyPath:@"customQueryInstance"] queryFavoritesHaveBeenUpdated:self]; diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m index 6d9e64ca..59acb9e0 100644 --- a/Source/SPSSHTunnel.m +++ b/Source/SPSSHTunnel.m @@ -72,6 +72,8 @@ } parentWindow = nil; + sshQuestionDialog = nil; + sshPasswordDialog = nil; password = nil; keychainName = nil; keychainAccount = nil; @@ -102,6 +104,11 @@ */ - (void)setParentWindow:(NSWindow *)theWindow { + + // As this object is not a NSWindowController, use manual top-level nib item management + if (sshQuestionDialog) [sshQuestionDialog release], sshQuestionDialog = nil; + if (sshPasswordDialog) [sshPasswordDialog release], sshPasswordDialog = nil; + parentWindow = theWindow; if (![NSBundle loadNibNamed:@"SSHQuestionDialog" owner:self]) { NSLog(@"SSH query dialog could not be loaded; SSH tunnels will not function correctly."); @@ -646,6 +653,10 @@ if (password) [password release]; if (keychainName) [keychainName release]; if (keychainAccount) [keychainAccount release]; + + // As this object is not a NSWindowController, use manual top-level nib item management + if (sshQuestionDialog) [sshQuestionDialog release], sshQuestionDialog = nil; + if (sshPasswordDialog) [sshPasswordDialog release], sshPasswordDialog = nil; [super dealloc]; } diff --git a/Source/SPServerVariablesController.m b/Source/SPServerVariablesController.m index 37868f10..7243298d 100644 --- a/Source/SPServerVariablesController.m +++ b/Source/SPServerVariablesController.m @@ -165,7 +165,7 @@ { if (returnCode == NSOKButton) { if ([variablesFiltered count] > 0) { - NSMutableString *variablesString = [NSMutableString stringWithFormat:@"# MySQL server variables for %@\n\n", [(TableDocument *)[[NSApp mainWindow] delegate] host]]; + NSMutableString *variablesString = [NSMutableString stringWithFormat:@"# MySQL server variables for %@\n\n", [[[NSApp delegate] frontDocument] host]]; for (NSDictionary *variable in variablesFiltered) { diff --git a/Source/SPTableData.m b/Source/SPTableData.m index feb77848..117f1a05 100644 --- a/Source/SPTableData.m +++ b/Source/SPTableData.m @@ -293,14 +293,15 @@ NSDictionary *columnData; NSEnumerator *enumerator; + [columns removeAllObjects]; + [columnNames removeAllObjects]; + [constraints removeAllObjects]; + if( [tableListInstance tableType] == SPTableTypeTable || [tableListInstance tableType] == SPTableTypeView ) { tableData = [self informationForTable:[tableListInstance tableName]]; } if (tableData == nil ) { - [columns removeAllObjects]; - [columnNames removeAllObjects]; - [constraints removeAllObjects]; return FALSE; } diff --git a/Source/SPTableRelations.h b/Source/SPTableRelations.h index 5b1db7dd..40ac2e79 100644 --- a/Source/SPTableRelations.h +++ b/Source/SPTableRelations.h @@ -33,7 +33,6 @@ IBOutlet id tableDataInstance; IBOutlet id tableList; - IBOutlet id tableWindow; IBOutlet NSButton *addRelationButton; IBOutlet NSButton *removeRelationButton; diff --git a/Source/SPTableRelations.m b/Source/SPTableRelations.m index 586f341d..e37d453d 100644 --- a/Source/SPTableRelations.m +++ b/Source/SPTableRelations.m @@ -186,7 +186,7 @@ [self selectReferenceTable:nil]; [NSApp beginSheet:addRelationPanel - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; @@ -214,7 +214,7 @@ [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:@"removeRelation"]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:@"removeRelation"]; } } diff --git a/Source/SPTableTriggers.h b/Source/SPTableTriggers.h index 8e97908a..c03b5f2c 100644 --- a/Source/SPTableTriggers.h +++ b/Source/SPTableTriggers.h @@ -33,7 +33,6 @@ IBOutlet id tableDataInstance; IBOutlet id tableList; - IBOutlet id tableWindow; IBOutlet NSButton *addTriggerButton; IBOutlet NSButton *removeTriggerButton; diff --git a/Source/SPTableTriggers.m b/Source/SPTableTriggers.m index 7a7cf8ac..e3cdda63 100644 --- a/Source/SPTableTriggers.m +++ b/Source/SPTableTriggers.m @@ -180,7 +180,7 @@ - (IBAction)addTrigger:(id)sender { [NSApp beginSheet:addTriggerPanel - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; @@ -208,7 +208,7 @@ [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:@"removeTrigger"]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:@"removeTrigger"]; } } diff --git a/Source/SPTableView.m b/Source/SPTableView.m index 4acf4879..58e2206e 100644 --- a/Source/SPTableView.m +++ b/Source/SPTableView.m @@ -37,14 +37,18 @@ - (NSMenu *)menuForEvent:(NSEvent *)event { + // Try to retrieve a reference to the table document (assuming this is frontmost tab) + TableDocument *parentTableDocument = nil; + if ([[[[[self window] delegate] class] description] isEqualToString:@"SPWindowController"]) { + parentTableDocument = [[[self window] delegate] selectedTableDocument]; + } + // If TableDocument is performing a task suppress any context menu - if ([[[[[self window] delegate] class] description] isEqualToString:@"TableDocument"] - && [[[self window] delegate] isWorking]) + if (parentTableDocument && [parentTableDocument isWorking]) return nil; // Check to see whether any edits-in-progress need to be saved before changing selections - if ([[[[[self window] delegate] class] description] isEqualToString:@"TableDocument"] - && ![[[self window] delegate] couldCommitCurrentViewActions]) + if (parentTableDocument && ![parentTableDocument couldCommitCurrentViewActions]) return nil; // If more than one row is selected only returns the default contextual menu diff --git a/Source/SPWindowController.h b/Source/SPWindowController.h new file mode 100644 index 00000000..00754848 --- /dev/null +++ b/Source/SPWindowController.h @@ -0,0 +1,46 @@ +// +// $Id$ +// +// SPWindowController.h +// sequel-pro +// +// Created by Rowan Beentje on May 16, 2010 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import +@class PSMTabBarControl, TableDocument; + +@interface SPWindowController : NSWindowController +{ + IBOutlet PSMTabBarControl *tabBar; + IBOutlet NSTabView *tabView; + + NSMenuItem *closeWindowMenuItem; + NSMenuItem *closeTabMenuItem; + + NSMutableArray *managedDatabaseConnections; +} + +// Database connection management +- (IBAction) addNewConnection:(id)sender; +- (TableDocument *) selectedTableDocument; +- (void) updateAllTabTitles:(id)sender; +- (IBAction)closeTab:(id)sender; +- (NSArray *)documents; + +@end diff --git a/Source/SPWindowController.m b/Source/SPWindowController.m new file mode 100644 index 00000000..3c8b9be7 --- /dev/null +++ b/Source/SPWindowController.m @@ -0,0 +1,372 @@ +// +// $Id$ +// +// SPWindowController.m +// sequel-pro +// +// Created by Rowan Beentje on May 16, 2010 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPWindowController.h" +#import "SPConstants.h" +#import "TableDocument.h" +#import +#import + +@implementation SPWindowController + +/** + * awakeFromNib + */ +- (void) awakeFromNib +{ + + // Disable automatic cascading - this occurs before the size is set, so let the app + // controller apply cascading after frame autosaving. + [self setShouldCascadeWindows:NO]; + + // Initialise the managed database connections array + managedDatabaseConnections = [[NSMutableArray alloc] init]; + + // Set up the tab bar + [tabBar setStyleNamed:@"Metal"]; + [tabBar setCanCloseOnlyTab:NO]; + [tabBar setHideForSingleTab:YES]; + [tabBar setShowAddTabButton:YES]; + [tabBar setSizeCellsToFit:NO]; + [tabBar setCellMinWidth:100]; + [tabBar setCellMaxWidth:250]; + [tabBar setCellOptimumWidth:250]; + + // hook up add tab button + [[tabBar addTabButton] setTarget:self]; + [[tabBar addTabButton] setAction:@selector(addNewConnection:)]; + + // Retrieve references to the 'Close Window' and 'Close Tab' menus. These are updated as window focus changes. + closeWindowMenuItem = [[[[NSApp mainMenu] itemWithTag:SPMainMenuFile] submenu] itemWithTag:1003]; + closeTabMenuItem = [[[[NSApp mainMenu] itemWithTag:SPMainMenuFile] submenu] itemWithTag:1103]; + + // Add a new connection to the new window + [self addNewConnection:self]; +} + +/** + * Deallocation + */ +- (void) dealloc +{ + [managedDatabaseConnections release]; +} + +#pragma mark - +#pragma mark Database connection management + +/** + * Add a new database connection to the window, in a tab view. + */ +- (IBAction) addNewConnection:(id)sender +{ + + // Create a new database connection view + TableDocument *newTableDocument = [[TableDocument alloc] init]; + [newTableDocument setParentWindowController:self]; + [newTableDocument setParentWindow:[self window]]; + + // Set up a new tab with the connection view as the identifier, add the view, and add it to the tab view + NSTabViewItem *newItem = [[[NSTabViewItem alloc] initWithIdentifier:newTableDocument] autorelease]; + [newItem setView:[newTableDocument parentView]]; + [tabView addTabViewItem:newItem]; + [tabView selectTabViewItem:newItem]; + [newTableDocument setParentTabViewItem:newItem]; + + // Tell the new database connection view to set up the window and update titles + [newTableDocument didBecomeActiveTabInWindow]; + [newTableDocument updateWindowTitle:self]; + + [newTableDocument release]; +} + +/** + * Retrieve the currently connection view in the window. + */ +- (TableDocument *) selectedTableDocument +{ + return [[tabView selectedTabViewItem] identifier]; +} + +/** + * Ask all the connection views to update their titles. + * As tab titles depend on the currently selected tab, changes + * within each tab may require other tabs to update their titles. + * If the sender is a tab, that tab is skipped when updating titles. + */ +- (void) updateAllTabTitles:(id)sender +{ + for (NSTabViewItem *eachItem in [tabView tabViewItems]) { + TableDocument *eachDocument = [eachItem identifier]; + if (eachDocument != sender) [eachDocument updateWindowTitle:self]; + } +} + + +/** + * Close the current tab, or if it's the last in the window, the window. + */ +- (IBAction)closeTab:(id)sender +{ + + // Return if the frontmost tab shouldn't be closed + TableDocument *frontDocument = [[tabView selectedTabViewItem] identifier]; + if (![frontDocument parentTabShouldClose]) return NO; + + // If there are multiple tabs, close the front tab. + if ([tabView numberOfTabViewItems] > 1) { + [tabView removeTabViewItem:[tabView selectedTabViewItem]]; + } else { + [[self window] performClose:self]; + } +} + +/** + * Retrieve the documents associated with this window. + */ +- (NSArray *)documents +{ + NSMutableArray *documentsArray = [NSMutableArray array]; + for (NSTabViewItem *eachItem in [tabView tabViewItems]) { + [documentsArray addObject:[eachItem identifier]]; + } + return documentsArray; +} + +#pragma mark - +#pragma mark Tab view delegate methods + +/** + * Called when a tab item is about to be selected. + */ +- (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem +{ + TableDocument *currentlySelectedDocument = [[tabView selectedTabViewItem] identifier]; + [currentlySelectedDocument willResignActiveTabInWindow]; +} + +/** + * Called when a tab item was selected. + */ +- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem +{ + TableDocument *theDocument = [tabViewItem identifier]; + [theDocument didBecomeActiveTabInWindow]; + if ([[self window] isKeyWindow]) [theDocument tabDidBecomeKey]; + [self updateAllTabTitles:self]; +} + +/** + * Called to determine whether a tab view item can be closed + */ +- (BOOL)tabView:(NSTabView *)aTabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem +{ + TableDocument *theDocument = [tabViewItem identifier]; + if (![theDocument parentTabShouldClose]) return NO; + return YES; +} + +/** + * Called after a tab view item is closed. + */ +- (void)tabView:(NSTabView *)aTabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem +{ + TableDocument *theDocument = [tabViewItem identifier]; + [theDocument parentTabDidClose]; +} + +/** + * Called to allow dragging of tab view items + */ +- (BOOL)tabView:(NSTabView *)aTabView shouldDragTabViewItem:(NSTabViewItem *)tabViewItem fromTabBar:(PSMTabBarControl *)tabBarControl +{ + return YES; +} + +/** + * Called when a tab finishes a drop. This is called with the new tabView. + */ +- (void)tabView:(NSTabView*)aTabView didDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl +{ + TableDocument *draggedDocument = [tabViewItem identifier]; + + // Grab a reference to the old window + NSWindow *draggedFromWindow = [draggedDocument parentWindow]; + + // If the window changed, perform additional processing. + if (draggedFromWindow != [tabBarControl window]) { + + // Update the old window + [[[draggedFromWindow windowController] selectedTableDocument] didBecomeActiveTabInWindow]; + + // Update the item's document's window + [draggedDocument willResignActiveTabInWindow]; + [draggedDocument setParentWindow:[tabBarControl window]]; + [draggedDocument didBecomeActiveTabInWindow]; + } +} + +- (void)tabView:(NSTabView *)aTabView closeWindowForLastTabViewItem:(NSTabViewItem *)tabViewItem +{ + [[aTabView window] close]; +} + +#pragma mark - +#pragma mark Window delegate methods + +/** + * Determine whether the window is permitted to close. + * Go through the tabs in this window, and ask the database connection view + * in each one if it can be closed, returning YES only if all can be closed. + */ +- (BOOL)windowShouldClose:(id)sender +{ + for (NSTabViewItem *eachItem in [tabView tabViewItems]) { + TableDocument *eachDocument = [eachItem identifier]; + if (![eachDocument parentTabShouldClose]) return NO; + } + + return YES; +} + +/** + * When the window does close, close all tabs. + */ +- (void)windowWillClose:(NSNotification *)notification + +{ + for (NSTabViewItem *eachItem in [tabView tabViewItems]) { + [tabView removeTabViewItem:eachItem]; + } + [self autorelease]; +} + +/** + * When the window becomes key, inform the selected tab and + * update menu items. + */ +- (void)windowDidBecomeKey:(NSNotification *)notification +{ + TableDocument *selectedTab = [[tabView selectedTabViewItem] identifier]; + [selectedTab tabDidBecomeKey]; + + // Update the "Close window" item + [closeWindowMenuItem setTitle:NSLocalizedString(@"Close Window", @"Close Window menu item")]; + [closeWindowMenuItem setKeyEquivalentModifierMask:(NSCommandKeyMask | NSShiftKeyMask)]; + + // Ensure the "Close tab" item is enabled and has the standard shortcut + [closeTabMenuItem setEnabled:YES]; + [closeTabMenuItem setKeyEquivalent:@"w"]; + [closeTabMenuItem setKeyEquivalentModifierMask:NSCommandKeyMask]; +} + +/** + * When the window resigns key, update menu items. + */ +- (void)windowDidResignKey:(NSNotification *)notification +{ + // Disable the "Close tab" menu item + [closeTabMenuItem setEnabled:NO]; + [closeTabMenuItem setKeyEquivalent:@""]; + + // Update the "Close window" item to show only "Close" + [closeWindowMenuItem setTitle:NSLocalizedString(@"Close", @"Close menu item")]; + [closeWindowMenuItem setKeyEquivalentModifierMask:NSCommandKeyMask]; +} + +/** + * If the window is resized, notify all the tabs. + */ +- (void)windowDidResize:(NSNotification *)notification +{ + for (NSTabViewItem *eachItem in [tabView tabViewItems]) { + TableDocument *eachDocument = [eachItem identifier]; + [eachDocument tabDidResize]; + } +} + +#pragma mark - +#pragma mark First responder forwarding to active tab + +/** + * Delegate unrecognised methods to the selected table document, thanks to the magic + * of NSInvocation (see forwardInvocation: docs for background). Must be paired + * with methodSignationForSelector:. + */ +- (void) forwardInvocation:(NSInvocation *)theInvocation +{ + TableDocument *frontDocument = [[tabView selectedTabViewItem] identifier]; + SEL theSelector = [theInvocation selector]; + if (![frontDocument respondsToSelector:theSelector]) [self doesNotRecognizeSelector:theSelector]; + [theInvocation invokeWithTarget:frontDocument]; +} + +/** + * Return the correct method signatures for the selected table document if + * NSObject doesn't implement the requested methods. + */ +- (NSMethodSignature *) methodSignatureForSelector:(SEL)theSelector +{ + NSMethodSignature *defaultSignature = [super methodSignatureForSelector:theSelector]; + if (defaultSignature) return defaultSignature; + + return [[[tabView selectedTabViewItem] identifier] methodSignatureForSelector:theSelector]; +} + +/** + * Override the default repondsToSelector:, returning true if either NSObject + * or the selected table document supports the selector. + */ +- (BOOL) respondsToSelector:(SEL)theSelector +{ + return ([super respondsToSelector:theSelector] || [[[tabView selectedTabViewItem] identifier] respondsToSelector:theSelector]); +} + +/** + * Override the default performSelector:, again either using NSObject defaults + * or performing the selector on the selected table document. + */ +- (id) performSelector:(SEL)theSelector +{ + if ([super respondsToSelector:theSelector]) return [super performSelector:theSelector]; + + TableDocument *frontDocument = [[tabView selectedTabViewItem] identifier]; + if (![frontDocument respondsToSelector:theSelector]) [self doesNotRecognizeSelector:theSelector]; + return [frontDocument performSelector:theSelector]; +} + +/** + * Override the default performSelector:withObject: - see performSelector: + */ +- (id) performSelector:(SEL)theSelector withObject:(id)theObject +{ + if ([super respondsToSelector:theSelector]) return [super performSelector:theSelector withObject:theObject]; + + TableDocument *frontDocument = [[tabView selectedTabViewItem] identifier]; + if (![frontDocument respondsToSelector:theSelector]) [self doesNotRecognizeSelector:theSelector]; + + return [frontDocument performSelector:theSelector withObject:theObject]; +} + +@end \ No newline at end of file diff --git a/Source/TableContent.h b/Source/TableContent.h index 9b2e953e..35bd6c31 100644 --- a/Source/TableContent.h +++ b/Source/TableContent.h @@ -40,7 +40,6 @@ IBOutlet SPTableInfo *tableInfoInstance; IBOutlet SPHistoryController *spHistoryControllerInstance; - IBOutlet id tableWindow; IBOutlet CMCopyTable *tableContentView; IBOutlet NSPopUpButton *fieldField; IBOutlet id compareField; @@ -72,6 +71,7 @@ BOOL _mainNibLoaded; BOOL isWorking; pthread_mutex_t tableValuesLock; + NSMutableArray *nibObjectsToRelease; NSString *selectedTable, *usedQuery; SPDataStorage *tableValues; diff --git a/Source/TableContent.m b/Source/TableContent.m index 3e9c933a..cf639b72 100644 --- a/Source/TableContent.m +++ b/Source/TableContent.m @@ -63,6 +63,7 @@ _mainNibLoaded = NO; isWorking = NO; pthread_mutex_init(&tableValuesLock, NULL); + nibObjectsToRelease = [[NSMutableArray alloc] init]; tableValues = [[SPDataStorage alloc] init]; tableRowsCount = 0; @@ -138,10 +139,17 @@ // Set the table content view's vertical gridlines if required [tableContentView setGridStyleMask:([prefs boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone]; - // Add the pagination view to the content area with ourselves as the owner - if (![NSBundle loadNibNamed:@"ContentPaginationView" owner:self]) { + // Load the pagination view, keeping references to the top-level objects for later release + NSArray *paginationViewTopLevelObjects = nil; + NSNib *nibLoader = [[NSNib alloc] initWithNibNamed:@"ContentPaginationView" bundle:[NSBundle mainBundle]]; + if (![nibLoader instantiateNibWithOwner:self topLevelObjects:&paginationViewTopLevelObjects]) { NSLog(@"Content pagination nib could not be loaded; pagination will not function correctly."); + } else { + [nibObjectsToRelease addObjectsFromArray:paginationViewTopLevelObjects]; } + [nibLoader release]; + + // Add the pagination view to the content area NSRect paginationViewFrame = [paginationView frame]; NSRect paginationButtonFrame = [paginationButton frame]; paginationViewHeight = paginationViewFrame.size.height; @@ -464,7 +472,7 @@ } // Store the current first responder so filter field doesn't steal focus - id currentFirstResponder = [tableWindow firstResponder]; + id currentFirstResponder = [[tableDocumentInstance parentWindow] firstResponder]; // Enable and initialize filter fields (with tags for position of menu item and field position) [fieldField setEnabled:YES]; @@ -505,7 +513,7 @@ if ([prefs boolForKey:SPLimitResults]) contentPage = pageToRestore; // Restore first responder - [tableWindow makeFirstResponder:currentFirstResponder]; + [[tableDocumentInstance parentWindow] makeFirstResponder:currentFirstResponder]; // Set the state of the table buttons [addButton setEnabled:enableInteraction]; @@ -1213,19 +1221,19 @@ paginationViewFrame.size.height = paginationViewHeight; [paginationButton setState:NSOnState]; [paginationButton setImage:[NSImage imageNamed:@"button_action"]]; - [tableWindow makeFirstResponder:paginationPageField]; + [[tableDocumentInstance parentWindow] makeFirstResponder:paginationPageField]; } else { if (paginationViewFrame.size.height == 0) return; paginationViewFrame.size.height = 0; [paginationButton setState:NSOffState]; [paginationButton setImage:[NSImage imageNamed:@"button_pagination"]]; - if ([tableWindow firstResponder] == paginationPageField - || ([[tableWindow firstResponder] respondsToSelector:@selector(superview)] - && [(id)[tableWindow firstResponder] superview] - && [[(id)[tableWindow firstResponder] superview] respondsToSelector:@selector(superview)] - && [[(id)[tableWindow firstResponder] superview] superview] == paginationPageField)) + if ([[tableDocumentInstance parentWindow] firstResponder] == paginationPageField + || ([[[tableDocumentInstance parentWindow] firstResponder] respondsToSelector:@selector(superview)] + && [(id)[[tableDocumentInstance parentWindow] firstResponder] superview] + && [[(id)[[tableDocumentInstance parentWindow] firstResponder] superview] respondsToSelector:@selector(superview)] + && [[(id)[[tableDocumentInstance parentWindow] firstResponder] superview] superview] == paginationPageField)) { - [tableWindow makeFirstResponder:nil]; + [[tableDocumentInstance parentWindow] makeFirstResponder:nil]; } } @@ -1321,7 +1329,7 @@ if ( [tableContentView numberOfSelectedRows] < 1 ) return; if ( [tableContentView numberOfSelectedRows] > 1 ) { - 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")); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, NSLocalizedString(@"You can only copy single rows.", @"message of panel when trying to copy multiple rows")); return; } @@ -1376,7 +1384,7 @@ // return; // cancel editing (maybe this is not the ideal method -- see xcode docs for that method) - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; if (![tableContentView numberOfSelectedRows]) @@ -1427,7 +1435,7 @@ [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(removeRowSheetDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(removeRowSheetDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; } /** @@ -2066,7 +2074,7 @@ // Open query favorite manager [NSApp beginSheet:[contentFilterManager window] - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:contentFilterManager didEndSelector:nil contextInfo:nil]; @@ -2201,7 +2209,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, + SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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(); @@ -2220,7 +2228,7 @@ // New row created successfully if ( isEditingNewRow ) { if ( [prefs boolForKey:SPReloadAfterAddingRow] ) { - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; previousTableRowsCount = tableRowsCount; [self loadTableValues]; } else { @@ -2239,7 +2247,7 @@ // Reload table if set to - otherwise no action required. if ( [prefs boolForKey:SPReloadAfterEditingRow] ) { - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; previousTableRowsCount = tableRowsCount; [self loadTableValues]; } @@ -2250,7 +2258,7 @@ // 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, + SPBeginAlertSheet(NSLocalizedString(@"Couldn't write row", @"Couldn't write row error"), NSLocalizedString(@"Edit row", @"Edit row button"), NSLocalizedString(@"Discard changes", @"discard changes button"), nil, [tableDocumentInstance parentWindow], 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; } @@ -2267,7 +2275,7 @@ // 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]; + [tableContentView performSelector:@selector(keyDown:) withObject:[NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[[tableDocumentInstance parentWindow] windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x24] afterDelay:0.0]; // Discard changes selected } else { @@ -2294,7 +2302,7 @@ - (BOOL)saveRowOnDeselect { // Save any edits which have been made but not saved to the table yet. - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; // If no rows are currently being edited, or a save is in progress, return success at once. if (!isEditingRow || isSavingRow) return YES; @@ -2357,7 +2365,7 @@ // 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, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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]; @@ -2465,7 +2473,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, [tableDocumentInstance parentWindow], self, nil, nil, [error objectAtIndex:1]); } @@ -2942,7 +2950,7 @@ [self loadTableValues]; if ([mySQLConnection queryErrored]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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]; @@ -3037,7 +3045,7 @@ MCPResult *tempResult = [mySQLConnection queryString:query]; if (![tempResult numOfRows]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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; } @@ -3064,7 +3072,7 @@ usingEncoding:[mySQLConnection encoding] isObjectBlob:isBlob isEditable:YES - withWindow:tableWindow] retain]; + withWindow:[tableDocumentInstance parentWindow]] retain]; if (editData) { if (!isEditingRow) { @@ -3363,13 +3371,13 @@ NSUInteger numOfArgs = [[filter objectForKey:@"NumberOfArguments"] integerValue]; switch(numOfArgs) { case 2: - [tableWindow makeFirstResponder:firstBetweenField]; + [[tableDocumentInstance parentWindow] makeFirstResponder:firstBetweenField]; break; case 1: - [tableWindow makeFirstResponder:argumentField]; + [[tableDocumentInstance parentWindow] makeFirstResponder:argumentField]; break; default: - [tableWindow makeFirstResponder:compareField]; + [[tableDocumentInstance parentWindow] makeFirstResponder:compareField]; } } } @@ -3378,7 +3386,7 @@ // Last but not least - (void)dealloc -{ +{NSLog(@"content is deallocd"); [[NSNotificationCenter defaultCenter] removeObserver:self]; [tableValues release]; diff --git a/Source/TableDocument.h b/Source/TableDocument.h index b3cbb6e7..539d14b2 100644 --- a/Source/TableDocument.h +++ b/Source/TableDocument.h @@ -29,12 +29,12 @@ #import #import -@class SPConnectionController, SPProcessListController, SPServerVariablesController, SPUserManager; +@class SPConnectionController, SPProcessListController, SPServerVariablesController, SPUserManager, SPWindowController; /** * The TableDocument class controls the primary database view window. */ -@interface TableDocument : NSDocument +@interface TableDocument : NSObject { // IBOutlets IBOutlet id tablesListInstance; @@ -58,7 +58,7 @@ IBOutlet NSSearchField *listFilterField; - IBOutlet NSWindow *tableWindow; + IBOutlet NSView *parentView; IBOutlet id titleAccessoryView; IBOutlet id titleImageView; @@ -135,12 +135,12 @@ NSString *selectedDatabase; NSString *mySQLVersion; NSUserDefaults *prefs; + NSMutableArray *nibObjectsToRelease; NSMenu *selectEncodingMenu; BOOL _supportsEncoding; NSString *_encoding; BOOL _encodingViaLatin1; - BOOL _shouldOpenConnectionAutomatically; BOOL _isConnected; NSInteger _isWorkingLevel; BOOL _mainNibLoaded; @@ -168,6 +168,7 @@ NSString *queryEditorInitString; + NSURL *spfFileURL; NSDictionary *spfSession; NSMutableDictionary *spfPreferences; NSMutableDictionary *spfDocData; @@ -177,8 +178,17 @@ NSThread *printThread; id statusValues; + + // Properties + SPWindowController *parentWindowController; + NSWindow *parentWindow; + NSTabViewItem *parentTabViewItem; + BOOL isProcessing; } +@property (readwrite, assign) SPWindowController *parentWindowController; +@property (readwrite, assign) NSTabViewItem *parentTabViewItem; + - (BOOL)isUntitled; - (BOOL)couldCommitCurrentViewActions; @@ -187,8 +197,6 @@ // Connection callback and methods - (void)setConnection:(MCPConnection *)theConnection; - (MCPConnection *) getConnection; -- (void)setShouldAutomaticallyConnect:(BOOL)shouldAutomaticallyConnect; -- (BOOL)shouldAutomaticallyConnect; - (void)setKeychainID:(NSString *)theID; // Database methods @@ -271,6 +279,7 @@ - (IBAction)toggleNavigator:(id)sender; // Accessor methods +- (NSView *)parentView; - (NSString *)host; - (NSString *)name; - (NSString *)database; @@ -278,7 +287,6 @@ - (NSString *)port; - (NSString *)mySQLVersion; - (NSString *)user; -- (NSString *)displaySPName; - (NSString *)keyChainID; - (NSString *)connectionID; @@ -288,7 +296,7 @@ - (void)applicationWillTerminate:(NSNotification *)notification; // Menu methods -- (BOOL)validateMenuItem:(NSMenuItem *)anItem; +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem; - (IBAction)saveConnectionSheet:(id)sender; - (IBAction)import:(id)sender; - (IBAction)importFromClipboard:(id)sender; @@ -309,9 +317,23 @@ - (void)clearStatusIcon; // Toolbar methods +- (void)updateWindowTitle:(id)sender; - (void)setupToolbar; - (NSString *)selectedToolbarItemIdentifier; - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag; - (void)updateChooseDatabaseToolbarItemWidth; +// Tab methods +- (void)makeKeyDocument; +- (BOOL)parentTabShouldClose; +- (void)parentTabDidClose; +- (void)willResignActiveTabInWindow; +- (void)didBecomeActiveTabInWindow; +- (void)tabDidBecomeKey; +- (void)tabDidResize; +- (void)setIsProcessing:(BOOL)value; +- (BOOL)isProcessing; +- (void)setParentWindow:(NSWindow *)aWindow; +- (NSWindow *)parentWindow; + @end diff --git a/Source/TableDocument.m b/Source/TableDocument.m index 385e9781..5d3209e5 100644 --- a/Source/TableDocument.m +++ b/Source/TableDocument.m @@ -73,6 +73,9 @@ @implementation TableDocument +@synthesize parentWindowController; +@synthesize parentTabViewItem; + - (id)init { @@ -92,6 +95,9 @@ mySQLVersion = nil; allDatabases = nil; allSystemDatabases = nil; + mainToolbar = nil; + parentWindow = nil; + isProcessing = NO; printWebView = [[WebView alloc] init]; [printWebView setFrameLoadDelegate:self]; @@ -99,10 +105,12 @@ prefs = [NSUserDefaults standardUserDefaults]; queryEditorInitString = nil; + spfFileURL = nil; spfSession = nil; spfPreferences = [[NSMutableDictionary alloc] init]; spfDocData = [[NSMutableDictionary alloc] init]; + titleAccessoryView = nil; taskProgressWindow = nil; taskDisplayIsIndeterminate = YES; taskDisplayLastValue = 0; @@ -117,6 +125,15 @@ keyChainID = nil; statusValues = nil; printThread = nil; + nibObjectsToRelease = [[NSMutableArray alloc] init]; + + // As this object is not an NSWindowController subclass, top-level objects in loaded nibs aren't + // automatically released. Keep track of the top-level objects for release on dealloc. + NSArray *dbViewTopLevelObjects = nil; + NSNib *nibLoader = [[NSNib alloc] initWithNibNamed:@"DBView" bundle:[NSBundle mainBundle]]; + [nibLoader instantiateNibWithOwner:self topLevelObjects:&dbViewTopLevelObjects]; + [nibLoader release]; + [nibObjectsToRelease addObjectsFromArray:dbViewTopLevelObjects]; } return self; @@ -127,48 +144,6 @@ if (_mainNibLoaded) return; _mainNibLoaded = YES; - // The first window should use autosaving; subsequent windows should cascade - BOOL usedAutosave = [tableWindow setFrameAutosaveName:[self windowNibName]]; - if (!usedAutosave) { - [tableWindow setFrameUsingName:[self windowNibName]]; - NSArray *documents = [[NSDocumentController sharedDocumentController] documents]; - NSRect previousFrame = [[[documents objectAtIndex:(([documents count] > 1)?[documents count]-2:[documents count]-1)] valueForKey:@"tableWindow"] frame]; - NSPoint topLeftPoint = previousFrame.origin; - topLeftPoint.y += previousFrame.size.height; - [tableWindow setFrameTopLeftPoint:[tableWindow cascadeTopLeftFromPoint:topLeftPoint]]; - - // Try to check if new frame fits into the screen - NSRect screenFrame = [[NSScreen mainScreen] frame]; - NSScreen* candidate; - for(candidate in [NSScreen screens]) - if(NSMinX([candidate frame]) < topLeftPoint.x && NSMinX([candidate frame]) > NSMinX(screenFrame)) - screenFrame = [candidate visibleFrame]; - - previousFrame = [tableWindow frame]; - - // Determine if move/resize is required - if(previousFrame.origin.x - screenFrame.origin.x + previousFrame.size.width >= screenFrame.size.width - || previousFrame.origin.y - screenFrame.origin.y + previousFrame.size.height >= screenFrame.size.height) - { - - // First try to move the window back onto the screen if it will fit - if (previousFrame.size.width <= screenFrame.size.width && previousFrame.size.height <= screenFrame.size.height) { - previousFrame.origin.x -= (previousFrame.origin.x + previousFrame.size.width) - (screenFrame.origin.x + screenFrame.size.width); - previousFrame.origin.y -= (previousFrame.origin.y + previousFrame.size.height) - (screenFrame.origin.y + screenFrame.size.height); - [tableWindow setFrame:previousFrame display:YES]; - - // Otherwise, resize and de-cascade a little - } else { - previousFrame.size.width -= 50; - previousFrame.size.height -= 50; - previousFrame.origin.y += 50; - if(previousFrame.size.width >= [tableWindow minSize].width && previousFrame.size.height >= [tableWindow minSize].height) - [tableWindow setFrame:previousFrame display:YES]; - } - } - - } - // Set up the toolbar [self setupToolbar]; @@ -200,23 +175,21 @@ // Register a second observer for when the logging preference changes so we can tell the current connection about it [prefs addObserver:self forKeyPath:SPConsoleEnableLogging options:NSKeyValueObservingOptionNew context:NULL]; + // Register for notifications + //register for notifications + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willPerformQuery:) + name:@"SMySQLQueryWillBePerformed" object:self]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hasPerformedQuery:) + name:@"SMySQLQueryHasBeenPerformed" object:self]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) + name:@"NSApplicationWillTerminateNotification" object:nil]; + // Find the Database -> Database Encoding menu (it's not in our nib, so we can't use interface builder) - selectEncodingMenu = [[[[[NSApp mainMenu] itemWithTag:1] submenu] itemWithTag:1] submenu]; + selectEncodingMenu = [[[[[NSApp mainMenu] itemWithTag:SPMainMenuDatabase] submenu] itemWithTag:1] submenu]; // Hide the tabs in the tab view (we only show them to allow switching tabs in interface builder) [tableTabView setTabViewType:NSNoTabsNoBorder]; - // Add the icon accessory view to the title bar - NSView *windowFrame = [[tableWindow contentView] superview]; - NSRect av = [titleAccessoryView frame]; - NSRect initialAccessoryViewFrame = NSMakeRect( - [windowFrame frame].size.width - av.size.width - 30, - [windowFrame frame].size.height - av.size.height, - av.size.width, - av.size.height); - [titleAccessoryView setFrame:initialAccessoryViewFrame]; - [windowFrame addSubview:titleAccessoryView]; - // Bind the background color of the create syntax text view to the users preference [createTableSyntaxTextView setAllowsDocumentBackgroundColorChange:YES]; @@ -229,25 +202,35 @@ withKeyPath:@"values.CustomQueryEditorBackgroundColor" options:bindingOptions]; - // Load additional nibs - if (![NSBundle loadNibNamed:@"ConnectionErrorDialog" owner:self]) { + // Load additional nibs, keeping track of the top-level objects to allow correct release + NSArray *connectionDialogTopLevelObjects = nil; + NSNib *nibLoader = [[NSNib alloc] initWithNibNamed:@"ConnectionErrorDialog" bundle:[NSBundle mainBundle]]; + if (![nibLoader instantiateNibWithOwner:self topLevelObjects:&connectionDialogTopLevelObjects]) { NSLog(@"Connection error dialog could not be loaded; connection failure handling will not function correctly."); + } else { + [nibObjectsToRelease addObjectsFromArray:connectionDialogTopLevelObjects]; } - if (![NSBundle loadNibNamed:@"ProgressIndicatorLayer" owner:self]) { + [nibLoader release]; + NSArray *progressIndicatorLayerTopLevelObjects = nil; + nibLoader = [[NSNib alloc] initWithNibNamed:@"ProgressIndicatorLayer" bundle:[NSBundle mainBundle]]; + if (![nibLoader instantiateNibWithOwner:self topLevelObjects:&progressIndicatorLayerTopLevelObjects]) { NSLog(@"Progress indicator layer could not be loaded; progress display will not function correctly."); + } else { + [nibObjectsToRelease addObjectsFromArray:progressIndicatorLayerTopLevelObjects]; } + [nibLoader release]; - // Set up the progress indicator child window and layer - add to main window, change indicator color and size + // Retain the icon accessory view to allow it to be added and removed from windows + [titleAccessoryView retain]; + + // Set up the progress indicator child window and layer - change indicator color and size [taskProgressIndicator setForeColor:[NSColor whiteColor]]; taskProgressWindow = [[NSWindow alloc] initWithContentRect:[taskProgressLayer bounds] styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + [taskProgressWindow setReleasedWhenClosed:NO]; [taskProgressWindow setOpaque:NO]; [taskProgressWindow setBackgroundColor:[NSColor clearColor]]; [taskProgressWindow setAlphaValue:0.0]; - [taskProgressWindow orderFront:self]; - [tableWindow addChildWindow:taskProgressWindow ordered:NSWindowAbove]; - [taskProgressWindow setReleasedWhenClosed:YES]; [taskProgressWindow setContentView:taskProgressLayer]; - [self centerTaskWindow]; } /** @@ -266,8 +249,7 @@ NSInteger connectionType = -1; - // Inform about the data source in the window title bar - [tableWindow setTitle:[self displaySPName]]; + [self updateWindowTitle:self]; // Clean fields [connectionController setName:@""]; @@ -305,7 +287,7 @@ [alert setAlertStyle:NSCriticalAlertStyle]; [alert runModal]; if (spf) [spf release]; - [self close]; + [self closeAndDisconnect]; return; } @@ -313,7 +295,7 @@ if(![[spf objectForKey:@"format"] isEqualToString:@"connection"]) { NSLog(@"SPF file format is not 'connection'."); [spf release]; - [self close]; + [self closeAndDisconnect]; return; } @@ -327,7 +309,7 @@ [alert setAlertStyle:NSCriticalAlertStyle]; [alert runModal]; [spf release]; - [self close]; + [self closeAndDisconnect]; return; } @@ -339,7 +321,7 @@ [inputTextWindowSecureTextField setStringValue:@""]; [inputTextWindowSecureTextField selectText:nil]; - [NSApp beginSheet:inputTextWindow modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [NSApp beginSheet:inputTextWindow modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; // wait for encryption password NSModalSession session = [NSApp beginModalSessionForWindow:inputTextWindow]; @@ -366,7 +348,7 @@ if(passwordSheetReturnCode) encryptpw = [inputTextWindowSecureTextField stringValue]; else { - [self close]; + [self closeAndDisconnect]; [spf release]; return; } @@ -392,7 +374,7 @@ [alert setAlertStyle:NSCriticalAlertStyle]; [alert runModal]; - [self close]; + [self closeAndDisconnect]; [spf release]; return; } @@ -407,7 +389,7 @@ [alert setAlertStyle:NSCriticalAlertStyle]; [alert runModal]; - [self close]; + [self closeAndDisconnect]; [spf release]; return; } @@ -422,7 +404,7 @@ [alert setAlertStyle:NSCriticalAlertStyle]; [alert runModal]; - [self close]; + [self closeAndDisconnect]; [spf release]; return; } @@ -545,7 +527,7 @@ // Restore toolbar setting if([spfSession objectForKey:@"isToolbarVisible"]) - [[tableWindow toolbar] setVisible:[[spfSession objectForKey:@"isToolbarVisible"] boolValue]]; + [mainToolbar setVisible:[[spfSession objectForKey:@"isToolbarVisible"] boolValue]]; // TODO up to now it doesn't work if([spfSession objectForKey:@"contentSelectedIndexSet"]) { @@ -592,7 +574,7 @@ [[tablesListInstance valueForKeyPath:@"tablesListView"] scrollRowToVisible:[tables indexOfObject:[spfSession objectForKey:@"selectedTable"]]]; - [tableWindow setTitle:[self displaySPName]]; + [self updateWindowTitle:self]; // dealloc spfSession data [spfSession release]; @@ -650,7 +632,7 @@ [self setFileURL:[[SPQueryController sharedQueryController] registerDocumentWithFileURL:[self fileURL] andContextInfo:spfPreferences]]; // ...but hide the icon while the document is temporary - if ([self isUntitled]) [[tableWindow standardWindowButton:NSWindowDocumentIconButton] setImage:nil]; + if ([self isUntitled]) [[parentWindow standardWindowButton:NSWindowDocumentIconButton] setImage:nil]; // Set the connection encoding NSString *encodingName = [prefs objectForKey:SPDefaultEncoding]; @@ -672,6 +654,7 @@ // Update the database list [self setDatabases:self]; + [chooseDatabaseButton setEnabled:!_isWorkingLevel]; // For each of the main controllers, assign the current connection [tablesListInstance setConnection:mySQLConnection]; @@ -690,12 +673,12 @@ // Set the cutom query editor's MySQL version [customQueryInstance setMySQLversion:mySQLVersion]; - [tableWindow setTitle:[self displaySPName]]; + [self updateWindowTitle:self]; // Connected Growl notification [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Connected" - description:[NSString stringWithFormat:NSLocalizedString(@"Connected to %@",@"description for connected growl notification"), [tableWindow title]] - window:tableWindow + description:[NSString stringWithFormat:NSLocalizedString(@"Connected to %@",@"description for connected growl notification"), [parentWindow title]] + document:self notificationName:@"Connected"]; // Init Custom Query editor with the stored queries in a spf file if given. @@ -722,9 +705,9 @@ // Set focus to table list filter field if visible // otherwise set focus to Table List view if ( [[tablesListInstance tables] count] > 20 ) - [tableWindow makeFirstResponder:listFilterField]; + [parentWindow makeFirstResponder:listFilterField]; else - [tableWindow makeFirstResponder:[tablesListInstance valueForKeyPath:@"tablesListView"]]; + [parentWindow makeFirstResponder:[tablesListInstance valueForKeyPath:@"tablesListView"]]; if(spfSession != nil) { @@ -764,25 +747,6 @@ return mySQLConnection; } - -/** - * Set whether the connection controller should automatically start - * connecting; called by maincontroller, but only for first window. - */ -- (void)setShouldAutomaticallyConnect:(BOOL)shouldAutomaticallyConnect -{ - _shouldOpenConnectionAutomatically = shouldAutomaticallyConnect; -} - -/** - * Allow the connection controller to determine whether connection should - * be automatically triggered. - */ -- (BOOL)shouldAutomaticallyConnect -{ - return _shouldOpenConnectionAutomatically; -} - /** * Sets this connection's Keychain ID. */ @@ -925,7 +889,7 @@ [databaseNameField setStringValue:@""]; [NSApp beginSheet:databaseSheet - modalForWindow:tableWindow + modalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"addDatabase"]; @@ -943,7 +907,7 @@ [copyDatabaseMessageField setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Duplicate database '%@' to:", @"duplicate database message"), selectedDatabase]]; [NSApp beginSheet:databaseCopySheet - modalForWindow:tableWindow + modalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"copyDatabase"]; @@ -960,7 +924,7 @@ [renameDatabaseMessageField setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Rename database '%@' to:", @"rename database message"), selectedDatabase]]; [NSApp beginSheet:databaseRenameSheet - modalForWindow:tableWindow + modalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"renameDatabase"]; @@ -991,7 +955,7 @@ [alert setAlertStyle:NSCriticalAlertStyle]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removeDatabase"]; + [alert beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removeDatabase"]; } /** @@ -1008,7 +972,7 @@ [prefs addObserver:serverVariablesController forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL]; } - [serverVariablesController displayServerVariablesSheetAttachedToWindow:tableWindow]; + [serverVariablesController displayServerVariablesSheetAttachedToWindow:parentWindow]; } /** @@ -1108,7 +1072,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, parentWindow, self, nil, nil, [error objectAtIndex:1]); } @@ -1135,12 +1099,12 @@ if (selectedDatabase) [selectedDatabase release], selectedDatabase = nil; selectedDatabase = [[NSString alloc] initWithString:dbName]; [chooseDatabaseButton selectItemWithTitle:selectedDatabase]; - [tableWindow setTitle:[self displaySPName]]; + [self updateWindowTitle:self]; } } else { if (selectedDatabase) [selectedDatabase release], selectedDatabase = nil; [chooseDatabaseButton selectItemAtIndex:0]; - [tableWindow setTitle:[self displaySPName]]; + [self updateWindowTitle:self]; } } @@ -1284,6 +1248,7 @@ databaseListIsSelectable = NO; [[NSNotificationCenter defaultCenter] postNotificationName:SPDocumentTaskStartNotification object:self]; [mainToolbar validateVisibleItems]; + [chooseDatabaseButton setEnabled:NO]; // Schedule appearance of the task window in the near future taskDrawTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(showTaskProgressWindow:) userInfo:nil repeats:NO] retain]; @@ -1399,6 +1364,7 @@ databaseListIsSelectable = YES; [[NSNotificationCenter defaultCenter] postNotificationName:SPDocumentTaskEndNotification object:self]; [mainToolbar validateVisibleItems]; + [chooseDatabaseButton setEnabled:_isConnected]; } } @@ -1476,7 +1442,7 @@ - (void) centerTaskWindow { NSPoint newBottomLeftPoint; - NSRect mainWindowRect = [tableWindow frame]; + NSRect mainWindowRect = [parentWindow frame]; NSRect taskWindowRect = [taskProgressWindow frame]; newBottomLeftPoint.x = round(mainWindowRect.origin.x + mainWindowRect.size.width/2 - taskWindowRect.size.width/2); @@ -1737,7 +1703,7 @@ defaultButton:NSLocalizedString(@"OK", @"OK button") alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"The creation syntax could not be retrieved due to a permissions error.\n\nPlease check your user permissions with an administrator.", @"Create syntax permission denied detail")] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; return; } @@ -1753,7 +1719,7 @@ // Show variables sheet [NSApp beginSheet:createTableSyntaxWindow - modalForWindow:tableWindow + modalForWindow:parentWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; @@ -1806,7 +1772,7 @@ defaultButton:NSLocalizedString(@"OK", @"OK button") alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"The creation syntax could not be retrieved due to a permissions error.\n\nPlease check your user permissions with an administrator.", @"Create syntax permission denied detail")] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; return; } @@ -1822,7 +1788,7 @@ // Table syntax copied Growl notification [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Syntax Copied" description:[NSString stringWithFormat:NSLocalizedString(@"Syntax for %@ table copied",@"description for table syntax copied growl notification"), [self table]] - window:tableWindow + document:self notificationName:@"Syntax Copied"]; } @@ -1851,7 +1817,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"An error occurred while trying to check the %@.\n\nMySQL said:%@",@"an error occurred while trying to check the %@.\n\nMySQL said:%@"), what, [mySQLConnection getLastErrorMessage]]] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -1886,7 +1852,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:message] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -1898,7 +1864,7 @@ [alert setInformativeText:message]; [alert setMessageText:NSLocalizedString(@"Error while checking selected items", @"error while checking selected items message")]; [alert setAccessoryView:statusTableAccessoryView]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; + [alert beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; } } @@ -1927,7 +1893,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"An error occurred while analyzing the %@.\n\nMySQL said:%@",@"an error occurred while analyzing the %@.\n\nMySQL said:%@"), what, [mySQLConnection getLastErrorMessage]]] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -1962,7 +1928,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:message] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -1974,7 +1940,7 @@ [alert setInformativeText:message]; [alert setMessageText:NSLocalizedString(@"Error while analyzing selected items", @"error while analyzing selected items message")]; [alert setAccessoryView:statusTableAccessoryView]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; + [alert beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; } } @@ -2003,7 +1969,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"An error occurred while optimzing the %@.\n\nMySQL said:%@",@"an error occurred while trying to optimze the %@.\n\nMySQL said:%@"), what, [mySQLConnection getLastErrorMessage]]] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -2038,7 +2004,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:message] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -2050,7 +2016,7 @@ [alert setInformativeText:message]; [alert setMessageText:NSLocalizedString(@"Error while optimizing selected items", @"error while optimizing selected items message")]; [alert setAccessoryView:statusTableAccessoryView]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; + [alert beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; } } @@ -2078,7 +2044,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"An error occurred while repairing the %@.\n\nMySQL said:%@",@"an error occurred while trying to repair the %@.\n\nMySQL said:%@"), what, [mySQLConnection getLastErrorMessage]]] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -2113,7 +2079,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:message] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -2125,7 +2091,7 @@ [alert setInformativeText:message]; [alert setMessageText:NSLocalizedString(@"Error while repairing selected items", @"error while repairing selected items message")]; [alert setAccessoryView:statusTableAccessoryView]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; + [alert beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; } } @@ -2153,7 +2119,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"An error occurred while flushing the %@.\n\nMySQL said:%@",@"an error occurred while trying to flush the %@.\n\nMySQL said:%@"), what, [mySQLConnection getLastErrorMessage]]] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -2188,7 +2154,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:message] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -2200,7 +2166,7 @@ [alert setInformativeText:message]; [alert setMessageText:NSLocalizedString(@"Error while flushing selected items", @"error while flushing selected items message")]; [alert setAccessoryView:statusTableAccessoryView]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; + [alert beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; } } @@ -2227,7 +2193,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"An error occurred while performing the checksum on %@.\n\nMySQL said:%@",@"an error occurred while performing the checksum on the %@.\n\nMySQL said:%@"), what, [mySQLConnection getLastErrorMessage]]] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -2244,7 +2210,7 @@ alternateButton:nil otherButton:nil informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"Table checksum: %@",@"table checksum: %@"), message]] - beginSheetModalForWindow:tableWindow + beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:NULL contextInfo:NULL]; @@ -2256,7 +2222,7 @@ [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Checksums of %@",@"Checksums of %@ message"), what]]; [alert setMessageText:NSLocalizedString(@"Table checksum",@"table checksum message")]; [alert setAccessoryView:statusTableAccessoryView]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; + [alert beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"statusError"]; } } @@ -2293,7 +2259,7 @@ // Table syntax copied Growl notification [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Syntax Copied" description:[NSString stringWithFormat:NSLocalizedString(@"Syntax for %@ table copied", @"description for table syntax copied growl notification"), [self table]] - window:tableWindow + document:self notificationName:@"Syntax Copied"]; } } @@ -2365,13 +2331,13 @@ [alert setAlertStyle:NSCriticalAlertStyle]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"cannotremovefield"]; + [alert beginSheetModalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"cannotremovefield"]; return; } [NSApp beginSheet:[userManagerInstance window] - modalForWindow:tableWindow + modalForWindow:parentWindow modalDelegate:userManagerInstance didEndSelector:@selector(userManagerSheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; @@ -2382,7 +2348,7 @@ */ - (void)doPerformQueryService:(NSString *)query { - [tableWindow makeKeyAndOrderFront:self]; + [parentWindow makeKeyAndOrderFront:self]; [tablesListInstance doPerformQueryService:query]; } @@ -2404,26 +2370,29 @@ 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, 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, parentWindow, 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, [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, parentWindow, self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't flush privileges.\nMySQL said: %@", @"message of panel when flushing privs failed"), [mySQLConnection getLastErrorMessage]]); } } - (IBAction)openCurrentConnectionInNewWindow:(id)sender { - TableDocument *newTableDocument; + [[NSApp delegate] newWindow:self]; + TableDocument *newTableDocument = [[NSApp delegate] frontDocument]; + [newTableDocument initWithConnectionFile:[[self fileURL] path]]; +} - // Manually open a new document, setting SPAppController as sender to trigger autoconnection - if (newTableDocument = [[NSDocumentController sharedDocumentController] makeUntitledDocumentOfType:@"Sequel Pro connection" error:nil]) { - [newTableDocument setShouldAutomaticallyConnect:NO]; - [[NSDocumentController sharedDocumentController] addDocument:newTableDocument]; - [newTableDocument makeWindowControllers]; - [newTableDocument showWindows]; - [newTableDocument initWithConnectionFile:[[self fileURL] path]]; - } +/** + * Ask the connection controller to initiate connection, if it hasn't + * already. Used to support automatic connections on window open, + */ +- (void)connect +{ + if (mySQLVersion) return; + [connectionController initiateConnection:self]; } - (void)closeConnection @@ -2433,8 +2402,8 @@ // Disconnected Growl notification [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Disconnected" - description:[NSString stringWithFormat:NSLocalizedString(@"Disconnected from %@",@"description for disconnected growl notification"), [tableWindow title]] - window:tableWindow + description:[NSString stringWithFormat:NSLocalizedString(@"Disconnected from %@",@"description for disconnected growl notification"), [parentTabViewItem label]] + document:self notificationName:@"Disconnected"]; } @@ -2453,7 +2422,7 @@ */ - (BOOL)isUntitled { - return ([[self fileURL] isFileURL]) ? NO : YES; + return ([self fileURL] && [[self fileURL] isFileURL]) ? NO : YES; } /** @@ -2463,7 +2432,7 @@ */ - (BOOL)couldCommitCurrentViewActions { - [tableWindow endEditingFor:nil]; + [parentWindow endEditingFor:nil]; switch ([tableTabView indexOfTabViewItem:[tableTabView selectedTabViewItem]]) { // Table structure view @@ -2484,6 +2453,16 @@ #pragma mark - #pragma mark Accessor methods + +/** + * Returns the parent view, which in its turn contains the database view for this + * connection. + */ +- (NSView *)parentView +{ + return parentView; +} + /** * Returns the host */ @@ -2604,6 +2583,7 @@ */ - (void)willPerformQuery:(NSNotification *)notification { + isProcessing = YES; [queryProgressBar startAnimation:self]; } @@ -2612,6 +2592,7 @@ */ - (void)hasPerformedQuery:(NSNotification *)notification { + isProcessing = NO; [queryProgressBar stopAnimation:self]; } @@ -2686,7 +2667,8 @@ return; } - // Load accessory nib each time + // Load accessory nib each time. + // Note that the top-level objects aren't released automatically, but are released when the panel ends. if(![NSBundle loadNibNamed:@"SaveSPFAccessory" owner:self]) { NSLog(@"SaveSPFAccessory accessory dialog could not be loaded."); return; @@ -2734,7 +2716,7 @@ [panel beginSheetForDirectory:nil file:filename - modalForWindow:tableWindow + modalForWindow:parentWindow modalDelegate:self didEndSelector:@selector(saveConnectionPanelDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; @@ -2763,7 +2745,6 @@ - (void)saveConnectionPanelDidEnd:(NSSavePanel *)panel returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - if ( returnCode ) { NSString *fileName = [panel filename]; @@ -2799,8 +2780,11 @@ [self saveDocumentWithFilePath:fileName inBackground:NO onlyPreferences:NO]; + // Manually loaded nibs don't have their top-level objects released automatically - do that here. + [saveConnectionAccessory release]; + if(contextInfo == @"saveSPFfileAndClose") - [self close]; + [self closeAndDisconnect]; } } } @@ -3010,7 +2994,7 @@ } [session setObject:aString forKey:@"view"]; - [session setObject:[NSNumber numberWithBool:[[tableWindow toolbar] isVisible]] forKey:@"isToolbarVisible"]; + [session setObject:[NSNumber numberWithBool:[[parentWindow toolbar] isVisible]] forKey:@"isToolbarVisible"]; [session setObject:[self connectionEncoding] forKey:@"connectionEncoding"]; [session setObject:[NSNumber numberWithBool:[tableContentInstance sortColumnIsAscending]] forKey:@"contentSortColIsAsc"]; @@ -3090,7 +3074,7 @@ [self setFileURL:[NSURL fileURLWithPath:fileName]]; [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:[NSURL fileURLWithPath:fileName]]; - [tableWindow setTitle:[self displaySPName]]; + [self updateWindowTitle:self]; // Store doc data permanently [spfDocData removeAllObjects]; @@ -3161,7 +3145,7 @@ } if (!_isConnected || _isWorkingLevel) { - return ([menuItem action] == @selector(newDocument:) || [menuItem action] == @selector(terminate:)); + return ([menuItem action] == @selector(newWindow:) || [menuItem action] == @selector(terminate:) || [menuItem action] == @selector(closeTab:) || [menuItem action] == @selector(newTab:)); } if ([menuItem action] == @selector(openCurrentConnectionInNewWindow:)) @@ -3291,7 +3275,8 @@ return NO; } - return [super validateMenuItem:menuItem]; + // Default to YES for unhandled menus + return YES; } - (IBAction)viewStructure:(id)sender @@ -3339,7 +3324,7 @@ [spHistoryControllerInstance updateHistoryEntries]; // Set the focus on the text field - [tableWindow makeFirstResponder:customQueryTextView]; + [parentWindow makeFirstResponder:customQueryTextView]; [prefs setInteger:SPQueryEditorViewMode forKey:SPLastViewMode]; } @@ -3363,7 +3348,7 @@ [extendedTableInfoInstance loadTable:[self table]]; } - [tableWindow makeFirstResponder:[extendedTableInfoInstance valueForKeyPath:@"tableCreateSyntaxTextView"]]; + [parentWindow makeFirstResponder:[extendedTableInfoInstance valueForKeyPath:@"tableCreateSyntaxTextView"]]; [prefs setInteger:SPTableInfoViewMode forKey:SPLastViewMode]; } @@ -3447,6 +3432,72 @@ #pragma mark - #pragma mark Titlebar Methods +/** + * Update the window title. + */ +- (void) updateWindowTitle:(id)sender +{ + NSMutableString *tabTitle; + NSMutableString *windowTitle; + TableDocument *frontTableDocument = [parentWindowController selectedTableDocument]; + + // Determine name details + NSString *pathName = @""; + if ([[[self fileURL] path] length] && ![self isUntitled]) { + pathName = [NSString stringWithFormat:@"%@ — ", [[[self fileURL] path] lastPathComponent]]; + } + if (!_isConnected) { + windowTitle = [NSString stringWithFormat:@"%@%@", pathName, @"Sequel Pro"]; + tabTitle = windowTitle; + } else { + windowTitle = [NSMutableString string]; + tabTitle = [NSMutableString string]; + + // Add the path to the window title + [windowTitle appendString:pathName]; + + // Add the MySQL version to the window title if enabled in prefs + if ([prefs boolForKey:SPDisplayServerVersionInWindowTitle]) [windowTitle appendFormat:@"(MySQL %@) ", mySQLVersion]; + + // Add the name to the window + [windowTitle appendString:[self name]]; + + // Also add to the frontmost tab, and other tabs if the host is different, not connected, or no db is selected + if (frontTableDocument == self || [[frontTableDocument name] isNotEqualTo:[self name]] || ![frontTableDocument getConnection] || ![self database]) { + [tabTitle appendString:[self name]]; + } + + // If a database is selected, add to the window - and other tabs if host is the same but table is set + if ([self database]) { + [windowTitle appendFormat:@"/%@", [self database]]; + if (frontTableDocument == self + || [[frontTableDocument name] isNotEqualTo:[self name]] + || ![[self table] length]) + { + if ([tabTitle length]) [tabTitle appendString:@"/"]; + [tabTitle appendString:[self database]]; + } + } + + // Add the table name if one is selected + if ([[self table] length]) { + [windowTitle appendFormat:@"/%@", [self table]]; + if ([tabTitle length]) [tabTitle appendString:@"/"]; + [tabTitle appendString:[self table]]; + } + } + + // Set the titles + [parentTabViewItem setLabel:tabTitle]; + if ([parentWindowController selectedTableDocument] == self) { + [parentWindow setTitle:windowTitle]; + } + + // If the sender wasn't the window controller, update other tabs in this window + // for shared pathname updates + if ([sender class] != [SPWindowController class]) [parentWindowController updateAllTabTitles:self]; +} + /** * Set the connection status icon in the titlebar */ @@ -3482,7 +3533,7 @@ - (void)setupToolbar { // create a new toolbar instance, and attach it to our document window - mainToolbar = [[[NSToolbar alloc] initWithIdentifier:@"TableWindowToolbar"] autorelease]; + mainToolbar = [[NSToolbar alloc] initWithIdentifier:@"TableWindowToolbar"]; // set up toolbar properties [mainToolbar setAllowsUserCustomization:YES]; @@ -3492,9 +3543,6 @@ // set ourself as the delegate [mainToolbar setDelegate:self]; - // attach the toolbar to the document window - [tableWindow setToolbar:mainToolbar]; - // update the toolbar item size [self updateChooseDatabaseToolbarItemWidth]; @@ -3525,6 +3573,7 @@ [toolbarItem setMaxSize:NSMakeSize(200,32)]; [chooseDatabaseButton setTarget:self]; [chooseDatabaseButton setAction:@selector(chooseDatabase:)]; + [chooseDatabaseButton setEnabled:(_isConnected && !_isWorkingLevel)]; if (willBeInsertedIntoToolbar) { chooseDatabaseToolbarItem = toolbarItem; @@ -3698,7 +3747,7 @@ /** * Validates the toolbar items */ -- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem; +- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem { if (!_isConnected || _isWorkingLevel) return NO; @@ -3732,40 +3781,50 @@ } #pragma mark - -#pragma mark NSDocument methods +#pragma mark Tab methods /** - * Returns the name of the nib file + * Make this document's window frontmost in the application, + * and ensure this tab is selected. */ -- (NSString *)windowNibName +- (void)makeKeyDocument { - return @"DBView"; + [[[self parentWindow] onMainThread] makeKeyAndOrderFront:self]; + [[[[self parentTabViewItem] onMainThread] tabView] selectTabViewItemWithIdentifier:self]; } /** - * Code that need to be executed once the windowController has loaded the document's window - * sets upt the interface (small fonts). + * Invoked to determine whether the parent tab is allowed to close */ -- (void)windowControllerDidLoadNib:(NSWindowController *)aController +- (BOOL)parentTabShouldClose { - [aController setShouldCascadeWindows:YES]; - [super windowControllerDidLoadNib:aController]; - //register for notifications - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willPerformQuery:) - name:@"SMySQLQueryWillBePerformed" object:self]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hasPerformedQuery:) - name:@"SMySQLQueryHasBeenPerformed" object:self]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) - name:@"NSApplicationWillTerminateNotification" object:nil]; -} + // If no connection is available, always return YES. Covers initial setup and disconnections. + if(!_isConnected) return YES; -// NSWindow delegate methods + // If tasks are active, return NO to allow tasks to complete + if (_isWorkingLevel) return NO; + + // If the table list considers itself to be working, return NO. This catches open alerts, and + // edits in progress in various views. + if ( ![tablesListInstance selectionShouldChangeInTableView:nil] ) return NO; + + // Auto-save spf file based connection and return whether the save was successful + if([self fileURL] && [[[self fileURL] path] length] && ![self isUntitled]) { + BOOL isSaved = [self saveDocumentWithFilePath:nil inBackground:YES onlyPreferences:YES]; + if(isSaved) + [[SPQueryController sharedQueryController] removeRegisteredDocumentWithFileURL:[self fileURL]]; + return isSaved; + } + + // Return YES by default + return YES; +} /** - * Invoked when the document window is about to close + * Invoked when the parent tab is about to close */ -- (void)windowWillClose:(NSNotification *)aNotification +- (void)parentTabDidClose { // Cancel autocompletion trigger @@ -3787,37 +3846,64 @@ if ([[[SPQueryController sharedQueryController] window] isVisible]) [self toggleConsole:self]; [createTableSyntaxWindow orderOut:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self]; + [self setParentWindow:nil]; } /** - * Invoked when the document window should close + * Invoked when the parent tab is currently the active tab in the + * window, but is being switched away from, to allow cleaning up + * details in the window. */ -- (BOOL)windowShouldClose:(id)sender +- (void)willResignActiveTabInWindow { - // If no connection is available, always return YES. Covers initial setup and disconnections. - if(!_isConnected) return YES; + // Remove the icon accessory view from the title bar + [titleAccessoryView removeFromSuperview]; - // If tasks are active, return NO to allow tasks to complete - if (_isWorkingLevel) return NO; + // Remove the task progress window + [parentWindow removeChildWindow:taskProgressWindow]; + [taskProgressWindow orderOut:self]; +} - // If the table list considers itself to be working, return NO. This catches open alerts, and - // edits in progress in various views. - if ( ![tablesListInstance selectionShouldChangeInTableView:nil] ) return NO; +/** + * Invoked when the parent tab became the active tab in the window, + * to allow the window to reflect the contents of this view. + */ +- (void)didBecomeActiveTabInWindow +{ - // Auto-save spf file based connection and return whether the save was successful - if([self fileURL] && [[[self fileURL] path] length] && ![self isUntitled]) { - BOOL isSaved = [self saveDocumentWithFilePath:nil inBackground:YES onlyPreferences:YES]; - if(isSaved) - [[SPQueryController sharedQueryController] removeRegisteredDocumentWithFileURL:[self fileURL]]; - return isSaved; - } + // Update the toolbar + [parentWindow setToolbar:mainToolbar]; - // Return YES by default - return YES; + // Update the window's title and represented document + [self updateWindowTitle:self]; + if (spfFileURL && [spfFileURL isFileURL]) + [parentWindow setRepresentedURL:spfFileURL]; + else + [parentWindow setRepresentedURL:nil]; + + // Add the icon accessory view to the title bar + NSView *windowFrame = [[parentWindow contentView] superview]; + NSRect av = [titleAccessoryView frame]; + NSRect initialAccessoryViewFrame = NSMakeRect( + [windowFrame frame].size.width - av.size.width - 30, + [windowFrame frame].size.height - av.size.height, + av.size.width, + av.size.height); + [titleAccessoryView setFrame:initialAccessoryViewFrame]; + [windowFrame addSubview:titleAccessoryView]; + + // Add the progress window to this window + [self centerTaskWindow]; + [taskProgressWindow orderFront:self]; + [parentWindow addChildWindow:taskProgressWindow ordered:NSWindowAbove]; } -- (void)windowDidBecomeKey:(NSNotification *)notification +/** + * Invoked when the parent tab became the key tab in the application; + * the selected tab in the frontmost window. + */ +- (void)tabDidBecomeKey { // Synchronize Navigator with current active document if Navigator runs in syncMode if([[SPNavigatorController sharedNavigatorController] syncMode] && [self connectionID] && ![[self connectionID] isEqualToString:@"_"]) { @@ -3838,19 +3924,67 @@ /** * Invoked when the document window is resized */ -- (void)windowDidResize:(NSNotification *)notification +- (void)tabDidResize { - // If the task interface is visible, re-center the task child window - if (_isWorkingLevel) [self centerTaskWindow]; + // If the task interface is visible, and this tab is frontmost, re-center the task child window + if (_isWorkingLevel && [parentWindowController selectedTableDocument] == self) [self centerTaskWindow]; } /** - * Invoked when the user command-clicks on the window title to see the document path + * Support the tab's progress spinner */ -- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu +- (BOOL)isProcessing +{ + return (isProcessing || (_isWorkingLevel > 0)); +} +- (void)setIsProcessing:(BOOL)value { - return ![self isUntitled]; + isProcessing = value; +} + +/** + * Set the parent window + */ +- (void)setParentWindow:(NSWindow *)aWindow +{ + parentWindow = aWindow; + SPSSHTunnel *currentTunnel = [connectionController valueForKeyPath:@"sshTunnel"]; + if (currentTunnel) [currentTunnel setParentWindow:parentWindow]; +} + +/** + * Return the parent window + */ +- (NSWindow *)parentWindow +{ + return parentWindow; +} + +#pragma mark - +#pragma mark NSDocument compatibility + +/** + * Set the NSURL for a .spf file for this connection instance. + */ +- (void)setFileURL:(NSURL *)theURL +{ + if (spfFileURL) [spfFileURL release], spfFileURL = nil; + spfFileURL = [theURL retain]; + if ([parentWindowController selectedTableDocument] == self) { + if (spfFileURL && [spfFileURL isFileURL]) + [parentWindow setRepresentedURL:spfFileURL]; + else + [parentWindow setRepresentedURL:nil]; + } +} + +/** + * Retrieve the NSURL for the .spf file for this connection instance (if any) + */ +- (NSURL *)fileURL +{ + return [[spfFileURL copy] autorelease]; } /* @@ -3883,27 +4017,13 @@ /** * The window title for this document. */ -- (NSString *)displaySPName +- (NSString *)displayName { if (!_isConnected) { return [NSString stringWithFormat:@"%@%@", ([[[self fileURL] path] length] && ![self isUntitled]) ? [NSString stringWithFormat:@"%@ — ",[[[self fileURL] path] lastPathComponent]] : @"", @"Sequel Pro"]; } - - return [NSString stringWithFormat:@"%@%@ %@%@%@", - ([[[self fileURL] path] length] && ![self isUntitled]) ? [NSString stringWithFormat:@"%@ — ",[self displayName]] : @"", - ([prefs boolForKey:SPDisplayServerVersionInWindowTitle]) ? [NSString stringWithFormat:@"(MySQL %@)", mySQLVersion] : @"", - [self name], - ([self database]?[NSString stringWithFormat:@"/%@",[self database]]:@""), - ([[self table] length]?[NSString stringWithFormat:@"/%@",[self table]]:@"")]; -} -/** - * The window title for this document. - */ -- (NSString *)displayName -{ - if(!_isConnected) return [self displaySPName]; return [[[self fileURL] path] lastPathComponent]; } @@ -3916,7 +4036,10 @@ - (void)connectionControllerInitiatingConnection:(id)controller { // Update the window title to indicate that we are try to establish a connection - [tableWindow setTitle:NSLocalizedString(@"Connecting…", @"window title string indicating that sp is connecting")]; + [parentTabViewItem setLabel:NSLocalizedString(@"Connecting…", @"window title string indicating that sp is connecting")]; + if ([parentWindowController selectedTableDocument] == self) { + [parentWindow setTitle:NSLocalizedString(@"Connecting…", @"window title string indicating that sp is connecting")]; + } } /** @@ -3925,7 +4048,7 @@ - (void)connectionControllerConnectAttemptFailed:(id)controller { // Reset the window title - [tableWindow setTitle:[self displaySPName]]; + [self updateWindowTitle:self]; } #pragma mark - @@ -4105,6 +4228,7 @@ */ - (void)dealloc { +NSLog(@"is dealloc'd"); // Unregister observers [prefs removeObserver:self forKeyPath:SPDisplayTableViewVerticalGridlines]; @@ -4123,6 +4247,9 @@ [[NSNotificationCenter defaultCenter] removeObserver:self]; [NSObject cancelPreviousPerformRequestsWithTarget:self]; + for (id retainedObject in nibObjectsToRelease) [retainedObject release]; + [nibObjectsToRelease release]; + [_encoding release]; [allDatabases release]; [allSystemDatabases release]; @@ -4138,10 +4265,14 @@ if (taskDrawTimer) [taskDrawTimer release]; if (taskFadeAnimator) [taskFadeAnimator release]; if (queryEditorInitString) [queryEditorInitString release]; + if (spfFileURL) [spfFileURL release]; if (spfPreferences) [spfPreferences release]; if (spfSession) [spfSession release]; if (spfDocData) [spfDocData release]; if (keyChainID) [keyChainID release]; + if (mainToolbar) [mainToolbar release]; + if (titleAccessoryView) [titleAccessoryView release]; + if (taskProgressWindow) [taskProgressWindow release]; [super dealloc]; } @@ -4152,12 +4283,12 @@ - (void)_copyDatabase { if ([[databaseCopyNameField stringValue] isEqualToString:@""]) { - 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")); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, parentWindow, self, nil, nil, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given")); return; } SPDatabaseCopy *dbActionCopy = [[SPDatabaseCopy alloc] init]; [dbActionCopy setConnection: [self getConnection]]; - [dbActionCopy setMessageWindow: tableWindow]; + [dbActionCopy setMessageWindow: parentWindow]; BOOL copyWithContent = [copyDatabaseDataButton state] == NSOnState; @@ -4172,12 +4303,12 @@ - (void)_renameDatabase { if ([[databaseRenameNameField stringValue] isEqualToString:@""]) { - 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")); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, parentWindow, self, nil, nil, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given")); return; } SPDatabaseRename *dbActionRename = [[SPDatabaseRename alloc] init]; [dbActionRename setConnection: [self getConnection]]; - [dbActionRename setMessageWindow: tableWindow]; + [dbActionRename setMessageWindow: parentWindow]; if ([dbActionRename renameDatabaseFrom: [self database] to: [databaseRenameNameField stringValue]]) { @@ -4195,7 +4326,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, 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, parentWindow, self, nil, nil, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given")); return; } @@ -4211,14 +4342,14 @@ if ([mySQLConnection queryErrored]) { // An error occurred - 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]]); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, parentWindow, 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, [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, parentWindow, 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]; @@ -4235,7 +4366,7 @@ [tablesListInstance setConnection:mySQLConnection]; [tableDumpInstance setConnection:mySQLConnection]; - [tableWindow setTitle:[self displaySPName]]; + [self updateWindowTitle:self]; } /** @@ -4274,8 +4405,7 @@ [tablesListInstance setConnection:mySQLConnection]; [tableDumpInstance setConnection:mySQLConnection]; - [tableWindow setTitle:[self displaySPName]]; - + [self updateWindowTitle:self]; } /** @@ -4301,7 +4431,7 @@ || ![mySQLConnection selectDB:targetDatabaseName]) { if ( [mySQLConnection isConnected] ) { - 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]); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, parentWindow, 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]; @@ -4328,7 +4458,7 @@ [tableDumpInstance setConnection:mySQLConnection]; // Update the window title - [[tableWindow onMainThread] setTitle:[self displaySPName]]; + [[self onMainThread] updateWindowTitle:self]; // Add a history entry if (!historyStateChanging) { @@ -4339,9 +4469,9 @@ // Set focus to table list filter field if visible // otherwise set focus to Table List view if ( [[tablesListInstance tables] count] > 20 ) - [[tableWindow onMainThread] makeFirstResponder:listFilterField]; + [[parentWindow onMainThread] makeFirstResponder:listFilterField]; else - [[tableWindow onMainThread] makeFirstResponder:[tablesListInstance valueForKeyPath:@"tablesListView"]]; + [[parentWindow onMainThread] makeFirstResponder:[tablesListInstance valueForKeyPath:@"tablesListView"]]; } // If a the table has changed, update the selection diff --git a/Source/TableDump.h b/Source/TableDump.h index e506a6d2..cec3d92b 100644 --- a/Source/TableDump.h +++ b/Source/TableDump.h @@ -38,8 +38,6 @@ IBOutlet id tableDataInstance; IBOutlet id customQueryInstance; - IBOutlet id tableWindow; - IBOutlet id exportDumpView; IBOutlet id exportCSVView; IBOutlet id exportMultipleCSVView; @@ -97,6 +95,7 @@ MCPConnection *mySQLConnection; NSMutableArray *tables; + NSMutableArray *nibObjectsToRelease; // Field Mapper Controller SPFieldMapperController *fieldMapperController; @@ -122,6 +121,7 @@ NSUInteger exportMode; NSUserDefaults *prefs; BOOL progressCancelled; + BOOL _mainNibLoaded; NSSavePanel *currentExportPanel; } diff --git a/Source/TableDump.m b/Source/TableDump.m index d6c703ca..3527ed35 100644 --- a/Source/TableDump.m +++ b/Source/TableDump.m @@ -142,7 +142,7 @@ - (void)export { [self reloadTables:self]; - [NSApp beginSheet:exportWindow modalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; + [NSApp beginSheet:exportWindow modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; } - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo @@ -264,7 +264,7 @@ // Open the savePanel [currentExportPanel beginSheetForDirectory:[prefs objectForKey:@"savePath"] - file:file modalForWindow:tableWindow modalDelegate:self + file:file modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; @@ -325,7 +325,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, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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; @@ -334,7 +334,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, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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; @@ -344,7 +344,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, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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; @@ -374,7 +374,7 @@ [[singleProgressText onMainThread] setStringValue:NSLocalizedString(@"Exporting data...", @"text showing that app is preparing data")]; [[singleProgressBar onMainThread] setUsesThreadedAnimation:YES]; [[singleProgressBar onMainThread] setIndeterminate:YES]; - [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; [[singleProgressSheet onMainThread] makeKeyWindow]; [[singleProgressBar onMainThread] startAnimation:self]; @@ -417,7 +417,7 @@ [[singleProgressText onMainThread] setStringValue:NSLocalizedString(@"Exporting data...", @"text showing that app is preparing data")]; [[singleProgressBar onMainThread] setUsesThreadedAnimation:YES]; [[singleProgressBar onMainThread] setIndeterminate:YES]; - [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; [[singleProgressSheet onMainThread] makeKeyWindow]; [[singleProgressBar onMainThread] startAnimation:self]; @@ -475,14 +475,14 @@ // Display error message on problems if ( !progressCancelled && !success ) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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")); } // Export finished Growl notification [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Export Finished" description:[NSString stringWithFormat:NSLocalizedString(@"Finished exporting to %@",@"description for finished exporting growl notification"), [exportFile lastPathComponent]] - window:tableWindow + document:tableDocumentInstance notificationName:@"Export Finished"]; [pool release]; } @@ -495,12 +495,6 @@ invoked when user clicks on an ImportFromClipboard menuItem */ { - // Load accessory nib each time - if(![NSBundle loadNibNamed:@"ImportAccessory" owner:self]) { - NSBeep(); - NSLog(@"ImportAccessory accessory dialog could not be loaded."); - return; - } // clipboard textview with no wrapping const CGFloat LargeNumberForText = 1.0e7; @@ -527,7 +521,7 @@ [importFromClipboardAccessoryView addSubview:importCSVView]; [NSApp beginSheet:importFromClipboardSheet - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:@"importFromClipboard"]; @@ -543,13 +537,6 @@ // prepare open panel and accessory view NSOpenPanel *openPanel = [NSOpenPanel openPanel]; - // Load accessory nib each time - if(![NSBundle loadNibNamed:@"ImportAccessory" owner:self]) { - NSBeep(); - NSLog(@"ImportAccessory accessory dialog could not be loaded."); - return; - } - // Preset the accessory view with prefs defaults [importFieldsTerminatedField setStringValue:[prefs objectForKey:SPCSVImportFieldTerminator]]; [importLinesTerminatedField setStringValue:[prefs objectForKey:SPCSVImportLineTerminator]]; @@ -567,7 +554,7 @@ // Show openPanel [openPanel beginSheetForDirectory:[prefs objectForKey:@"openPath"] file:[lastFilename lastPathComponent] - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:nil]; @@ -624,7 +611,7 @@ if (!sqlFileHandle) { SPBeginAlertSheet(NSLocalizedString(@"Import Error title", @"Import Error"), NSLocalizedString(@"OK button label", @"OK button"), - nil, nil, tableWindow, self, nil, nil, + nil, nil, [tableDocumentInstance parentWindow], 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]; @@ -646,7 +633,7 @@ [[singleProgressBar onMainThread] startAnimation:self]; // Open the progress sheet - [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; [[singleProgressSheet onMainThread] makeKeyWindow]; [tableDocumentInstance setQueryMode:SPImportExportQueryMode]; @@ -668,7 +655,7 @@ [self closeAndStopProgressSheet]; SPBeginAlertSheet(NSLocalizedString(@"SQL read error title", @"File read error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, + nil, nil, [tableDocumentInstance parentWindow], 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]; @@ -718,7 +705,7 @@ [self closeAndStopProgressSheet]; SPBeginAlertSheet(NSLocalizedString(@"SQL read error title", @"File read error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, + nil, nil, [tableDocumentInstance parentWindow], 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]; @@ -842,7 +829,7 @@ // Import finished Growl notification [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Import Finished" description:[NSString stringWithFormat:NSLocalizedString(@"Finished importing %@",@"description for finished importing growl notification"), [filename lastPathComponent]] - window:tableWindow + document:tableDocumentInstance notificationName:@"Import Finished"]; } @@ -887,7 +874,7 @@ if (!csvFileHandle) { SPBeginAlertSheet(NSLocalizedString(@"Import Error title", @"Import Error"), NSLocalizedString(@"OK button label", @"OK button"), - nil, nil, tableWindow, self, nil, nil, + nil, nil, [tableDocumentInstance parentWindow], 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]; @@ -907,7 +894,7 @@ [[singleProgressBar onMainThread] startAnimation:self]; // Open the progress sheet - [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; [[singleProgressSheet onMainThread] makeKeyWindow]; [tableDocumentInstance setQueryMode:SPImportExportQueryMode]; @@ -947,7 +934,7 @@ [self closeAndStopProgressSheet]; SPBeginAlertSheet(NSLocalizedString(@"CSV read error title", @"File read error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, + nil, nil, [tableDocumentInstance parentWindow], 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]; @@ -996,7 +983,7 @@ [self closeAndStopProgressSheet]; SPBeginAlertSheet(NSLocalizedString(@"CSV read error title", @"File read error"), NSLocalizedString(@"OK", @"OK button"), - nil, nil, tableWindow, self, nil, nil, + nil, nil, [tableDocumentInstance parentWindow], 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]; @@ -1073,7 +1060,7 @@ [[singleProgressBar onMainThread] setIndeterminate:NO]; [[singleProgressBar onMainThread] setMaxValue:fileTotalLength]; [[singleProgressBar onMainThread] startAnimation:self]; - [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; [[singleProgressSheet onMainThread] makeKeyWindow]; // Set up the field names import string for INSERT or REPLACE INTO @@ -1277,7 +1264,7 @@ // Import finished Growl notification [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Import Finished" description:[NSString stringWithFormat:NSLocalizedString(@"Finished importing %@",@"description for finished importing growl notification"), [filename lastPathComponent]] - window:tableWindow + document:tableDocumentInstance notificationName:@"Import Finished"]; // If the table selected for import is also selected in the content view, @@ -1360,7 +1347,6 @@ - (void)startSQLImportProcessWithFile:(NSString *)filename { - if (!importFormatPopup) [NSBundle loadNibNamed:@"ImportAccessory" owner:self]; [importFormatPopup selectItemWithTitle:@"SQL"]; [NSThread detachNewThreadSelector:@selector(importBackgroundProcess:) toTarget:self withObject:filename]; } @@ -1380,7 +1366,7 @@ SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, - tableWindow, self, + [tableDocumentInstance parentWindow], self, nil, nil, NSLocalizedString(@"Could not parse file as CSV", @"Error when we can't parse/split file as CSV") ); @@ -1393,7 +1379,7 @@ SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, - tableWindow, self, + [tableDocumentInstance parentWindow], self, 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") ); @@ -1407,7 +1393,7 @@ SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, - tableWindow, self, + [tableDocumentInstance parentWindow], self, 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") ); @@ -1430,7 +1416,7 @@ // Show field mapper sheet and set the focus to it [[NSApp onMainThread] beginSheet:[fieldMapperController window] - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(fieldMapperDidEndSheet:returnCode:contextInfo:) contextInfo:nil]; @@ -1655,7 +1641,7 @@ // Open the progress sheet [[NSApp onMainThread] beginSheet:singleProgressSheet - modalForWindow:tableWindow modalDelegate:self + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; [[singleProgressSheet onMainThread] makeKeyWindow]; @@ -2162,7 +2148,7 @@ // Open the progress sheet [[NSApp onMainThread] beginSheet:singleProgressSheet - modalForWindow:tableWindow modalDelegate:self + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; [[singleProgressSheet onMainThread] makeKeyWindow]; @@ -2368,7 +2354,7 @@ // Open progress sheet [[NSApp onMainThread] beginSheet:singleProgressSheet - modalForWindow:tableWindow modalDelegate:self + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; [[singleProgressSheet onMainThread] makeKeyWindow]; } @@ -2595,7 +2581,7 @@ // Open progress sheet [[NSApp onMainThread] beginSheet:singleProgressSheet - modalForWindow:tableWindow modalDelegate:self + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; [[singleProgressSheet onMainThread] makeKeyWindow]; } @@ -2762,7 +2748,7 @@ // Open the progress sheet [[NSApp onMainThread] beginSheet:singleProgressSheet - modalForWindow:tableWindow modalDelegate:self + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; [[singleProgressSheet onMainThread] makeKeyWindow]; @@ -3190,8 +3176,18 @@ - (void)awakeFromNib { + if (_mainNibLoaded) return; + _mainNibLoaded = YES; + [self switchTab:[[exportToolbar items] objectAtIndex:0]]; [exportToolbar setSelectedItemIdentifier:[[[exportToolbar items] objectAtIndex:0] itemIdentifier]]; + + // Load the import accessory view, retaining a reference to the top-level objects that need releasing. + NSArray *importAccessoryTopLevelObjects = nil; + NSNib *nibLoader = [[NSNib alloc] initWithNibNamed:@"ImportAccessory" bundle:[NSBundle mainBundle]]; + [nibLoader instantiateNibWithOwner:self topLevelObjects:&importAccessoryTopLevelObjects]; + [nibObjectsToRelease addObjectsFromArray:importAccessoryTopLevelObjects]; + [nibLoader release]; } - (id)init @@ -3199,6 +3195,7 @@ self = [super init]; tables = [[NSMutableArray alloc] init]; + nibObjectsToRelease = [[NSMutableArray alloc] init]; fieldMappingArray = nil; fieldMappingGlobalValueArray = nil; fieldMappingTableColumnNames = nil; @@ -3215,6 +3212,7 @@ prefs = nil; lastFilename = nil; + _mainNibLoaded = NO; return self; } @@ -3225,6 +3223,8 @@ if (fieldMappingImportArray) [fieldMappingImportArray release]; if (lastFilename) [lastFilename release]; if (prefs) [prefs release]; + for (id retainedObject in nibObjectsToRelease) [retainedObject release]; + [nibObjectsToRelease release]; [super dealloc]; } @@ -3256,7 +3256,7 @@ [errorsView setString:message]; [NSApp beginSheet:errorsSheet - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:nil]; diff --git a/Source/TableSource.h b/Source/TableSource.h index 1d5cbcc0..0278f282 100644 --- a/Source/TableSource.h +++ b/Source/TableSource.h @@ -34,7 +34,6 @@ IBOutlet id tableInfoInstance; IBOutlet id extendedTableInfoInstance; - IBOutlet id tableWindow; IBOutlet id indexSheet; IBOutlet id keySheet; IBOutlet id resetAutoIncrementSheet; diff --git a/Source/TableSource.m b/Source/TableSource.m index c3424c0a..1ae93a00 100644 --- a/Source/TableSource.m +++ b/Source/TableSource.m @@ -362,7 +362,7 @@ [alert setAlertStyle:NSCriticalAlertStyle]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"cannotremovefield"]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"cannotremovefield"]; } @@ -400,7 +400,7 @@ [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(removeFieldSheetDidEnd:returnCode:contextInfo:) contextInfo:(hasForeignKey) ? @"removeFieldAndForeignKey" : @"removeField"]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(removeFieldSheetDidEnd:returnCode:contextInfo:) contextInfo:(hasForeignKey) ? @"removeFieldAndForeignKey" : @"removeField"]; } /** @@ -477,7 +477,7 @@ [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(removeIndexSheetDidEnd:returnCode:contextInfo:) contextInfo:(hasForeignKey) ? @"removeIndexAndForeignKey" : @"removeIndex"]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(removeIndexSheetDidEnd:returnCode:contextInfo:) contextInfo:(hasForeignKey) ? @"removeIndexAndForeignKey" : @"removeIndex"]; } /** @@ -516,7 +516,7 @@ // Begin the sheet [NSApp beginSheet:resetAutoIncrementSheet - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(resetAutoincrementSheetDidEnd:returnCode:contextInfo:) contextInfo:@"resetAutoIncrement"]; @@ -577,7 +577,7 @@ // Begin the sheet [NSApp beginSheet:indexSheet - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(addIndexSheetDidEnd:returnCode:contextInfo:) contextInfo:@"addIndex"]; @@ -762,7 +762,7 @@ closes the keySheet isSavingRow = YES; // Save any edits which have been made but not saved to the table yet. - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; // Attempt to save the row, and return YES if the save succeeded. if ([self addRowToDB]) { @@ -921,7 +921,7 @@ closes the keySheet [chooseKeyButton selectItemAtIndex:0]; [NSApp beginSheet:keySheet - modalForWindow:tableWindow modalDelegate:self + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; @@ -980,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, nil, nil, + nil, nil, [tableDocumentInstance parentWindow], 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]]); @@ -1004,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(addRowErrorSheetDidEnd:returnCode:contextInfo:), nil, + NSLocalizedString(@"Discard changes", @"discard changes button"), nil, [tableDocumentInstance parentWindow], 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(addRowErrorSheetDidEnd:returnCode:contextInfo:), nil, + NSLocalizedString(@"Discard changes", @"discard changes button"), nil, [tableDocumentInstance parentWindow], 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]]); } @@ -1036,7 +1036,7 @@ closes the keySheet // 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]; + [tableSourceView performSelector:@selector(keyDown:) withObject:[NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:[[tableDocumentInstance parentWindow] windowNumber] context:[NSGraphicsContext currentContext] characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:0x24] afterDelay:0.0]; } // Discard changes and cancel editing @@ -1077,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, [tableDocumentInstance parentWindow], self, nil, nil, [errorDictionary objectForKey:@"message"]); } @@ -1517,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, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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]; @@ -1774,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, + SPBeginAlertSheet(NSLocalizedString(@"Unable to add index", @"add index error message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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.h b/Source/TablesList.h index 36d3ccaa..6a313d44 100644 --- a/Source/TablesList.h +++ b/Source/TablesList.h @@ -51,7 +51,6 @@ IBOutlet id tableTriggersInstance; IBOutlet SPHistoryController *spHistoryControllerInstance; - IBOutlet id tableWindow; IBOutlet id copyTableSheet; IBOutlet id tablesListView; IBOutlet id copyTableButton; diff --git a/Source/TablesList.m b/Source/TablesList.m index 2a77db53..be617aa2 100644 --- a/Source/TablesList.m +++ b/Source/TablesList.m @@ -271,7 +271,7 @@ return; } - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; // Populate the table type (engine) popup button [tableTypeButton removeAllItems]; @@ -288,7 +288,7 @@ } [NSApp beginSheet:tableSheet - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"addTable"]; @@ -311,7 +311,7 @@ if (![tablesListView numberOfSelectedRows]) return; - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; NSAlert *alert = [NSAlert alertWithMessageText:@"" defaultButton:NSLocalizedString(@"Delete", @"delete button") alternateButton:NSLocalizedString(@"Cancel", @"cancel button") otherButton:nil informativeTextWithFormat:@""]; @@ -379,7 +379,7 @@ [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete the selected %@? This operation cannot be undone.", @"delete tables/views informative message"), tblTypes]]; } - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removeRow"]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removeRow"]; } /** @@ -392,7 +392,7 @@ if ([tablesListView numberOfSelectedRows] != 1) return; if (![tableSourceInstance saveRowOnDeselect] || ![tableContentInstance saveRowOnDeselect]) return; - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; // Detect table type: table or view NSInteger tblType = [[filteredTableTypes objectAtIndex:[tablesListView selectedRow]] integerValue]; @@ -423,7 +423,7 @@ [copyTableContentSwitch setState:NSOffState]; [NSApp beginSheet:copyTableSheet - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"copyTable"]; @@ -438,7 +438,7 @@ return; } - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; if ([tablesListView numberOfSelectedRows] != 1) return; if (![[self tableName] length]) return; @@ -471,7 +471,7 @@ [NSApp beginSheet:tableRenameSheet - modalForWindow:tableWindow + modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"renameTable"]; @@ -486,7 +486,7 @@ if (![tablesListView numberOfSelectedRows]) return; - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; NSAlert *alert = [NSAlert alertWithMessageText:@"" defaultButton:NSLocalizedString(@"Truncate", @"truncate button") @@ -512,7 +512,7 @@ [alert setInformativeText:NSLocalizedString(@"Are you sure you want to delete ALL records in the selected tables? This operation cannot be undone.", @"truncate tables informative message")]; } - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"truncateTable"]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"truncateTable"]; } /** @@ -862,7 +862,7 @@ [[tableSubMenu itemAtIndex:10] setHidden:NO]; // set window title - [tableWindow setTitle:[tableDocumentInstance displaySPName]]; + [tableDocumentInstance updateWindowTitle:self]; return; } @@ -1035,7 +1035,7 @@ } // set window title - [tableWindow setTitle:[tableDocumentInstance displaySPName]]; + [tableDocumentInstance updateWindowTitle:self]; } #pragma mark - @@ -1333,7 +1333,7 @@ if (![self isTableNameValid:newTableName forType:selectedTableType ignoringSelectedTable:YES]) { // 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, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, @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,11 +1380,11 @@ } } @catch (NSException * myException) { - SPBeginAlertSheet( NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, [myException reason]); + SPBeginAlertSheet( NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, [myException reason]); } // Set window title to reflect the new table name - [tableWindow setTitle:[tableDocumentInstance displaySPName]]; + [tableDocumentInstance updateWindowTitle:self]; // Query the structure of all databases in the background (mainly for completion) [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", nil]]; @@ -1425,7 +1425,7 @@ if (!tableListIsSelectable) return NO; // End editing (otherwise problems when user hits reload button) - [tableWindow endEditingFor:nil]; + [[tableDocumentInstance parentWindow] endEditingFor:nil]; if ( alertSheetOpened ) { return NO; @@ -1651,10 +1651,10 @@ - (void) makeTableListFilterHaveFocus { if([tables count] > 20) { - [tableWindow makeFirstResponder:listFilterField]; + [[tableDocumentInstance parentWindow] makeFirstResponder:listFilterField]; } else if([tables count] > 2) { - [tableWindow makeFirstResponder:tablesListView]; + [[tableDocumentInstance parentWindow] makeFirstResponder:tablesListView]; if([tablesListView numberOfSelectedRows] < 1) [tablesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO]; @@ -1940,7 +1940,7 @@ [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Couldn't delete '%@'.\nMySQL said: %@", @"message of panel when an item cannot be deleted"), [tables objectAtIndex:currentIndex], [mySQLConnection getLastErrorMessage]]]; [alert setAlertStyle:NSWarningAlertStyle]; if ([indexes indexLessThanIndex:currentIndex] == NSNotFound) { - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; currentIndex = NSNotFound; } else { NSInteger choice = [alert runModal]; @@ -1965,7 +1965,7 @@ [tablesListView reloadData]; // set window title - [tableWindow setTitle:[tableDocumentInstance displaySPName]]; + [tableDocumentInstance updateWindowTitle:self]; [tablesListView deselectAll:self]; @@ -2003,7 +2003,7 @@ // [[buttons objectAtIndex:0] setKeyEquivalent:@"t"]; // [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; // [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - [alert beginSheetModalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"truncateTableError"]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"truncateTableError"]; } // Get next index (beginning from the end) @@ -2097,7 +2097,7 @@ alertSheetOpened = YES; SPBeginAlertSheet(NSLocalizedString(@"Error adding new table", @"error adding new table message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, + NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, @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]]); @@ -2116,7 +2116,7 @@ NSString *tableType = @""; if ([[copyTableNameField stringValue] isEqualToString:@""]) { - 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")); + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, NSLocalizedString(@"Table must have a name.", @"message of panel when no name is given for table")); return; } @@ -2152,7 +2152,7 @@ if ( ![queryResult numOfRows] ) { //error while getting table structure - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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 { @@ -2198,7 +2198,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, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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; @@ -2211,7 +2211,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, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't duplicate '%@'.\nMySQL said: %@", @"message of panel when an item cannot be renamed"), [copyTableNameField stringValue], [mySQLConnection getLastErrorMessage]]); } @@ -2219,7 +2219,7 @@ if ([mySQLConnection queryErrored]) { //error while creating new table - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], 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,7 @@ NSLocalizedString(@"OK", @"OK button"), nil, nil, - tableWindow, + [tableDocumentInstance parentWindow], self, nil, nil, -- cgit v1.2.3