aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2010-03-20 22:07:49 +0000
committerrowanbeentje <rowan@beent.je>2010-03-20 22:07:49 +0000
commita4ee82dd8446b3374adac9f826118ab6c8a108a9 (patch)
tree26fdb41583773afd3dc1dcc10c6fd26e3c190129
parent57d35fbecd2517fe8b1914d1f2335be06e2a8b17 (diff)
downloadsequelpro-a4ee82dd8446b3374adac9f826118ab6c8a108a9.tar.gz
sequelpro-a4ee82dd8446b3374adac9f826118ab6c8a108a9.tar.bz2
sequelpro-a4ee82dd8446b3374adac9f826118ab6c8a108a9.zip
- Add a new SPMainThreadTrampoline NSObject category, allowing all NSObjects to easily proxy commands onto the main thread with any number and type of arguments.
- Use the new trampoline and other thread safety tweaks to hopefully address a number of what appear to be threading issues: http://log.sequelpro.com/view/20 http://log.sequelpro.com/view/32 http://log.sequelpro.com/view/41 http://log.sequelpro.com/view/42 http://log.sequelpro.com/view/55 http://log.sequelpro.com/view/64 http://log.sequelpro.com/view/65 http://log.sequelpro.com/view/66
-rw-r--r--Source/SPAlertSheets.m4
-rw-r--r--Source/SPMainThreadTrampoline.h54
-rw-r--r--Source/SPMainThreadTrampoline.m153
-rw-r--r--Source/TableContent.m31
-rw-r--r--Source/TableDump.m258
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj6
6 files changed, 355 insertions, 151 deletions
diff --git a/Source/SPAlertSheets.m b/Source/SPAlertSheets.m
index 2505fb61..9692e848 100644
--- a/Source/SPAlertSheets.m
+++ b/Source/SPAlertSheets.m
@@ -22,6 +22,8 @@
//
// More info at <http://code.google.com/p/sequel-pro/>
+#import "SPMainThreadTrampoline.h"
+
/**
* Provide a very simple alias of NSBeginAlertSheet with one difference:
* printf-type format strings are no longer supported within the "msg"
@@ -52,5 +54,5 @@ void SPBeginAlertSheet(
contextInfo,
[msg stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]
);
- [docWindow makeKeyWindow];
+ [[docWindow onMainThread] makeKeyWindow];
}
diff --git a/Source/SPMainThreadTrampoline.h b/Source/SPMainThreadTrampoline.h
new file mode 100644
index 00000000..9cd4030a
--- /dev/null
+++ b/Source/SPMainThreadTrampoline.h
@@ -0,0 +1,54 @@
+//
+// $Id$
+//
+// SPMainThreadTrampoline.h
+// sequel-pro
+//
+// Created by Rowan Beentje on 20/03/2010.
+// Copyright 2010 Rowan Beentje. All rights reserved.
+//
+// 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 <http://code.google.com/p/sequel-pro/>
+//
+
+#import <Cocoa/Cocoa.h>
+
+/**
+ * Set up the categories, available on all NSObjects.
+ */
+@interface NSObject (SPMainThreadTrampoline)
+
+- (id) onMainThread;
+- (id) retainedOnMainThread;
+
+@end
+
+
+/**
+ * Set up the trampoline class.
+ * This is created automatically by using the onMainThread category; all messages
+ * sent to this object are bounced to the initial object on the main thread.
+ * Note that base NSObject messages like retain or release apply to the trampoline.
+ */
+
+@interface SPMainThreadTrampoline : NSObject
+{
+ IBOutlet id trampolineObject;
+}
+
+- (id) initWithObject:(id)theObject;
+
+@end
diff --git a/Source/SPMainThreadTrampoline.m b/Source/SPMainThreadTrampoline.m
new file mode 100644
index 00000000..381e42ee
--- /dev/null
+++ b/Source/SPMainThreadTrampoline.m
@@ -0,0 +1,153 @@
+//
+// $Id$
+//
+// SPMainThreadTrampoline.m
+// sequel-pro
+//
+// Created by Rowan Beentje on 20/03/2010.
+// Copyright 2010 Rowan Beentje. All rights reserved.
+//
+// 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 <http://code.google.com/p/sequel-pro/>
+//
+
+#import "SPMainThreadTrampoline.h"
+
+@implementation NSObject (SPMainThreadTrampoline)
+
+/**
+ * Provide a category on all NSObjects to return a trampoline for that
+ * object on the main thread.
+ * This cannot be retained or released.
+ */
+- (id) onMainThread
+{
+
+ // Return an autoreleased trampoline object
+ return [[[SPMainThreadTrampoline alloc] initWithObject:self] autorelease];
+}
+
+/**
+ * Provide a retained version of the category
+ */
+- (id) retainedOnMainThread
+{
+ return [[SPMainThreadTrampoline alloc] initWithObject:self];
+}
+
+@end
+
+
+@implementation SPMainThreadTrampoline
+
+/**
+ * The master initiliasation - the category implementation calls this
+ * with the requested object.
+ */
+- (id) initWithObject:(id)theObject
+{
+ if (self = [super init]) {
+ trampolineObject = theObject;
+ }
+ return self;
+}
+
+/**
+ * Delegate unrecognised methods to the trampolined objects, thanks to the magic
+ * of NSInvocation (see forwardInvocation: docs for background). Must be paired
+ * with methodSignationForSelector:.
+ */
+- (void) forwardInvocation:(NSInvocation *)theInvocation
+{
+ SEL theSelector = [theInvocation selector];
+ if (![trampolineObject respondsToSelector:theSelector]) [self doesNotRecognizeSelector:theSelector];
+
+ // Retain the arguments and object for the call for safety
+ [theInvocation retainArguments];
+ [trampolineObject retain];
+ [theInvocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:trampolineObject waitUntilDone:YES];
+ [trampolineObject release];
+}
+
+/**
+ * Return the correct method signatures for the trampolined object if
+ * NSObject doesn't implement the requested methods.
+ */
+- (NSMethodSignature *) methodSignatureForSelector:(SEL)theSelector
+{
+ NSMethodSignature *defaultSignature = [super methodSignatureForSelector:theSelector];
+ if (defaultSignature) return defaultSignature;
+
+ return [trampolineObject methodSignatureForSelector:theSelector];
+}
+
+/**
+ * Override the default repondsToSelector:, returning true if either NSObject
+ * or the trampolined object supports the selector.
+ */
+- (BOOL) respondsToSelector:(SEL)theSelector
+{
+ return ([super respondsToSelector:theSelector] || [trampolineObject respondsToSelector:theSelector]);
+}
+
+/**
+ * Override the default performSelector:, again either using NSObject defaults
+ * or performing the selector on the trampolined object.
+ * Note that the return value from the trampolined object is not preserved in this case.
+ */
+- (id) performSelector:(SEL)theSelector
+{
+ if ([super respondsToSelector:theSelector]) return [super performSelector:theSelector];
+
+ if (![trampolineObject respondsToSelector:theSelector]) [self doesNotRecognizeSelector:theSelector];
+
+ // Retain the object while performing calls on it
+ [trampolineObject retain];
+ [trampolineObject performSelectorOnMainThread:theSelector withObject:nil waitUntilDone:YES];
+ [trampolineObject release];
+
+ return nil;
+}
+
+/**
+ * Override the default performSelector:withObject: - see performSelector:
+ * Note that the return value from the trampolined object is not preserved in this case.
+ */
+- (id) performSelector:(SEL)theSelector withObject:(id)theObject
+{
+ if ([super respondsToSelector:theSelector]) return [super performSelector:theSelector withObject:theObject];
+
+ if (![trampolineObject respondsToSelector:theSelector]) [self doesNotRecognizeSelector:theSelector];
+
+ // Retain the trampolined object, and the argument object, while performing calls
+ [trampolineObject retain];
+ [theObject retain];
+ [trampolineObject performSelectorOnMainThread:theSelector withObject:theObject waitUntilDone:YES];
+ [theObject release];
+ [trampolineObject release];
+
+ return nil;
+}
+
+/**
+ * If the trampoline is sent the onMainThread category, just return the trampoline directly.
+ */
+- (id) onMainThread
+{
+ return self;
+}
+
+@end \ No newline at end of file
diff --git a/Source/TableContent.m b/Source/TableContent.m
index c5090573..abc6a8e7 100644
--- a/Source/TableContent.m
+++ b/Source/TableContent.m
@@ -51,6 +51,7 @@
#import "SPConstants.h"
#import "SPDataStorage.h"
#import "SPAlertSheets.h"
+#import "SPMainThreadTrampoline.h"
@implementation TableContent
@@ -783,7 +784,7 @@
// If the clause has the placeholder $BINARY that placeholder will be replaced
// by BINARY if the user pressed ⇧ while invoking 'Filter' otherwise it will
// replaced by @"".
- BOOL caseSensitive = (([[NSApp currentEvent] modifierFlags]
+ BOOL caseSensitive = (([[[NSApp onMainThread] currentEvent] modifierFlags]
& (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask)) > 0);
NSString *filterString;
@@ -1001,7 +1002,7 @@
[countString appendFormat:NSLocalizedString(@"%@ %@ selected", @"text showing how many rows are selected"), [numberFormatter stringFromNumber:[NSNumber numberWithInteger:[tableContentView numberOfSelectedRows]]], rowString];
}
- [countText setStringValue:countString];
+ [[countText onMainThread] setStringValue:countString];
}
#pragma mark -
@@ -1028,7 +1029,7 @@
NSAutoreleasePool *reloadPool = [[NSAutoreleasePool alloc] init];
// Check whether a save of the current row is required.
- if (![self saveRowOnDeselect]) return;
+ if (![[self onMainThread] saveRowOnDeselect]) return;
// Save view details to restore safely if possible (except viewport, which will be
// preserved automatically, and can then be scrolled as the table loads)
@@ -1086,7 +1087,7 @@
NSAutoreleasePool *filterPool = [[NSAutoreleasePool alloc] init];
// Check whether a save of the current row is required.
- if (![self saveRowOnDeselect]) return;
+ if (![[self onMainThread] saveRowOnDeselect]) return;
// Update history
[spHistoryControllerInstance updateHistoryEntries];
@@ -1095,7 +1096,7 @@
previousTableRowsCount = 0;
[self clearTableValues];
[self loadTableValues];
- [tableContentView scrollPoint:NSMakePoint(0.0, 0.0)];
+ [[tableContentView onMainThread] scrollPoint:NSMakePoint(0.0, 0.0)];
[tableDocumentInstance endTask];
[filterPool drain];
@@ -2840,7 +2841,7 @@
NSAutoreleasePool *sortPool = [[NSAutoreleasePool alloc] init];
// Check whether a save of the current row is required.
- if (![self saveRowOnDeselect]) {
+ if (![[self onMainThread] saveRowOnDeselect]) {
[sortPool drain];
return;
}
@@ -2850,11 +2851,19 @@
isDesc = !isDesc;
} else {
isDesc = NO;
- [tableContentView setIndicatorImage:nil inTableColumn:[tableContentView tableColumnWithIdentifier:sortCol]];
+ [[tableContentView onMainThread] setIndicatorImage:nil inTableColumn:[tableContentView tableColumnWithIdentifier:sortCol]];
}
if (sortCol) [sortCol release];
sortCol = [[NSNumber alloc] initWithInteger:[[tableColumn identifier] integerValue]];
+ // Set the highlight and indicatorImage
+ [[tableContentView onMainThread] setHighlightedTableColumn:tableColumn];
+ if (isDesc) {
+ [[tableContentView onMainThread] setIndicatorImage:[NSImage imageNamed:@"NSDescendingSortIndicator"] inTableColumn:tableColumn];
+ } else {
+ [[tableContentView onMainThread] setIndicatorImage:[NSImage imageNamed:@"NSAscendingSortIndicator"] inTableColumn:tableColumn];
+ }
+
// Update data using the new sort order
previousTableRowsCount = tableRowsCount;
[self loadTableValues];
@@ -2867,14 +2876,6 @@
return;
}
- // Set the highlight and indicatorImage
- [tableContentView setHighlightedTableColumn:tableColumn];
- if (isDesc) {
- [tableContentView setIndicatorImage:[NSImage imageNamed:@"NSDescendingSortIndicator"] inTableColumn:tableColumn];
- } else {
- [tableContentView setIndicatorImage:[NSImage imageNamed:@"NSAscendingSortIndicator"] inTableColumn:tableColumn];
- }
-
[tableDocumentInstance endTask];
[sortPool drain];
}
diff --git a/Source/TableDump.m b/Source/TableDump.m
index d50d2b85..6657a4ee 100644
--- a/Source/TableDump.m
+++ b/Source/TableDump.m
@@ -39,6 +39,7 @@
#import "SPConstants.h"
#import "SPAlertSheets.h"
#import "SPFieldMapperController.h"
+#import "SPMainThreadTrampoline.h"
@implementation TableDump
@@ -122,10 +123,15 @@
*/
- (void) closeAndStopProgressSheet
{
+ if (![NSThread isMainThread]) {
+ [self performSelectorOnMainThread:@selector(closeAndStopProgressSheet) withObject:nil waitUntilDone:YES];
+ return;
+ }
+
[NSApp endSheet:singleProgressSheet];
[singleProgressSheet orderOut:nil];
- [singleProgressBar stopAnimation:self];
- [singleProgressBar setMaxValue:100];
+ [[singleProgressBar onMainThread] stopAnimation:self];
+ [[singleProgressBar onMainThread] setMaxValue:100];
}
#pragma mark -
@@ -346,14 +352,14 @@
{
// Start an indeterminate progress sheet, as getting the current result set can take a significant period of time
- [singleProgressTitle setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting content view to CSV", @"title showing that application is saving content view as CSV")]];
- [singleProgressText setStringValue:NSLocalizedString(@"Exporting data...", @"text showing that app is preparing data")];
- [singleProgressBar setUsesThreadedAnimation:YES];
- [singleProgressBar setIndeterminate:YES];
- [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
- [singleProgressSheet makeKeyWindow];
-
- [singleProgressBar startAnimation:self];
+ [[singleProgressTitle onMainThread] setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting content view to CSV", @"title showing that application is saving content view as CSV")]];
+ [[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];
+ [[singleProgressSheet onMainThread] makeKeyWindow];
+
+ [[singleProgressBar onMainThread] startAnimation:self];
NSArray *contentViewArray = [tableContentInstance currentResult];
if ( [exportActionName isEqualToString:@"exportBrowseViewAsCSV"] ) {
@@ -386,17 +392,17 @@
// Start an indeterminate progress sheet, as getting the current result set can take a significant period of time
if ([exportActionName isEqualToString:@"exportCustomResultAsCSV"]) {
- [singleProgressTitle setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting custom query view to CSV", @"title showing that application is saving custom query view as CSV")]];
+ [[singleProgressTitle onMainThread] setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting custom query view to CSV", @"title showing that application is saving custom query view as CSV")]];
} else {
- [singleProgressTitle setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting custom query view to XML", @"title showing that application is saving custom query view as XML")]];
+ [[singleProgressTitle onMainThread] setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting custom query view to XML", @"title showing that application is saving custom query view as XML")]];
}
- [singleProgressText setStringValue:NSLocalizedString(@"Exporting data...", @"text showing that app is preparing data")];
- [singleProgressBar setUsesThreadedAnimation:YES];
- [singleProgressBar setIndeterminate:YES];
- [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
- [singleProgressSheet makeKeyWindow];
+ [[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];
+ [[singleProgressSheet onMainThread] makeKeyWindow];
- [singleProgressBar startAnimation:self];
+ [[singleProgressBar onMainThread] startAnimation:self];
NSArray *customQueryViewArray = [customQueryInstance currentResult];
if ( [exportActionName isEqualToString:@"exportCustomResultAsCSV"] ) {
@@ -565,16 +571,16 @@
// Reset progress interface
[errorsView setString:@""];
- [singleProgressTitle setStringValue:NSLocalizedString(@"Importing SQL", @"text showing that the application is importing SQL")];
- [singleProgressText setStringValue:NSLocalizedString(@"Reading...", @"text showing that app is reading dump")];
- [singleProgressBar setIndeterminate:NO];
- [singleProgressBar setMaxValue:fileTotalLength];
- [singleProgressBar setUsesThreadedAnimation:YES];
- [singleProgressBar startAnimation:self];
+ [[singleProgressTitle onMainThread] setStringValue:NSLocalizedString(@"Importing SQL", @"text showing that the application is importing SQL")];
+ [[singleProgressText onMainThread] setStringValue:NSLocalizedString(@"Reading...", @"text showing that app is reading dump")];
+ [[singleProgressBar onMainThread] setIndeterminate:NO];
+ [[singleProgressBar onMainThread] setMaxValue:fileTotalLength];
+ [[singleProgressBar onMainThread] setUsesThreadedAnimation:YES];
+ [[singleProgressBar onMainThread] startAnimation:self];
// Open the progress sheet
- [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
- [singleProgressSheet makeKeyWindow];
+ [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
+ [[singleProgressSheet onMainThread] makeKeyWindow];
[tableDocumentInstance setQueryMode:SPImportExportQueryMode];
@@ -804,15 +810,15 @@
// Reset progress interface
[errorsView setString:@""];
- [singleProgressTitle setStringValue:NSLocalizedString(@"Importing CSV", @"text showing that the application is importing CSV")];
- [singleProgressText setStringValue:NSLocalizedString(@"Reading...", @"text showing that app is reading dump")];
- [singleProgressBar setIndeterminate:YES];
- [singleProgressBar setUsesThreadedAnimation:YES];
- [singleProgressBar startAnimation:self];
+ [[singleProgressTitle onMainThread] setStringValue:NSLocalizedString(@"Importing CSV", @"text showing that the application is importing CSV")];
+ [[singleProgressText onMainThread] setStringValue:NSLocalizedString(@"Reading...", @"text showing that app is reading dump")];
+ [[singleProgressBar onMainThread] setIndeterminate:YES];
+ [[singleProgressBar onMainThread] setUsesThreadedAnimation:YES];
+ [[singleProgressBar onMainThread] startAnimation:self];
// Open the progress sheet
- [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
- [singleProgressSheet makeKeyWindow];
+ [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
+ [[singleProgressSheet onMainThread] makeKeyWindow];
[tableDocumentInstance setQueryMode:SPImportExportQueryMode];
@@ -947,11 +953,11 @@
}
// Reset progress interface and open the progress sheet
- [singleProgressBar setIndeterminate:NO];
- [singleProgressBar setMaxValue:fileTotalLength];
- [singleProgressBar startAnimation:self];
- [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
- [singleProgressSheet makeKeyWindow];
+ [[singleProgressBar onMainThread] setIndeterminate:NO];
+ [[singleProgressBar onMainThread] setMaxValue:fileTotalLength];
+ [[singleProgressBar onMainThread] startAnimation:self];
+ [[NSApp onMainThread] beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil];
+ [[singleProgressSheet onMainThread] makeKeyWindow];
// Set up the field names import string for INSERT or REPLACE INTO
[insertBaseString appendString:csvImportHeaderString];
@@ -1243,13 +1249,13 @@
[fieldMapperController setImportDataArray:fieldMappingImportArray hasHeader:[importFieldNamesSwitch state] isPreview:fieldMappingImportArrayIsPreview];
// Show field mapper sheet and set the focus to it
- [NSApp beginSheet:[fieldMapperController window]
+ [[NSApp onMainThread] beginSheet:[fieldMapperController window]
modalForWindow:tableWindow
modalDelegate:self
didEndSelector:@selector(fieldMapperDidEndSheet:returnCode:contextInfo:)
contextInfo:nil];
- [[fieldMapperController window] makeKeyWindow];
+ [[[fieldMapperController window] onMainThread] makeKeyWindow];
// Wait for field mapper sheet
while (fieldMapperSheetStatus == 1)
@@ -1443,21 +1449,17 @@
// Reset the interface
[errorsView setString:@""];
- [errorsView displayIfNeeded];
- [singleProgressTitle setStringValue:NSLocalizedString(@"Exporting SQL", @"text showing that the application is exporting SQL")];
- [singleProgressTitle displayIfNeeded];
- [singleProgressText setStringValue:NSLocalizedString(@"Dumping...", @"text showing that app is writing dump")];
- [singleProgressText displayIfNeeded];
- [singleProgressBar setDoubleValue:0];
- [singleProgressBar displayIfNeeded];
+ [[singleProgressTitle onMainThread] setStringValue:NSLocalizedString(@"Exporting SQL", @"text showing that the application is exporting SQL")];
+ [[singleProgressText onMainThread] setStringValue:NSLocalizedString(@"Dumping...", @"text showing that app is writing dump")];
+ [[singleProgressBar onMainThread] setDoubleValue:0];
progressBarWidth = (NSInteger)[singleProgressBar bounds].size.width;
- [singleProgressBar setMaxValue:progressBarWidth];
+ [[singleProgressBar onMainThread] setMaxValue:progressBarWidth];
// Open the progress sheet
- [NSApp beginSheet:singleProgressSheet
+ [[NSApp onMainThread] beginSheet:singleProgressSheet
modalForWindow:tableWindow modalDelegate:self
didEndSelector:nil contextInfo:nil];
- [singleProgressSheet makeKeyWindow];
+ [[singleProgressSheet onMainThread] makeKeyWindow];
[tableDocumentInstance setQueryMode:SPImportExportQueryMode];
@@ -1525,11 +1527,11 @@
// Update the progress text and reset the progress bar to indeterminate status while fetching data
tableName = NSArrayObjectAtIndex(selectedTables, i);
- [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %ld of %lu (%@): Fetching data...", @"text showing that app is fetching data for table dump"), (long)(i+1), (unsigned long)[selectedTables count], tableName]];
- [singleProgressText displayIfNeeded];
- [singleProgressBar setIndeterminate:YES];
- [singleProgressBar setUsesThreadedAnimation:YES];
- [singleProgressBar startAnimation:self];
+ [[singleProgressText onMainThread] setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %ld of %lu (%@): Fetching data...", @"text showing that app is fetching data for table dump"), (long)(i+1), (unsigned long)[selectedTables count], tableName]];
+ [[singleProgressText onMainThread] displayIfNeeded];
+ [[singleProgressBar onMainThread] setIndeterminate:YES];
+ [[singleProgressBar onMainThread] setUsesThreadedAnimation:YES];
+ [[singleProgressBar onMainThread] startAnimation:self];
// Add the name of table
[fileHandle writeData:[[NSString stringWithFormat:@"# Dump of table %@\n# ------------------------------------------------------------\n\n", tableName]
@@ -1599,12 +1601,10 @@
fieldNames = [streamingResult fetchFieldNames];
// Update the progress text and set the progress bar back to determinate
- [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %ld of %lu (%@): Dumping...", @"text showing that app is writing data for table dump"), (long)(i+1), (unsigned long)[selectedTables count], tableName]];
- [singleProgressText displayIfNeeded];
- [singleProgressBar stopAnimation:self];
- [singleProgressBar setIndeterminate:NO];
- [singleProgressBar setDoubleValue:0];
- [singleProgressBar displayIfNeeded];
+ [[singleProgressText onMainThread] setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %ld of %lu (%@): Dumping...", @"text showing that app is writing data for table dump"), (long)(i+1), (unsigned long)[selectedTables count], tableName]];
+ [[singleProgressBar onMainThread] stopAnimation:self];
+ [[singleProgressBar onMainThread] setIndeterminate:NO];
+ [[singleProgressBar onMainThread] setDoubleValue:0];
if (rowCount) {
queryLength = 0;
@@ -1632,12 +1632,12 @@
[sqlString setString:@""];
// Update the progress bar
- [singleProgressBar setDoubleValue:(j*progressBarWidth/rowCount)];
- if ((NSInteger)[singleProgressBar doubleValue] > lastProgressValue) {
- lastProgressValue = (NSInteger)[singleProgressBar doubleValue];
- [singleProgressBar displayIfNeeded];
+ if ((j*progressBarWidth/rowCount) > lastProgressValue) {
+ [singleProgressBar setDoubleValue:(j*progressBarWidth/rowCount)];
+ lastProgressValue = (j*progressBarWidth/rowCount);
}
-
+
+
for ( t = 0 ; t < colCount ; t++ ) {
// Add NULL values directly to the output row
@@ -1710,7 +1710,7 @@
[metaString appendString:@"UNLOCK TABLES;\n"];
[fileHandle writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]];
- // Drain the autorelease pool
+ // Drain the autorelease pool
[exportAutoReleasePool drain];
}
@@ -1930,19 +1930,16 @@
NSString *previousConnectionEncoding;
BOOL previousConnectionEncodingViaLatin1;
- [singleProgressTitle setStringValue:NSLocalizedString(@"Exporting Dot file", @"text showing that the application is exporting a Dot file")];
- [singleProgressTitle displayIfNeeded];
- [singleProgressText setStringValue:NSLocalizedString(@"Dumping...", @"text showing that app is writing dump")];
- [singleProgressText displayIfNeeded];
+ [[singleProgressTitle onMainThread] setStringValue:NSLocalizedString(@"Exporting Dot file", @"text showing that the application is exporting a Dot file")];
+ [[singleProgressText onMainThread] setStringValue:NSLocalizedString(@"Dumping...", @"text showing that app is writing dump")];
progressBarWidth = (NSInteger)[singleProgressBar bounds].size.width;
- [singleProgressBar setDoubleValue:0];
- [singleProgressBar displayIfNeeded];
+ [[singleProgressBar onMainThread] setDoubleValue:0];
// Open the progress sheet
- [NSApp beginSheet:singleProgressSheet
+ [[NSApp onMainThread] beginSheet:singleProgressSheet
modalForWindow:tableWindow modalDelegate:self
didEndSelector:nil contextInfo:nil];
- [singleProgressSheet makeKeyWindow];
+ [[singleProgressSheet onMainThread] makeKeyWindow];
[metaString setString:@"// Generated by: Sequel Pro\n"];
[metaString appendString:[NSString stringWithFormat:@"// Version %@\n",
@@ -1980,11 +1977,10 @@
NSString *tableName = [[tables objectAtIndex:i] objectAtIndex:1];
NSDictionary *tinfo = [tableDataInstance informationForTable:tableName];
- [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %ld of %lu (%@): Fetching data...", @"text showing that app is fetching data for table dump"), (long)(i+1), (unsigned long)[tables count], tableName]];
- [singleProgressText displayIfNeeded];
- [singleProgressBar setIndeterminate:YES];
- [singleProgressBar setUsesThreadedAnimation:YES];
- [singleProgressBar startAnimation:self];
+ [[singleProgressText onMainThread] setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %ld of %lu (%@): Fetching data...", @"text showing that app is fetching data for table dump"), (long)(i+1), (unsigned long)[tables count], tableName]];
+ [[singleProgressBar onMainThread] setIndeterminate:YES];
+ [[singleProgressBar onMainThread] setUsesThreadedAnimation:YES];
+ [[singleProgressBar onMainThread] startAnimation:self];
NSString *hdrColor = @"#DDDDDD";
if( [[tinfo objectForKey:@"type"] isEqualToString:@"View"] ) {
@@ -2035,11 +2031,10 @@
}
- [singleProgressText setStringValue:NSLocalizedString(@"Fetching relations...", @"text showing that app is fetching data")];
- [singleProgressText displayIfNeeded];
- [singleProgressBar setIndeterminate:YES];
- [singleProgressBar setUsesThreadedAnimation:YES];
- [singleProgressBar startAnimation:self];
+ [[singleProgressText onMainThread] setStringValue:NSLocalizedString(@"Fetching relations...", @"text showing that app is fetching data")];
+ [[singleProgressBar onMainThread] setIndeterminate:YES];
+ [[singleProgressBar onMainThread] setUsesThreadedAnimation:YES];
+ [[singleProgressBar onMainThread] startAnimation:self];
[metaString setString:@"edge [ arrowhead=inv, arrowtail=normal, style=dashed, color=\"#444444\" ];\n"];
@@ -2090,7 +2085,7 @@
lineEnds:(NSString *)lineEndString
withNumericColumns:(NSArray *)tableColumnNumericStatus
totalRows:(NSInteger)totalRows
- silently:(BOOL)silently;
+ silently:(BOOL)silently
{
NSAutoreleasePool *csvExportPool;
NSStringEncoding tableEncoding = [MCPConnection encodingForMySQLEncoding:[[tableDocumentInstance connectionEncoding] UTF8String]];
@@ -2135,23 +2130,22 @@
// Updating the progress bar can take >20% of processing time - store details to only update when required
progressBarWidth = (NSInteger)[singleProgressBar bounds].size.width;
lastProgressValue = 0;
- [singleProgressBar setMaxValue:progressBarWidth];
- [singleProgressBar setDoubleValue:0];
- [singleProgressBar setIndeterminate:NO];
- [singleProgressBar setUsesThreadedAnimation:YES];
- [singleProgressBar displayIfNeeded];
+ [[singleProgressBar onMainThread] setMaxValue:progressBarWidth];
+ [[singleProgressBar onMainThread] setDoubleValue:0];
+ [[singleProgressBar onMainThread] setIndeterminate:NO];
+ [[singleProgressBar onMainThread] setUsesThreadedAnimation:YES];
if ( !silently ) {
// Set the progress text
- [singleProgressTitle setStringValue:NSLocalizedString(@"Exporting CSV", @"text showing that the application is exporting a CSV")];
- [singleProgressText setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")];
+ [[singleProgressTitle onMainThread] setStringValue:NSLocalizedString(@"Exporting CSV", @"text showing that the application is exporting a CSV")];
+ [[singleProgressText onMainThread] setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")];
// Open progress sheet
- [NSApp beginSheet:singleProgressSheet
+ [[NSApp onMainThread] beginSheet:singleProgressSheet
modalForWindow:tableWindow modalDelegate:self
didEndSelector:nil contextInfo:nil];
- [singleProgressSheet makeKeyWindow];
+ [[singleProgressSheet onMainThread] makeKeyWindow];
}
// Set up escaped versions of strings for substitution within the loop
@@ -2297,11 +2291,9 @@
// Update the progress counter and progress bar
currentRowIndex++;
- if (totalRows)
+ if (totalRows && (currentRowIndex*progressBarWidth/totalRows) > lastProgressValue) {
[singleProgressBar setDoubleValue:(currentRowIndex*progressBarWidth/totalRows)];
- if ((NSInteger)[singleProgressBar doubleValue] > lastProgressValue) {
- lastProgressValue = (NSInteger)[singleProgressBar doubleValue];
- [singleProgressBar displayIfNeeded];
+ lastProgressValue = (currentRowIndex*progressBarWidth/totalRows);
}
// If an array was supplied and we've processed all rows, break
@@ -2322,7 +2314,7 @@
} else {
// Restore the progress bar to a normal maximum
- [singleProgressBar setMaxValue:100];
+ [[singleProgressBar onMainThread] setMaxValue:100];
}
return TRUE;
@@ -2352,10 +2344,9 @@
// Updating the progress bar can take >20% of processing time - store details to only update when required
progressBarWidth = (NSInteger)[singleProgressBar bounds].size.width;
lastProgressValue = 0;
- [singleProgressBar setIndeterminate:NO];
- [singleProgressBar setMaxValue:progressBarWidth];
- [singleProgressBar setDoubleValue:0];
- [singleProgressBar displayIfNeeded];
+ [[singleProgressBar onMainThread] setIndeterminate:NO];
+ [[singleProgressBar onMainThread] setMaxValue:progressBarWidth];
+ [[singleProgressBar onMainThread] setDoubleValue:0];
// Set up an array of encoded field names as opening and closing tags
if (array) {
@@ -2374,14 +2365,14 @@
if ( !silently ) {
// Set the progress text
- [singleProgressTitle setStringValue:NSLocalizedString(@"Exporting XML", @"text showing that the application is exporting XML")];
- [singleProgressText setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")];
+ [[singleProgressTitle onMainThread] setStringValue:NSLocalizedString(@"Exporting XML", @"text showing that the application is exporting XML")];
+ [[singleProgressText onMainThread] setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")];
// Open progress sheet
- [NSApp beginSheet:singleProgressSheet
+ [[NSApp onMainThread] beginSheet:singleProgressSheet
modalForWindow:tableWindow modalDelegate:self
didEndSelector:nil contextInfo:nil];
- [singleProgressSheet makeKeyWindow];
+ [[singleProgressSheet onMainThread] makeKeyWindow];
}
// Output the XML header if required
@@ -2465,11 +2456,9 @@
// Update the progress counter and progress bar
currentRowIndex++;
- if (totalRows)
+ if (totalRows && (currentRowIndex*progressBarWidth/totalRows) > lastProgressValue) {
[singleProgressBar setDoubleValue:(currentRowIndex*progressBarWidth/totalRows)];
- if ((NSInteger)[singleProgressBar doubleValue] > lastProgressValue) {
- lastProgressValue = (NSInteger)[singleProgressBar doubleValue];
- [singleProgressBar displayIfNeeded];
+ lastProgressValue = (currentRowIndex*progressBarWidth/totalRows);
}
// If an array was supplied and we've processed all rows, break
@@ -2495,7 +2484,7 @@
} else {
// Restore the progress bar to a normal maximum
- [singleProgressBar setMaxValue:100];
+ [[singleProgressBar onMainThread] setMaxValue:100];
}
return TRUE;
@@ -2540,20 +2529,17 @@
// Reset the interface
[errorsView setString:@""];
- [errorsView displayIfNeeded];
- [singleProgressTitle setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting %@", @"text showing that the application is importing a supplied format"), [type uppercaseString]]];
- [singleProgressText setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")];
- [singleProgressText displayIfNeeded];
- [singleProgressBar setDoubleValue:0];
- [singleProgressBar displayIfNeeded];
+ [[singleProgressTitle onMainThread] setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting %@", @"text showing that the application is importing a supplied format"), [type uppercaseString]]];
+ [[singleProgressText onMainThread] setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")];
+ [[singleProgressBar onMainThread] setDoubleValue:0];
[tableDocumentInstance setQueryMode:SPImportExportQueryMode];
// Open the progress sheet
- [NSApp beginSheet:singleProgressSheet
+ [[NSApp onMainThread] beginSheet:singleProgressSheet
modalForWindow:tableWindow modalDelegate:self
didEndSelector:nil contextInfo:nil];
- [singleProgressSheet makeKeyWindow];
+ [[singleProgressSheet onMainThread] makeKeyWindow];
// Add a dump header to the dump file, dependant on export type.
if ( [type isEqualToString:@"csv"] ) {
@@ -2594,11 +2580,10 @@
// Update the progress text and reset the progress bar to indeterminate status
tableName = [selectedTables objectAtIndex:i];
- [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %ld of %lu (%@): fetching data...", @"text showing that app is fetching data for table dump"), (long)(i+1), (unsigned long)[selectedTables count], tableName]];
- [singleProgressText displayIfNeeded];
- [singleProgressBar setIndeterminate:YES];
- [singleProgressBar setUsesThreadedAnimation:YES];
- [singleProgressBar startAnimation:self];
+ [[singleProgressText onMainThread] setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %ld of %lu (%@): fetching data...", @"text showing that app is fetching data for table dump"), (long)(i+1), (unsigned long)[selectedTables count], tableName]];
+ [[singleProgressBar onMainThread] setIndeterminate:YES];
+ [[singleProgressBar onMainThread] setUsesThreadedAnimation:YES];
+ [[singleProgressBar onMainThread] startAnimation:self];
// For CSV exports of more than one table, output the name of the table
if ( [type isEqualToString:@"csv"] && [selectedTables count] > 1) {
@@ -2649,13 +2634,11 @@
}
// Update the progress text and set the progress bar back to determinate
- [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %ld of %lu (%@): Writing data...", @"text showing that app is writing data for table export"), (long)(i+1), (unsigned long)[selectedTables count], tableName]];
- [singleProgressText displayIfNeeded];
- [singleProgressBar stopAnimation:self];
- [singleProgressBar setUsesThreadedAnimation:NO];
- [singleProgressBar setIndeterminate:NO];
- [singleProgressBar setDoubleValue:0];
- [singleProgressBar displayIfNeeded];
+ [[singleProgressText onMainThread] setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %ld of %lu (%@): Writing data...", @"text showing that app is writing data for table export"), (long)(i+1), (unsigned long)[selectedTables count], tableName]];
+ [[singleProgressBar onMainThread] stopAnimation:self];
+ [[singleProgressBar onMainThread] setUsesThreadedAnimation:NO];
+ [[singleProgressBar onMainThread] setIndeterminate:NO];
+ [[singleProgressBar onMainThread] setDoubleValue:0];
// Use the appropriate export method to write the data to file
if ( [type isEqualToString:@"csv"] ) {
@@ -2865,7 +2848,7 @@
#pragma mark -
#pragma mark Table view datasource methods
-- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView;
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [tables count];
}
@@ -2951,7 +2934,7 @@
[exportToolbar setSelectedItemIdentifier:[[[exportToolbar items] objectAtIndex:0] itemIdentifier]];
}
-- (id)init;
+- (id)init
{
self = [super init];
@@ -3004,6 +2987,11 @@
- (void)showErrorSheetWithMessage:(NSString*)message
{
+ if (![NSThread isMainThread]) {
+ [self performSelectorOnMainThread:@selector(showErrorSheetWithMessage:) withObject:message waitUntilDone:YES];
+ return;
+ }
+
[errorsView setString:message];
[NSApp beginSheet:errorsSheet
modalForWindow:tableWindow
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index def54998..1d068c78 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -151,6 +151,7 @@
588B2CC90FE5641E00EC5FC0 /* ssh-connecting.png in Resources */ = {isa = PBXBuildFile; fileRef = 588B2CC60FE5641E00EC5FC0 /* ssh-connecting.png */; };
588B2CCA0FE5641E00EC5FC0 /* ssh-disconnected.png in Resources */ = {isa = PBXBuildFile; fileRef = 588B2CC70FE5641E00EC5FC0 /* ssh-disconnected.png */; };
589235321020C1230011DE00 /* SPHistoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = 589235301020C1230011DE00 /* SPHistoryController.m */; };
+ 589582151154F8F400EDCC28 /* SPMainThreadTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 589582141154F8F400EDCC28 /* SPMainThreadTrampoline.m */; };
58BC5E56103898140058C2E6 /* MCPStreamingResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 583B779710386B0200B21F7E /* MCPStreamingResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
58BC5E571038983E0058C2E6 /* MCPResultPlus.h in Headers */ = {isa = PBXBuildFile; fileRef = 17B7B5D1101603B200F057DE /* MCPResultPlus.h */; settings = {ATTRIBUTES = (Public, ); }; };
58C34F5310B86CAE00D37E14 /* NSNotificationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C34F5210B86CAE00D37E14 /* NSNotificationAdditions.m */; };
@@ -553,6 +554,8 @@
588B2CC70FE5641E00EC5FC0 /* ssh-disconnected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ssh-disconnected.png"; sourceTree = "<group>"; };
589235301020C1230011DE00 /* SPHistoryController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPHistoryController.m; sourceTree = "<group>"; };
589235311020C1230011DE00 /* SPHistoryController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPHistoryController.h; sourceTree = "<group>"; };
+ 589582131154F8F400EDCC28 /* SPMainThreadTrampoline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPMainThreadTrampoline.h; sourceTree = "<group>"; };
+ 589582141154F8F400EDCC28 /* SPMainThreadTrampoline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMainThreadTrampoline.m; sourceTree = "<group>"; };
58C34F5110B86CAE00D37E14 /* NSNotificationAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSNotificationAdditions.h; sourceTree = "<group>"; };
58C34F5210B86CAE00D37E14 /* NSNotificationAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSNotificationAdditions.m; sourceTree = "<group>"; };
58C3506410B9A56C00D37E14 /* button_left.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = button_left.png; sourceTree = "<group>"; };
@@ -1449,6 +1452,8 @@
582A01E8107C0C170027D42B /* SPNotLoaded.m */,
5870868210FA3E9C00D58E1C /* SPDataStorage.h */,
5870868310FA3E9C00D58E1C /* SPDataStorage.m */,
+ 589582131154F8F400EDCC28 /* SPMainThreadTrampoline.h */,
+ 589582141154F8F400EDCC28 /* SPMainThreadTrampoline.m */,
);
name = "Category Additions";
sourceTree = "<group>";
@@ -1849,6 +1854,7 @@
BCE0025D11173D2A009DA533 /* SPFieldMapperController.m in Sources */,
17E090E811498FC9007FC1B4 /* SPPrintController.m in Sources */,
BC2777A011514B940034DF6A /* SPNavigatorController.m in Sources */,
+ 589582151154F8F400EDCC28 /* SPMainThreadTrampoline.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};