From 1ddc896d2f88bea86e4b0457d63f7a53b846851d Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Wed, 24 Mar 2010 02:30:55 +0000 Subject: - Add a new selectDatabase:item: method to TableDocument, to centralise code for selecting databases and tables. Clean up database selection to use this new method, and avoid reloads when re-selecting the same database. - Convert the history controller to use this new call. This fixes history behaviour when a table list filter was active. - Convert the (hidden) navigator to use this new call. This fixes timing issues. - Make some thread safety tweaks to TablesList. - Change TablesList selectTableOrViewWithName: to selectItemWithName:, including procs etc. --- Source/SPHistoryController.m | 41 ++++-------- Source/SPNavigatorController.m | 12 +--- Source/TableContent.m | 2 +- Source/TableDocument.h | 2 +- Source/TableDocument.m | 144 +++++++++++++++++++++++++++-------------- Source/TablesList.h | 2 +- Source/TablesList.m | 21 +++--- 7 files changed, 128 insertions(+), 96 deletions(-) diff --git a/Source/SPHistoryController.m b/Source/SPHistoryController.m index 991c6e1e..c1c6b386 100644 --- a/Source/SPHistoryController.m +++ b/Source/SPHistoryController.m @@ -325,35 +325,22 @@ return; } - // Check and set the database - if (![[theDocument database] isEqualToString:[historyEntry objectForKey:@"database"]]) { - NSPopUpButton *chooseDatabaseButton = [theDocument valueForKey:@"chooseDatabaseButton"]; - [tablesListInstance setTableListSelectability:YES]; - [[tablesListInstance valueForKey:@"tablesListView"] deselectAll:self]; - [theDocument setDatabaseListIsSelectable:YES]; - [tablesListInstance setTableListSelectability:YES]; - [chooseDatabaseButton selectItemWithTitle:[historyEntry objectForKey:@"database"]]; - [theDocument chooseDatabase:self]; - if (![[theDocument database] isEqualToString:[historyEntry objectForKey:@"database"]]) { - return [self abortEntryLoadWithPool:loadPool]; - } + // If the same table was selected, mark the content as requiring a reload + if ([historyEntry objectForKey:@"table"] && [[theDocument table] isEqualToString:[historyEntry objectForKey:@"table"]]) { + [tablesListInstance setContentRequiresReload:YES]; } - // Check and set the table - if ([historyEntry objectForKey:@"table"] && ![[theDocument table] isEqualToString:[historyEntry objectForKey:@"table"]]) { - NSArray *tables = [tablesListInstance tables]; - if ([tables indexOfObject:[historyEntry objectForKey:@"table"]] == NSNotFound) { - return [self abortEntryLoadWithPool:loadPool]; - } - [[tablesListInstance valueForKey:@"tablesListView"] selectRowIndexes:[NSIndexSet indexSetWithIndex:[tables indexOfObject:[historyEntry objectForKey:@"table"]]] byExtendingSelection:NO]; - if (![[theDocument table] isEqualToString:[historyEntry objectForKey:@"table"]]) { - return [self abortEntryLoadWithPool:loadPool]; - } - } else if (![historyEntry objectForKey:@"table"] && [theDocument table]) { - [tablesListInstance setTableListSelectability:YES]; - [[tablesListInstance valueForKey:@"tablesListView"] deselectAll:self]; - } else { - [tablesListInstance setContentRequiresReload:YES]; + // Update the database and table name if necessary + [theDocument selectDatabase:[historyEntry objectForKey:@"database"] item:[historyEntry objectForKey:@"table"]]; + + // If the database or table couldn't be selected, error. + if ((![[theDocument database] isEqualToString:[historyEntry objectForKey:@"database"]] + && ([theDocument database] || [historyEntry objectForKey:@"database"])) + || + (![[theDocument table] isEqualToString:[historyEntry objectForKey:@"table"]] + && ([theDocument table] || [historyEntry objectForKey:@"table"]))) + { + return [self abortEntryLoadWithPool:loadPool]; } // Check and set the view diff --git a/Source/SPNavigatorController.m b/Source/SPNavigatorController.m index f0d8961a..757797ee 100644 --- a/Source/SPNavigatorController.m +++ b/Source/SPNavigatorController.m @@ -666,16 +666,8 @@ static SPNavigatorController *sharedNavigatorController = nil; } if([[doc connectionID] isEqualToString:[pathArray objectAtIndex:0]]) { - // select db - if(![[doc database] isEqualToString:[pathArray objectAtIndex:1]]) { - if([[doc valueForKeyPath:@"mySQLConnection"] isConnected]) { - [[doc valueForKeyPath:@"chooseDatabaseButton"] selectItemWithTitle:[pathArray objectAtIndex:1]]; - [doc chooseDatabase:self]; - } - } - // select table/view if given - if([pathArray count] > 2) - [[doc valueForKeyPath:@"tablesListInstance"] performSelector:@selector(selectTableOrViewWithName:) withObject:[pathArray objectAtIndex:2] afterDelay:0.1]; + // Select the database and table + [doc selectDatabase:[pathArray objectAtIndex:1] item:([pathArray count] > 2)?[pathArray objectAtIndex:2]:nil]; } else { [SPTooltip showWithObject:NSLocalizedString(@"The connection of the active connection window is not identical.", @"the connection of the active connection window is not identical tooltip") diff --git a/Source/TableContent.m b/Source/TableContent.m index 42f65d7f..51d9cbe3 100644 --- a/Source/TableContent.m +++ b/Source/TableContent.m @@ -1596,7 +1596,7 @@ [self setFiltersToRestore:filterSettings]; // Attempt to switch to the target table - if (![tablesListInstance selectTableOrViewWithName:[refDictionary objectForKey:@"table"]]) { + if (![tablesListInstance selectItemWithName:[refDictionary objectForKey:@"table"]]) { NSBeep(); [self setFiltersToRestore:nil]; } diff --git a/Source/TableDocument.h b/Source/TableDocument.h index c1dd92a6..5178ef56 100644 --- a/Source/TableDocument.h +++ b/Source/TableDocument.h @@ -180,7 +180,7 @@ // Database methods - (IBAction)setDatabases:(id)sender; - (IBAction)chooseDatabase:(id)sender; -- (void)chooseDatabaseTask; +- (void)selectDatabase:(NSString *)aDatabase item:(NSString *)anItem; - (IBAction)addDatabase:(id)sender; - (IBAction)removeDatabase:(id)sender; - (IBAction)showMySQLHelp:(id)sender; diff --git a/Source/TableDocument.m b/Source/TableDocument.m index ee793689..06f5f744 100644 --- a/Source/TableDocument.m +++ b/Source/TableDocument.m @@ -60,6 +60,7 @@ - (void)_addDatabase; - (void)_removeDatabase; +- (void)_selectDatabaseAndItem:(NSDictionary *)selectionDetails; @end @@ -856,6 +857,16 @@ // Lock editability again if performing a task if (_isWorkingLevel) databaseListIsSelectable = NO; + // Select the database + [self selectDatabase:[chooseDatabaseButton titleOfSelectedItem] item:[self table]]; +} + +/** + * Select the specified database and, optionally, table. + */ +- (void)selectDatabase:(NSString *)aDatabase item:(NSString *)anItem +{ + // Do not update the navigator since nothing is changed [[SPNavigatorController sharedNavigatorController] setIgnoreUpdate:YES]; @@ -872,57 +883,16 @@ // Start a task [self startTaskWithDescription:[NSString stringWithFormat:NSLocalizedString(@"Loading database '%@'...", @"Loading database task string"), [chooseDatabaseButton titleOfSelectedItem]]]; + NSDictionary *selectionDetails = [NSDictionary dictionaryWithObjectsAndKeys: + aDatabase, @"database", + anItem, @"item", + nil]; if ([NSThread isMainThread]) { - [NSThread detachNewThreadSelector:@selector(chooseDatabaseTask) toTarget:self withObject:nil]; + [NSThread detachNewThreadSelector:@selector(_selectDatabaseAndItem:) toTarget:self withObject:selectionDetails]; } else { - [self chooseDatabaseTask]; - } -} -- (void)chooseDatabaseTask -{ - NSAutoreleasePool *taskPool = [[NSAutoreleasePool alloc] init]; - - // Save existing scroll position and details, and ensure no duplicate entries are created as table list changes - BOOL historyStateChanging = [spHistoryControllerInstance modifyingState]; - if (!historyStateChanging) { - [spHistoryControllerInstance updateHistoryEntries]; - [spHistoryControllerInstance setModifyingState:YES]; + [self _selectDatabaseAndItem:selectionDetails]; } - // show error on connection failed - if ( ![mySQLConnection selectDB:[chooseDatabaseButton titleOfSelectedItem]] ) { - if ( [mySQLConnection isConnected] ) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to database %@.\nBe sure that you have the necessary privileges.", @"message of panel when connection to db failed after selecting from popupbutton"), [chooseDatabaseButton titleOfSelectedItem]]); - [self setDatabases:self]; - } - [self endTask]; - [taskPool drain]; - return; - } - - //setConnection of TablesList and TablesDump to reload tables in db - if (selectedDatabase) [selectedDatabase release], selectedDatabase = nil; - selectedDatabase = [[NSString alloc] initWithString:[chooseDatabaseButton titleOfSelectedItem]]; - [tablesListInstance setConnection:mySQLConnection]; - [tableDumpInstance setConnection:mySQLConnection]; - - [[tableWindow onMainThread] setTitle:[self displaySPName]]; - - // Add a history entry - if (!historyStateChanging) { - [spHistoryControllerInstance setModifyingState:NO]; - [spHistoryControllerInstance updateHistoryEntries]; - } - - // 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]; - else - [[tableWindow onMainThread] makeFirstResponder:[tablesListInstance valueForKeyPath:@"tablesListView"]]; - - [self endTask]; - [taskPool drain]; } /** @@ -4130,4 +4100,84 @@ [tableWindow setTitle:[self displaySPName]]; } +/** + * Select the specified database and, optionally, table. + */ +- (void)_selectDatabaseAndItem:(NSDictionary *)selectionDetails +{ + NSAutoreleasePool *taskPool = [[NSAutoreleasePool alloc] init]; + NSString *targetDatabaseName = [selectionDetails objectForKey:@"database"]; + NSString *targetItemName = [selectionDetails objectForKey:@"item"]; + + // Save existing scroll position and details, and ensure no duplicate entries are created as table list changes + BOOL historyStateChanging = [spHistoryControllerInstance modifyingState]; + if (!historyStateChanging) { + [spHistoryControllerInstance updateHistoryEntries]; + [spHistoryControllerInstance setModifyingState:YES]; + } + + if (![targetDatabaseName isEqualToString:selectedDatabase]) { + + // Attempt to select the specified database, and abort on failure + if ([chooseDatabaseButton indexOfItemWithTitle:targetDatabaseName] == NSNotFound + || ![mySQLConnection selectDB:targetDatabaseName]) + { + if ( [mySQLConnection isConnected] ) { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to database %@.\nBe sure that you have the necessary privileges.", @"message of panel when connection to db failed after selecting from popupbutton"), targetDatabaseName]); + + // Update the database list + [self setDatabases:self]; + } + + [self endTask]; + [taskPool drain]; + return; + } + + [[chooseDatabaseButton onMainThread] selectItemWithTitle:targetDatabaseName]; + if (selectedDatabase) [selectedDatabase release], selectedDatabase = nil; + selectedDatabase = [[NSString alloc] initWithString:[chooseDatabaseButton titleOfSelectedItem]]; + + // If the item has changed, clear the item selection for cleaner loading + if (![targetItemName isEqualToString:[self table]]) { + [[tablesListInstance onMainThread] setTableListSelectability:YES]; + [[[tablesListInstance valueForKey:@"tablesListView"] onMainThread] deselectAll:self]; + [[tablesListInstance onMainThread] setTableListSelectability:NO]; + } + + // Set the connection of TablesList and TablesDump to reload tables in db + [tablesListInstance setConnection:mySQLConnection]; + [tableDumpInstance setConnection:mySQLConnection]; + + // Update the window title + [[tableWindow onMainThread] setTitle:[self displaySPName]]; + + // Add a history entry + if (!historyStateChanging) { + [spHistoryControllerInstance setModifyingState:NO]; + [spHistoryControllerInstance updateHistoryEntries]; + } + + // 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]; + else + [[tableWindow onMainThread] makeFirstResponder:[tablesListInstance valueForKeyPath:@"tablesListView"]]; + } + + // If a the table has changed, update the selection + if (![targetItemName isEqualToString:[self table]]) { + if (targetItemName) { + [tablesListInstance selectItemWithName:targetItemName]; + } else { + [[tablesListInstance onMainThread] setTableListSelectability:YES]; + [[[tablesListInstance valueForKey:@"tablesListView"] onMainThread] deselectAll:self]; + [[tablesListInstance onMainThread] setTableListSelectability:NO]; + } + } + + [self endTask]; + [taskPool drain]; +} @end diff --git a/Source/TablesList.h b/Source/TablesList.h index acdd28c7..509ac96f 100644 --- a/Source/TablesList.h +++ b/Source/TablesList.h @@ -148,7 +148,7 @@ enum sp_table_types // Setters - (void)setContentRequiresReload:(BOOL)reload; - (void)setStatusRequiresReload:(BOOL)reload; -- (BOOL)selectTableOrViewWithName:(NSString *)theName; +- (BOOL)selectItemWithName:(NSString *)theName; // Tabview delegate methods - (void)loadTabTask:(NSTabViewItem *)tabViewItem; diff --git a/Source/TablesList.m b/Source/TablesList.m index 03454e80..67fb8f04 100644 --- a/Source/TablesList.m +++ b/Source/TablesList.m @@ -40,6 +40,7 @@ #import "SPConstants.h" #import "SPAlertSheets.h" #import "SPNavigatorController.h" +#import "SPMainThreadTrampoline.h" @interface TablesList (PrivateAPI) @@ -80,7 +81,7 @@ tableListContainsViews = NO; tableListIsSelectable = YES; - [tablesListView deselectAll:self]; + [[tablesListView onMainThread] deselectAll:self]; tableListIsSelectable = previousTableListIsSelectable; [tables removeAllObjects]; [tableTypes removeAllObjects]; @@ -220,7 +221,7 @@ } [tableTypes insertObject:[NSNumber numberWithInteger:SP_TABLETYPE_NONE] atIndex:0]; - [tablesListView reloadData]; + [[tablesListView onMainThread] reloadData]; // if the previous selected table still exists, select it // but not if the update was called from SPTableData since it calls that method @@ -229,7 +230,7 @@ if( ![sender isKindOfClass:[SPTableData class]] && previousSelectedTable != nil && [tables indexOfObject:previousSelectedTable] < [tables count]) { NSInteger itemToReselect = [tables indexOfObject:previousSelectedTable]; tableListIsSelectable = YES; - [tablesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:itemToReselect] byExtendingSelection:NO]; + [[tablesListView onMainThread] selectRowIndexes:[NSIndexSet indexSetWithIndex:itemToReselect] byExtendingSelection:NO]; tableListIsSelectable = previousTableListIsSelectable; if (selectedTableName) [selectedTableName release]; selectedTableName = [[NSString alloc] initWithString:[tables objectAtIndex:itemToReselect]]; @@ -241,13 +242,13 @@ } // Determine whether or not to show the list filter based on the number of tables, and clear it - [self clearFilter]; + [[self onMainThread] clearFilter]; if ([tables count] > 20) [self showFilter]; else [self hideFilter]; // Set the filter placeholder text if ([tableDocumentInstance database]) { - [[listFilterField cell] setPlaceholderString:NSLocalizedString(@"Filter", @"Filter placeholder")]; + [[[listFilterField cell] onMainThread] setPlaceholderString:NSLocalizedString(@"Filter", @"Filter placeholder")]; } if (previousSelectedTable) [previousSelectedTable release]; @@ -1191,10 +1192,10 @@ } /** - * Select a table or view using the provided name; returns YES if the + * Select an item using the provided name; returns YES if the * supplied name could be selected, or NO if not. */ -- (BOOL)selectTableOrViewWithName:(NSString *)theName +- (BOOL)selectItemWithName:(NSString *)theName { NSInteger i, tableType; NSInteger itemIndex = NSNotFound; @@ -1203,7 +1204,7 @@ // Loop through the unfiltered tables/views to find the desired item for (i = 0; i < [tables count]; i++) { tableType = [[tableTypes objectAtIndex:i] integerValue]; - if (tableType != SP_TABLETYPE_TABLE && tableType != SP_TABLETYPE_VIEW) continue; + if (tableType == SP_TABLETYPE_NONE) continue; if ([[tables objectAtIndex:i] isEqualToString:theName]) { itemIndex = i; break; @@ -1234,7 +1235,9 @@ [self updateSelectionWithTaskString:[NSString stringWithFormat:NSLocalizedString(@"Loading %@...", @"Loading table task string"), theName]]; } } - [tablesListView scrollRowToVisible:[tablesListView selectedRow]]; + + [[tablesListView onMainThread] scrollRowToVisible:[tablesListView selectedRow]]; + return YES; } -- cgit v1.2.3