aboutsummaryrefslogtreecommitdiffstats
path: root/Source/TableContent.m
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2009-11-04 01:15:53 +0000
committerrowanbeentje <rowan@beent.je>2009-11-04 01:15:53 +0000
commit48d02b7080cadc507b1e7897c54ce2a8cf149acf (patch)
treef45eba9b97c71e1579580d790e9c9aa00088b26a /Source/TableContent.m
parent0d5acfadf8d43ab3889f50456f5dc7ff42bf1a12 (diff)
downloadsequelpro-48d02b7080cadc507b1e7897c54ce2a8cf149acf.tar.gz
sequelpro-48d02b7080cadc507b1e7897c54ce2a8cf149acf.tar.bz2
sequelpro-48d02b7080cadc507b1e7897c54ce2a8cf149acf.zip
- Add task support to all the main interface views
- Improve task support on previously supported views - Use a threaded task load for all initial table loads - Support threaded task loads for table content loads, reloads, sorts, and filters - Improve upon previous threaded task loads by minimising view updates and supporting updates of the existing data arrays where valid
Diffstat (limited to 'Source/TableContent.m')
-rw-r--r--Source/TableContent.m439
1 files changed, 260 insertions, 179 deletions
diff --git a/Source/TableContent.m b/Source/TableContent.m
index 610cfa86..ccc82e5e 100644
--- a/Source/TableContent.m
+++ b/Source/TableContent.m
@@ -59,8 +59,10 @@
if ((self == [super init])) {
tableValues = [[NSMutableArray alloc] init];
- tableValuesCount = 0;
+ tableRowsCount = 0;
+ previousTableRowsCount = 0;
dataColumns = [[NSMutableArray alloc] init];
+ tableColumnsCount = 0;
oldRow = [[NSMutableArray alloc] init];
selectedTable = nil;
@@ -125,11 +127,11 @@
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(startDocumentTaskForTab:)
name:SPDocumentTaskStartNotification
- object:nil];
+ object:tableDocumentInstance];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(endDocumentTaskForTab:)
name:SPDocumentTaskEndNotification
- object:nil];
+ object:tableDocumentInstance];
}
#pragma mark -
@@ -146,36 +148,50 @@
NSArray *columnNames;
NSDictionary *columnDefinition;
NSTableColumn *theCol;
+ BOOL enableInteraction = ![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:MAIN_TOOLBAR_TABLE_CONTENT] || ![tableDocumentInstance isWorking];
- // Clear the selection, and abort the reload if the user is still editing a row
- [tableContentView deselectAll:self];
+ // Abort the reload if the user is still editing a row
if ( isEditingRow )
return;
- // Store the newly selected table name
- selectedTable = aTable;
-
- // Reset table key store for use in argumentForRow:
- if (keys) [keys release], keys = nil;
+ // Check the supplied table name. If it matches the old one, a reload is being performed;
+ // reload the data in-place to maintain table state if possible.
+ if ([selectedTable isEqualToString:aTable]) {
+ previousTableRowsCount = tableRowsCount;
+
+ // Otherwise store the newly selected table name and reset the data
+ } else {
+ selectedTable = aTable;
+ previousTableRowsCount = 0;
- // Restore the table content view to the top left
- [tableContentView scrollRowToVisible:0];
- [tableContentView scrollColumnToVisible:0];
+ // Clear the selection
+ [tableContentView deselectAll:self];
- // Remove existing columns from the table
- while ([[tableContentView tableColumns] count]) {
- [tableContentView removeTableColumn:NSArrayObjectAtIndex([tableContentView tableColumns], 0)];
+ // Restore the table content view to the top left
+ [tableContentView scrollRowToVisible:0];
+ [tableContentView scrollColumnToVisible:0];
}
+ // Reset table key store for use in argumentForRow:
+ if (keys) [keys release], keys = nil;
+
// Reset data column store
+ tableColumnsCount = 0;
[dataColumns removeAllObjects];
// If no table has been supplied, reset the view to a blank table and disabled elements.
// [tableDataInstance tableEncoding] == nil indicates that an error occured while retrieving table data
if ( [[[tableDataInstance statusValues] objectForKey:@"Rows"] isNSNull] || [aTable isEqualToString:@""] || !aTable || [tableDataInstance tableEncoding] == nil)
{
+
+ // Remove existing columns from the table
+ while ([[tableContentView tableColumns] count]) {
+ [tableContentView removeTableColumn:NSArrayObjectAtIndex([tableContentView tableColumns], 0)];
+ }
+
// Empty the stored data arrays
- tableValuesCount = 0;
+ tableRowsCount = 0;
+ previousTableRowsCount = 0;
[tableValues removeAllObjects];
[tableContentView reloadData];
isFiltered = NO;
@@ -250,7 +266,15 @@
}
NSString *nullValue = [prefs objectForKey:SPNullValue];
-
+
+ // Lock drawing in the window
+ [tableWindow disableFlushWindow];
+
+ // Remove existing columns from the table
+ while ([[tableContentView tableColumns] count]) {
+ [tableContentView removeTableColumn:NSArrayObjectAtIndex([tableContentView tableColumns], 0)];
+ }
+
// Add the new columns to the table
for ( i = 0 ; i < [dataColumns count] ; i++ ) {
columnDefinition = NSArrayObjectAtIndex(dataColumns, i);
@@ -316,6 +340,7 @@
// Add the column to the table
[tableContentView addTableColumn:theCol];
+ tableColumnsCount++;
[theCol release];
}
@@ -341,6 +366,9 @@
isDesc = NO;
}
+ // Restore window drawing
+ [tableWindow enableFlushWindow];
+
// Store the current first responder so filter field doesn't steal focus
id currentFirstResponder = [tableWindow firstResponder];
@@ -355,7 +383,7 @@
[self setCompareTypes:self];
[argumentField setEnabled:YES];
[argumentField setStringValue:@""];
- [filterButton setEnabled:YES];
+ [filterButton setEnabled:enableInteraction];
// Restore preserved filter settings if appropriate and valid
if (filterFieldToRestore) {
@@ -401,19 +429,22 @@
}
// Set the state of the table buttons
- [addButton setEnabled:YES];
+ [addButton setEnabled:enableInteraction];
[copyButton setEnabled:NO];
[removeButton setEnabled:NO];
+ // Reset the table store if required - basically if the table is being changed
+ if (!previousTableRowsCount) {
+ tableRowsCount = 0;
+ [tableValues removeAllObjects];
+ }
+
// Trigger a data refresh
[self loadTableValues];
// Restore the view origin if appropriate
if (!NSEqualRects(selectionViewportToRestore, NSZeroRect)) {
- // Let the table know the size of the newly available data
- [tableContentView reloadData];
-
// Scroll the viewport to the saved location
selectionViewportToRestore.size = [tableContentView visibleRect].size;
[tableContentView scrollRectToVisible:selectionViewportToRestore];
@@ -424,8 +455,8 @@
[tableContentView selectRowIndexes:selectionIndexToRestore byExtendingSelection:NO];
}
- // Reload the table data display
- [tableContentView reloadData];
+ // Update display if necessary
+ [tableContentView displayIfNeeded];
// Init copyTable with necessary information for copying selected rows as SQL INSERT
[tableContentView setTableInstance:self withTableData:tableValues withColumns:dataColumns withTableName:selectedTable withConnection:mySQLConnection];
@@ -441,6 +472,7 @@
* Reload the table data without reconfiguring the tableView,
* using filters and limits as appropriate.
* Will not refresh the table view itself.
+ * Note that this does not empty the table array - see use of previousTableRowsCount.
*/
- (void) loadTableValues
{
@@ -456,11 +488,6 @@
[countText setStringValue:NSLocalizedString(@"Loading table data...", @"Loading table data string")];
- // Remove all items from the table
- tableValuesCount = 0;
- [tableContentView noteNumberOfRowsChanged];
- [tableValues removeAllObjects];
-
// Notify any listeners that a query has started
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self];
@@ -506,13 +533,14 @@
}
// Perform and process the query
+ [tableContentView performSelectorOnMainThread:@selector(noteNumberOfRowsChanged) withObject:nil waitUntilDone:YES];
[self setUsedQuery:queryString];
streamingResult = [mySQLConnection streamingQueryString:queryString];
[self processResultIntoDataStorage:streamingResult approximateRowCount:rowsToLoad];
[streamingResult release];
// If the result is empty, and a limit is active, reset the limit
- if ([prefs boolForKey:SPLimitResults] && queryStringBeforeLimit && !tableValuesCount) {
+ if ([prefs boolForKey:SPLimitResults] && queryStringBeforeLimit && !tableRowsCount) {
[limitRowsField setStringValue:@"1"];
queryString = [NSMutableString stringWithFormat:@"%@ LIMIT 0,%d", queryStringBeforeLimit, [prefs integerForKey:SPLimitResultsValue]];
[self setUsedQuery:queryString];
@@ -523,7 +551,7 @@
if ([prefs boolForKey:SPLimitResults]
&& ([limitRowsField intValue] > 1
- || tableValuesCount == [prefs integerForKey:SPLimitResultsValue]))
+ || tableRowsCount == [prefs integerForKey:SPLimitResultsValue]))
{
isLimited = YES;
} else {
@@ -540,6 +568,96 @@
[[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:self];
}
+/*
+ * Processes a supplied streaming result set, loading it into the data array.
+ */
+- (void)processResultIntoDataStorage:(MCPStreamingResult *)theResult approximateRowCount:(long)targetRowCount
+{
+ NSArray *tempRow;
+ NSMutableArray *newRow;
+ NSMutableArray *columnBlobStatuses = [[NSMutableArray alloc] init];
+ NSUInteger i;
+
+ float relativeTargetRowCount = 100.0/targetRowCount;
+ NSUInteger nextTableDisplayBoundary = 50;
+ BOOL tableViewRedrawn = NO;
+
+ long rowsProcessed = 0;
+
+ NSAutoreleasePool *dataLoadingPool;
+ NSProgressIndicator *dataLoadingIndicator = [tableDocumentInstance valueForKey:@"queryProgressBar"];
+ BOOL prefsLoadBlobsAsNeeded = [prefs boolForKey:SPLoadBlobsAsNeeded];
+
+ // Build up an array of which columns are blobs for faster iteration
+ for ( i = 0; i < tableColumnsCount ; i++ ) {
+ [columnBlobStatuses addObject:[NSNumber numberWithBool:[tableDataInstance columnIsBlobOrText:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"] ]]];
+ }
+
+ // Set up an autorelease pool for row processing
+ dataLoadingPool = [[NSAutoreleasePool alloc] init];
+
+ // Loop through the result rows as they become available
+ while (tempRow = [theResult fetchNextRowAsArray]) {
+
+ if (rowsProcessed < previousTableRowsCount) {
+ NSMutableArrayReplaceObject(tableValues, rowsProcessed, [NSMutableArray arrayWithArray:tempRow]);
+ } else {
+ NSMutableArrayAddObject(tableValues, [NSMutableArray arrayWithArray:tempRow]);
+ }
+
+ // Alter the values for hidden blob and text fields if appropriate
+ if ( prefsLoadBlobsAsNeeded ) {
+ newRow = NSArrayObjectAtIndex(tableValues, rowsProcessed);
+ for ( i = 0 ; i < tableColumnsCount ; i++ ) {
+ if ( [NSArrayObjectAtIndex(columnBlobStatuses, i) boolValue] ) {
+ NSMutableArrayReplaceObject(newRow, i, [SPNotLoaded notLoaded]);
+ }
+ }
+ }
+ rowsProcessed++;
+
+ // Update the task interface as necessary
+ if (!isFiltered) {
+ if (rowsProcessed < targetRowCount) {
+ [tableDocumentInstance setTaskPercentage:(rowsProcessed*relativeTargetRowCount)];
+ } else if (rowsProcessed == targetRowCount) {
+ [tableDocumentInstance performSelectorOnMainThread:@selector(setTaskProgressToIndeterminate) withObject:nil waitUntilDone:NO];
+ }
+ }
+
+ // Update the table view with new results every now and then
+ if (rowsProcessed > nextTableDisplayBoundary) {
+ if (rowsProcessed > tableRowsCount) tableRowsCount = rowsProcessed;
+ [tableContentView performSelectorOnMainThread:@selector(noteNumberOfRowsChanged) withObject:nil waitUntilDone:NO];
+ if (!tableViewRedrawn) {
+ [tableContentView performSelectorOnMainThread:@selector(displayIfNeeded) withObject:nil waitUntilDone:NO];
+ tableViewRedrawn = YES;
+ }
+ nextTableDisplayBoundary *= 2;
+ }
+
+ // Drain and reset the autorelease pool every ~1024 rows
+ if (!(rowsProcessed % 1024)) {
+ [dataLoadingPool drain];
+ dataLoadingPool = [[NSAutoreleasePool alloc] init];
+ }
+ }
+ tableRowsCount = rowsProcessed;
+
+ // If the reloaded table is shorter than the previous table, remove the extra values from the storage
+ if (tableRowsCount < [tableValues count]) {
+ [tableValues removeObjectsInRange:NSMakeRange(tableRowsCount, [tableValues count] - tableRowsCount)];
+ }
+
+ [tableContentView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
+
+ // Clean up the autorelease pool and reset the progress indicator
+ [dataLoadingPool drain];
+ [dataLoadingIndicator setIndeterminate:YES];
+
+ [columnBlobStatuses release];
+}
+
/**
* Returns the query string for the current filter settings,
* ready to be dropped into a WHERE clause, or nil if no filtering
@@ -703,25 +821,25 @@
// If no filter or limit is active, show just the count of rows in the table
if (!isFiltered && !isLimited) {
- if (tableValuesCount == 1)
- [countString appendFormat:NSLocalizedString(@"%d row in table", @"text showing a single row in the result"), tableValuesCount];
+ if (tableRowsCount == 1)
+ [countString appendFormat:NSLocalizedString(@"%d row in table", @"text showing a single row in the result"), tableRowsCount];
else
- [countString appendFormat:NSLocalizedString(@"%d rows in table", @"text showing how many rows are in the result"), tableValuesCount];
+ [countString appendFormat:NSLocalizedString(@"%d rows in table", @"text showing how many rows are in the result"), tableRowsCount];
// If a limit is active, display a string suggesting a limit is active
} else if (!isFiltered && isLimited) {
- [countString appendFormat:NSLocalizedString(@"Rows %d-%d of %@%d from table", @"text showing how many rows are in the limited result"), [limitRowsField intValue], [limitRowsField intValue]+tableValuesCount-1, maxNumRowsIsEstimate?@"~":@"", maxNumRows];
+ [countString appendFormat:NSLocalizedString(@"Rows %d-%d of %@%d from table", @"text showing how many rows are in the limited result"), [limitRowsField intValue], [limitRowsField intValue]+tableRowsCount-1, maxNumRowsIsEstimate?@"~":@"", maxNumRows];
// If just a filter is active, show a count and an indication a filter is active
} else if (isFiltered && !isLimited) {
- if (tableValuesCount == 1)
- [countString appendFormat:NSLocalizedString(@"%d row of %@%d matches filter", @"text showing how a single rows matched filter"), tableValuesCount, maxNumRowsIsEstimate?@"~":@"", maxNumRows];
+ if (tableRowsCount == 1)
+ [countString appendFormat:NSLocalizedString(@"%d row of %@%d matches filter", @"text showing how a single rows matched filter"), tableRowsCount, maxNumRowsIsEstimate?@"~":@"", maxNumRows];
else
- [countString appendFormat:NSLocalizedString(@"%d rows of %@%d match filter", @"text showing how many rows matched filter"), tableValuesCount, maxNumRowsIsEstimate?@"~":@"", maxNumRows];
+ [countString appendFormat:NSLocalizedString(@"%d rows of %@%d match filter", @"text showing how many rows matched filter"), tableRowsCount, maxNumRowsIsEstimate?@"~":@"", maxNumRows];
// If both a filter and limit is active, display full string
} else {
- [countString appendFormat:NSLocalizedString(@"Rows %d-%d from filtered matches", @"text showing how many rows are in the limited filter match"), [limitRowsField intValue], [limitRowsField intValue]+tableValuesCount-1];
+ [countString appendFormat:NSLocalizedString(@"Rows %d-%d from filtered matches", @"text showing how many rows are in the limited filter match"), [limitRowsField intValue], [limitRowsField intValue]+tableRowsCount-1];
}
// If rows are selected, append selection count
@@ -741,10 +859,18 @@
#pragma mark Table interface actions
/*
- * Reloads the current table data, performing a new SQL query. Now attempts to preserve sort order, filters, and viewport.
+ * Reloads the current table data, performing a new SQL query. Now attempts to preserve sort
+ * order, filters, and viewport. Performs the action in a new thread.
*/
- (IBAction)reloadTable:(id)sender
{
+ [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Reloading data...", @"Reloading data task description")];
+ [NSThread detachNewThreadSelector:@selector(reloadTableTask) toTarget:self withObject:nil];
+}
+- (void)reloadTableTask
+{
+ NSAutoreleasePool *reloadPool = [[NSAutoreleasePool alloc] init];
+
// Check whether a save of the current row is required.
if (![self saveRowOnDeselect]) return;
@@ -756,14 +882,24 @@
// Load the table's data
[self loadTable:selectedTable];
+
+ [tableDocumentInstance endTask];
+ [reloadPool drain];
}
/*
- * Filter the table with arguments given by the user
+ * Filter the table with arguments given by the user.
+ * Performs the action in a new thread.
*/
- (IBAction)filterTable:(id)sender
{
if ([tableDocumentInstance isWorking]) return;
+ [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Filtering table...", @"Filtering table task description")];
+ [NSThread detachNewThreadSelector:@selector(filterTableTask) toTarget:self withObject:nil];
+}
+- (void)filterTableTask
+{
+ NSAutoreleasePool *filterPool = [[NSAutoreleasePool alloc] init];
// Check whether a save of the current row is required.
if (![self saveRowOnDeselect]) return;
@@ -782,9 +918,15 @@
[limitRowsField setStringValue:[[NSNumber numberWithInt:(newLimit<1)?1:newLimit] stringValue]];
}
- // Reload data using the new filter settings
+ // Reset and reload data using the new filter settings
+ previousTableRowsCount = 0;
+ tableRowsCount = 0;
+ [tableValues removeAllObjects];
[self loadTableValues];
[tableContentView scrollPoint:NSMakePoint(0.0, 0.0)];
+
+ [tableDocumentInstance endTask];
+ [filterPool drain];
}
/**
@@ -876,7 +1018,7 @@
}
}
[tableValues addObject:newRow];
- tableValuesCount++;
+ tableRowsCount++;
[tableContentView reloadData];
[tableContentView selectRowIndexes:[NSIndexSet indexSetWithIndex:[tableContentView numberOfRows]-1] byExtendingSelection:NO];
@@ -911,7 +1053,7 @@
//copy row
tempRow = [NSMutableArray arrayWithArray:[tableValues objectAtIndex:[tableContentView selectedRow]]];
[tableValues insertObject:tempRow atIndex:[tableContentView selectedRow]+1];
- tableValuesCount++;
+ tableRowsCount++;
//if we don't show blobs, read data for this duplicate column from db
if ([prefs boolForKey:SPLoadBlobsAsNeeded]) {
@@ -1331,95 +1473,6 @@
}
/*
- * Processes a supplied streaming result set, loading it into the data array.
- */
-- (void)processResultIntoDataStorage:(MCPStreamingResult *)theResult approximateRowCount:(long)targetRowCount
-{
- NSArray *tempRow;
- NSMutableArray *newRow;
- NSMutableArray *columnBlobStatuses = [[NSMutableArray alloc] init];
- NSUInteger i;
-
- float relativeTargetRowCount = 100.0/targetRowCount;
- NSUInteger nextTableDisplayBoundary = 50;
- BOOL tableViewRedrawn = NO;
-
- long rowsProcessed = 0;
- long columnsCount = [dataColumns count];
-
- NSAutoreleasePool *dataLoadingPool;
- NSProgressIndicator *dataLoadingIndicator = [tableDocumentInstance valueForKey:@"queryProgressBar"];
- BOOL prefsLoadBlobsAsNeeded = [prefs boolForKey:SPLoadBlobsAsNeeded];
-
- // Build up an array of which columns are blobs for faster iteration
- for ( i = 0; i < columnsCount ; i++ ) {
- [columnBlobStatuses addObject:[NSNumber numberWithBool:[tableDataInstance columnIsBlobOrText:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"] ]]];
- }
-
- // Set up an autorelease pool for row processing
- dataLoadingPool = [[NSAutoreleasePool alloc] init];
-
- // Loop through the result rows as they become available
- while (tempRow = [theResult fetchNextRowAsArray]) {
-
- // Add values for hidden blob and text fields if appropriate
- if ( prefsLoadBlobsAsNeeded ) {
- NSMutableArrayAddObject(tableValues, [NSMutableArray arrayWithCapacity:columnsCount]);
- tableValuesCount++;
- newRow = NSArrayObjectAtIndex(tableValues, rowsProcessed);
- for ( i = 0 ; i < columnsCount ; i++ ) {
- if ( [NSArrayObjectAtIndex(columnBlobStatuses, i) boolValue] ) {
- [newRow addObject:[SPNotLoaded notLoaded]];
- } else {
- NSMutableArrayAddObject(newRow, NSArrayObjectAtIndex(tempRow, i));
- }
- }
-
- // Otherwise just add the new row
- } else {
- NSMutableArrayAddObject(tableValues, [NSMutableArray arrayWithArray:tempRow]);
- tableValuesCount++;
- }
-
- // Update the task interface as necessary
- rowsProcessed++;
- /*if (!isFiltered) {
- if (rowsProcessed < targetRowCount) {
- [tableDocumentInstance setTaskPercentage:(rowsProcessed*relativeTargetRowCount)];
- } else if (rowsProcessed == targetRowCount) {
- [tableDocumentInstance performSelectorOnMainThread:@selector(setTaskProgressToIndeterminate) withObject:nil waitUntilDone:NO];
- }
- }*/
-
- // Update the table view with new results every now and then
- if (rowsProcessed > nextTableDisplayBoundary) {
- [tableContentView performSelectorOnMainThread:@selector(noteNumberOfRowsChanged) withObject:nil waitUntilDone:NO];
- if (!tableViewRedrawn) {
- [tableContentView performSelectorOnMainThread:@selector(displayIfNeeded) withObject:nil waitUntilDone:NO];
- tableViewRedrawn = YES;
- }
- nextTableDisplayBoundary *= 2;
- }
-
- // Drain and reset the autorelease pool every ~1024 rows
- if (!(rowsProcessed % 1024)) {
- [dataLoadingPool drain];
- dataLoadingPool = [[NSAutoreleasePool alloc] init];
- }
- }
-
- [tableContentView performSelectorOnMainThread:@selector(noteNumberOfRowsChanged) withObject:nil waitUntilDone:NO];
- [tableContentView setNeedsDisplay:YES];
-
- // Clean up the autorelease pool and reset the progress indicator
- [dataLoadingPool drain];
- [dataLoadingIndicator setIndeterminate:YES];
-
- [columnBlobStatuses release];
-}
-
-
-/*
* Tries to write a new row to the database.
* Returns YES if row is written to database, otherwise NO; also returns YES if no row
* is being edited and nothing has to be written to the database.
@@ -1548,6 +1601,7 @@
if ( isEditingNewRow ) {
if ( [prefs boolForKey:SPReloadAfterAddingRow] ) {
[tableWindow endEditingFor:nil];
+ previousTableRowsCount = tableRowsCount;
[self loadTableValues];
} else {
@@ -1566,6 +1620,7 @@
// Reload table if set to - otherwise no action required.
if ( [prefs boolForKey:SPReloadAfterEditingRow] ) {
[tableWindow endEditingFor:nil];
+ previousTableRowsCount = tableRowsCount;
[self loadTableValues];
}
}
@@ -1774,7 +1829,7 @@
withObject:[NSMutableArray arrayWithArray:oldRow]];
isEditingRow = NO;
} else {
- tableValuesCount--;
+ tableRowsCount--;
[tableValues removeObjectAtIndex:[tableContentView selectedRow]];
isEditingRow = NO;
isEditingNewRow = NO;
@@ -1989,13 +2044,14 @@
// Refresh table content
if ( errors || reloadAfterRemovingRow ) {
+ previousTableRowsCount = tableRowsCount;
[self loadTableValues];
} else {
- for ( i = 0 ; i < tableValuesCount ; i++ ) {
+ for ( i = 0 ; i < tableRowsCount ; i++ ) {
if ( ![selectedRows containsIndex:i] )
[tempResult addObject:NSArrayObjectAtIndex(tableValues, i)];
}
- tableValuesCount = [tempResult count];
+ tableRowsCount = [tempResult count];
[tableValues setArray:tempResult];
[tableContentView reloadData];
}
@@ -2202,7 +2258,7 @@
// For unfiltered and non-limited tables, use the result count - and update the status count
if (!isLimited && !isFiltered) {
- maxNumRows = tableValuesCount;
+ maxNumRows = tableRowsCount;
maxNumRowsIsEstimate = NO;
[tableDataInstance setStatusValue:[NSString stringWithFormat:@"%d", maxNumRows] forKey:@"Rows"];
[tableDataInstance setStatusValue:@"y" forKey:@"RowsCountAccurate"];
@@ -2235,7 +2291,7 @@
if (checkStatusCount) {
NSInteger foundMaxRows;
if ([prefs boolForKey:SPLimitResults]) {
- foundMaxRows = [limitRowsField intValue] - 1 + tableValuesCount;
+ foundMaxRows = [limitRowsField intValue] - 1 + tableRowsCount;
if (foundMaxRows > maxNumRows) {
if (foundMaxRows == [limitRowsField intValue] - 1 + [prefs integerForKey:SPLimitResultsValue]) {
maxNumRows = foundMaxRows + 1;
@@ -2245,8 +2301,8 @@
maxNumRowsIsEstimate = NO;
}
}
- } else if (tableValuesCount > maxNumRows) {
- maxNumRows = tableValuesCount;
+ } else if (tableRowsCount > maxNumRows) {
+ maxNumRows = tableRowsCount;
maxNumRowsIsEstimate = YES;
}
[tableDataInstance setStatusValue:[NSString stringWithFormat:@"%d", maxNumRows] forKey:@"Rows"];
@@ -2310,21 +2366,26 @@
- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
- return tableValuesCount;
+ return tableRowsCount;
}
- (id)tableView:(CMCopyTable *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
- if (rowIndex >= tableValuesCount) return nil;
- id theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(tableValues, rowIndex), [[aTableColumn identifier] intValue]);
+ // In some loading situations, where the table is being redrawn while a load operation is in process on a background
+ // thread, an index higher than the available rows/columns may be requested. Return "..." to indicate loading in these
+ // cases - when the load completes all table data will be redrawn.
+ int columnIndex = [[aTableColumn identifier] intValue];
+ if (rowIndex >= tableRowsCount || columnIndex >= tableColumnsCount) return @"...";
- if ([theValue isKindOfClass:[NSData class]])
- return [theValue shortStringRepresentationUsingEncoding:[mySQLConnection encoding]];
+ id theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(tableValues, rowIndex), columnIndex);
if ([theValue isNSNull])
return [prefs objectForKey:SPNullValue];
+ if ([theValue isKindOfClass:[NSData class]])
+ return [theValue shortStringRepresentationUsingEncoding:[mySQLConnection encoding]];
+
if ([theValue isSPNotLoaded])
return NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields");
@@ -2334,21 +2395,32 @@
/**
* This function changes the text color of text/blob fields which are null or not yet loaded to gray
*/
-- (void)tableView:(CMCopyTable *)aTableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)aTableColumn row:(int)row
+- (void)tableView:(CMCopyTable *)aTableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)aTableColumn row:(int)rowIndex
{
-
- if (row >= tableValuesCount) return;
if (![cell respondsToSelector:@selector(setTextColor:)]) return;
+ // In some loading situations, where the table is being redrawn while a load operation is in process on a background
+ // thread, an index higher than the available rows/columns may be requested. Return gray to indicate loading in these
+ // cases - when the load completes all table data will be redrawn.
+ int columnIndex = [[aTableColumn identifier] intValue];
+ if (rowIndex >= tableRowsCount || columnIndex >= tableColumnsCount) {
+ [cell setTextColor:[NSColor lightGrayColor]];
+ return;
+ }
+
+ id theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(tableValues, rowIndex), columnIndex);
+ if (!theValue) {
+ [cell setTextColor:[NSColor lightGrayColor]];
+ return;
+ }
+
// If user wants to edit 'cell' set text color to black and return to avoid
// writing in gray if value was NULL
- if ( [aTableView editedColumn] == [[aTableColumn identifier] intValue] && [aTableView editedRow] == row) {
+ if ( [aTableView editedColumn] == [[aTableColumn identifier] intValue] && [aTableView editedRow] == rowIndex) {
[cell setTextColor:[NSColor blackColor]];
return;
}
- id theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(tableValues, row), [[aTableColumn identifier] intValue]);
-
// For null cells and not loaded cells, display the contents in gray.
if ([theValue isNSNull] || [theValue isSPNotLoaded]) {
[cell setTextColor:[NSColor lightGrayColor]];
@@ -2386,29 +2458,37 @@
#pragma mark -
#pragma mark TableView delegate methods
-- (void)tableView:(NSTableView*)tableView didClickTableColumn:(NSTableColumn *)tableColumn
-/*
- sorts the tableView by the clicked column
- if clicked twice, order is descending
+/**
+ * Sorts the tableView by the clicked column.
+ * If clicked twice, order is altered to descending.
+ * Performs the task in a new thread.
*/
+- (void)tableView:(NSTableView*)tableView didClickTableColumn:(NSTableColumn *)tableColumn
{
if ( [selectedTable isEqualToString:@""] || !selectedTable )
return;
- // Check whether a save of the current row is required.
- if ( ![self saveRowOnDeselect] ) return;
-
// Prevent sorting while the table is still loading
if ([tableDocumentInstance isWorking]) return;
-
- //sets order descending if a header is clicked twice
- if ( [[tableColumn identifier] isEqualTo:sortCol] ) {
- if ( isDesc ) {
- isDesc = NO;
- } else {
- isDesc = YES;
- }
+
+ // Start the task
+ [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Sorting table...", @"Sorting table task description")];
+ [NSThread detachNewThreadSelector:@selector(sortTableTaskWithColumn:) toTarget:self withObject:tableColumn];
+}
+- (void)sortTableTaskWithColumn:(NSTableColumn *)tableColumn
+{
+ NSAutoreleasePool *sortPool = [[NSAutoreleasePool alloc] init];
+
+ // Check whether a save of the current row is required.
+ if (![self saveRowOnDeselect]) {
+ [sortPool drain];
+ return;
+ }
+
+ // Sets order descending if a header is clicked twice
+ if ([[tableColumn identifier] isEqualTo:sortCol]) {
+ isDesc = !isDesc;
} else {
isDesc = NO;
[tableContentView setIndicatorImage:nil inTableColumn:[tableContentView tableColumnWithIdentifier:sortCol]];
@@ -2417,21 +2497,26 @@
sortCol = [[NSNumber alloc] initWithInt:[[tableColumn identifier] intValue]];
// Update data using the new sort order
+ previousTableRowsCount = tableRowsCount;
[self loadTableValues];
if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) {
NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil,
[NSString stringWithFormat:NSLocalizedString(@"Couldn't sort table. MySQL said: %@", @"message of panel when sorting of table failed"), [mySQLConnection getLastErrorMessage]]);
+ [sortPool drain];
return;
}
-
- //sets highlight and indicatorImage
+
+ // Set the highlight and indicatorImage
[tableContentView setHighlightedTableColumn:tableColumn];
- if ( isDesc ) {
+ if (isDesc) {
[tableContentView setIndicatorImage:[NSImage imageNamed:@"NSDescendingSortIndicator"] inTableColumn:tableColumn];
} else {
[tableContentView setIndicatorImage:[NSImage imageNamed:@"NSAscendingSortIndicator"] inTableColumn:tableColumn];
}
+
+ [tableDocumentInstance endTask];
+ [sortPool drain];
}
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
@@ -2635,10 +2720,8 @@
- (void) startDocumentTaskForTab:(NSNotification *)aNotification
{
- // Only proceed if the current document is the notifying document, and only if
- // this view is selected.
- if ([aNotification object] != tableDocumentInstance
- || ![[[aNotification object] selectedToolbarItemIdentifier] isEqualToString:MAIN_TOOLBAR_TABLE_CONTENT])
+ // Only proceed if this view is selected.
+ if (![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:MAIN_TOOLBAR_TABLE_CONTENT])
return;
[addButton setEnabled:NO];
@@ -2654,10 +2737,8 @@
- (void) endDocumentTaskForTab:(NSNotification *)aNotification
{
- // Only proceed if the current document is the notifying document, and only if
- // this view is selected.
- if ([aNotification object] != tableDocumentInstance
- || ![[[aNotification object] selectedToolbarItemIdentifier] isEqualToString:MAIN_TOOLBAR_TABLE_CONTENT])
+ // Only proceed if this view is selected.
+ if (![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:MAIN_TOOLBAR_TABLE_CONTENT])
return;
if ( ![[[tableDataInstance statusValues] objectForKey:@"Rows"] isNSNull] && selectedTable && [selectedTable length] && [tableDataInstance tableEncoding]) [addButton setEnabled:YES];
@@ -2729,7 +2810,7 @@
} else if ( isEditingNewRow ) {
isEditingRow = NO;
isEditingNewRow = NO;
- tableValuesCount--;
+ tableRowsCount--;
[tableValues removeObjectAtIndex:row];
[tableContentView reloadData];
}