aboutsummaryrefslogtreecommitdiffstats
path: root/Source/SPExportController.m
diff options
context:
space:
mode:
Diffstat (limited to 'Source/SPExportController.m')
-rw-r--r--Source/SPExportController.m766
1 files changed, 477 insertions, 289 deletions
diff --git a/Source/SPExportController.m b/Source/SPExportController.m
index 559e8071..83c87ea6 100644
--- a/Source/SPExportController.m
+++ b/Source/SPExportController.m
@@ -5,6 +5,7 @@
// sequel-pro
//
// Created by Ben Perry (benperry.com.au) on 21/02/09.
+// Modified by Stuart Connolly (stuconnolly.com)
//
// 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
@@ -22,39 +23,68 @@
//
// More info at <http://code.google.com/p/sequel-pro/>
+#import <MCPKit/MCPKit.h>
+
#import "SPExportController.h"
-#import "SPCSVExporter.h"
+#import "SPExportInitializer.h"
#import "TablesList.h"
#import "SPTableData.h"
-#import "TableDocument.h"
+#import "TableContent.h"
#import "SPArrayAdditions.h"
#import "SPStringAdditions.h"
#import "SPConstants.h"
+#import "SPGrowlController.h"
@interface SPExportController (PrivateAPI)
-- (void)_initializeExportUsingSelectedOptions;
-- (BOOL)_exportTables:(NSArray *)exportTables asType:(SPExportType)type toMultipleFiles:(BOOL)multipleFiles;
+- (void)_toggleExportButton;
+- (void)_resizeWindowByHeightDelta:(NSInteger)delta;
@end
@implementation SPExportController
@synthesize connection;
+@synthesize exportToMultipleFiles;
@synthesize exportCancelled;
+#pragma mark -
+#pragma mark Initialization
+
/**
* Initializes an instance of SPExportController
*/
- (id)init
{
- if ((self = [super init])) {
+ if (self = [super initWithWindowNibName:@"ExportDialog"]) {
+
[self setExportCancelled:NO];
+ [self setExportToMultipleFiles:YES];
+
+ exportType = 0;
+ exportTableCount = 0;
+ currentTableExportIndex = 0;
+
+ exportFilename = @"";
+ exportTypeLabel = @"";
+
+ createCustomFilename = NO;
+ sqlPreviousConnectionEncodingViaLatin1 = NO;
tables = [[NSMutableArray alloc] init];
+ exporters = [[NSMutableArray alloc] init];
operationQueue = [[NSOperationQueue alloc] init];
- tableExportMapping = [NSMutableDictionary dictionary];
- nibObjectsToRelease = [[NSMutableArray alloc] init];
+
+ showAdvancedView = NO;
+
+ heightOffset = 0;
+ windowMinWidth = [[self window] minSize].width;
+ windowMinHeigth = [[self window] minSize].height;
+
+ prefs = [NSUserDefaults standardUserDefaults];
+
+ // Default filename tokens
+ availableFilenameTokens = @"host,database,table,date,time";
}
return self;
@@ -64,9 +94,12 @@
* Upon awakening select the first toolbar item
*/
- (void)awakeFromNib
-{
+{
// Upon awakening select the SQL tab
[exportToolbar setSelectedItemIdentifier:[[[exportToolbar items] objectAtIndex:0] itemIdentifier]];
+
+ // Select the 'selected tables' option
+ [exportInputMatrix selectCellAtRow:2 column:0];
}
#pragma mark -
@@ -76,52 +109,166 @@
* Display the export window allowing the user to select what and of what type to export.
*/
- (void)export
-{
+{
+ [self exportTables:nil asFormat:0];
+}
- // 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];
+/**
+ * Displays the export window with the supplied tables and export type/format selected.
+ */
+- (void)exportTables:(NSArray *)exportTables asFormat:(SPExportType)format
+{
+ [self refreshTableList:self];
+
+ if (exportTables && format) {
+
+ // Select the correct tab according to the supplied export type
+ [exportToolbar setSelectedItemIdentifier:[[[exportToolbar items] objectAtIndex:(format - 1)] itemIdentifier]];
+
+ // Select the 'selected tables' source option
+ [exportInputMatrix selectCellAtRow:2 column:0];
+
+ // Disable all tables
+ for (NSMutableArray *table in tables)
+ {
+ [table replaceObjectAtIndex:1 withObject:[NSNumber numberWithBool:NO]];
+ [table replaceObjectAtIndex:2 withObject:[NSNumber numberWithBool:NO]];
+ [table replaceObjectAtIndex:3 withObject:[NSNumber numberWithBool:NO]];
+ }
+
+ // Select the supplied tables
+ for (NSMutableArray *table in tables)
+ {
+ for (NSString *exportTable in exportTables)
+ {
+ if ([exportTable isEqualToString:[table objectAtIndex:0]]) {
+ [table replaceObjectAtIndex:1 withObject:[NSNumber numberWithBool:YES]];
+ [table replaceObjectAtIndex:2 withObject:[NSNumber numberWithBool:YES]];
+ [table replaceObjectAtIndex:3 withObject:[NSNumber numberWithBool:YES]];
+ }
+ }
+ }
+
+ [exportTableList reloadData];
+
+ // Ensure interface validation
+ [self switchTab:[[exportToolbar items] objectAtIndex:(format - 1)]];
}
- NSUInteger i;
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
- [tables removeAllObjects];
+ // If found the set the default path to the user's desktop, otherwise use their home directory
+ [exportPathField setStringValue:([paths count] > 0) ? [paths objectAtIndex:0] : NSHomeDirectory()];
- MCPResult *queryResult = (MCPResult *)[[self connection] listTables];
+ [NSApp beginSheet:[self window]
+ modalForWindow:[tableDocumentInstance parentWindow]
+ modalDelegate:self
+ didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
+ contextInfo:nil];
+}
+
+/**
+ * Opens the errors sheet and displays the supplied errors string.
+ */
+- (void)openExportErrorsSheetWithString:(NSString *)errors
+{
+ [errorsTextView setString:@""];
+ [errorsTextView setString:errors];
- if ([queryResult numOfRows]) [queryResult dataSeek:0];
+ [NSApp beginSheet:errorsWindow
+ modalForWindow:[tableDocumentInstance parentWindow]
+ modalDelegate:self
+ didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
+ contextInfo:nil];
+}
+
+/**
+ * Displays the export finished Growl notification.
+ */
+- (void)displayExportFinishedGrowlNotification
+{
+ // Export finished Growl notification
+ [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Export Finished"
+ description:[NSString stringWithFormat:NSLocalizedString(@"Finished exporting to %@", @"description for finished exporting growl notification"), exportFilename]
+ window:[tableDocumentInstance parentWindow]
+ notificationName:@"Export Finished"];
+}
+
+/**
+ * Expands the custom filename format based on the selected tokens.
+ */
+- (NSString *)expandCustomFilenameFormatFromString:(NSString *)format usingTableName:(NSString *)table
+{
+ NSMutableString *string = [NSMutableString stringWithString:format];
- for ( i = 0 ; i < [queryResult numOfRows] ; i++ )
- {
- [tables addObject:[NSMutableArray arrayWithObjects:
- [NSNumber numberWithBool:YES],
- NSArrayObjectAtIndex([queryResult fetchRowAsArray], 0),
- nil]];
+ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+
+ [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
+
+ [dateFormatter setDateStyle:NSDateFormatterShortStyle];
+ [dateFormatter setTimeStyle:NSDateFormatterNoStyle];
+
+ [string replaceOccurrencesOfString:@"host" withString:[tableDocumentInstance host]
+ options:NSLiteralSearch
+ range:NSMakeRange(0, [string length])];
+
+ [string replaceOccurrencesOfString:@"database" withString:[tableDocumentInstance database]
+ options:NSLiteralSearch
+ range:NSMakeRange(0, [string length])];
+
+ if (table) {
+ [string replaceOccurrencesOfString:@"table" withString:table
+ options:NSLiteralSearch
+ range:NSMakeRange(0, [string length])];
+ }
+ else {
+ [string replaceOccurrencesOfString:@"table" withString:@""
+ options:NSLiteralSearch
+ range:NSMakeRange(0, [string length])];
}
- [exportTableList reloadData];
-
- [exportPathField setStringValue:NSHomeDirectory()];
+ [string replaceOccurrencesOfString:@"date" withString:[dateFormatter stringFromDate:[NSDate date]]
+ options:NSLiteralSearch
+ range:NSMakeRange(0, [string length])];
- [NSApp beginSheet:exportWindow
- modalForWindow:[tableDocumentInstance parentWindow]
- modalDelegate:self
- didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
- contextInfo:nil];
+ [dateFormatter setDateStyle:NSDateFormatterNoStyle];
+ [dateFormatter setTimeStyle:NSDateFormatterShortStyle];
+
+ [string replaceOccurrencesOfString:@"time" withString:[dateFormatter stringFromDate:[NSDate date]]
+ options:NSLiteralSearch
+ range:NSMakeRange(0, [string length])];
+
+ // Strip comma separators
+ [string replaceOccurrencesOfString:@"," withString:@""
+ options:NSLiteralSearch
+ range:NSMakeRange(0, [string length])];
+
+ // Replace colons with hyphens
+ [string replaceOccurrencesOfString:@":" withString:@"-"
+ options:NSLiteralSearch
+ range:NSMakeRange(0, [string length])];
+
+ [dateFormatter release];
+
+ return string;
}
/**
- * Closes the export dialog
+ * Closes the export dialog.
*/
- (IBAction)closeSheet:(id)sender
{
- [NSApp endSheet:exportWindow returnCode:[sender tag]];
- [exportWindow orderOut:self];
+ if ([sender window] == [self window]) {
+
+ // Close the advanced options view if it's open
+ [exportAdvancedOptionsView setHidden:YES];
+ [exportAdvancedOptionsViewButton setState:NSOffState];
+
+ [self _resizeWindowByHeightDelta:0];
+ }
+
+ [NSApp endSheet:[sender window] returnCode:[sender tag]];
+ [[sender window] orderOut:self];
}
/**
@@ -130,20 +277,70 @@
- (IBAction)switchTab:(id)sender
{
if ([sender isKindOfClass:[NSToolbarItem class]]) {
- [exportTabBar selectTabViewItemWithIdentifier:[[sender label] lowercaseString]];
+
+ currentToolbarItem = sender;
+
+ NSString *label = [[currentToolbarItem label] lowercaseString];
+
+ [exportTabBar selectTabViewItemWithIdentifier:label];
+
+ BOOL isSQL = [label isEqualToString:@"sql"];
+ BOOL isCSV = [label isEqualToString:@"csv"];
+ BOOL isXML = [label isEqualToString:@"xml"];
+ BOOL isHTML = [label isEqualToString:@"html"];
+ BOOL isPDF = [label isEqualToString:@"pdf"];
+ BOOL isDot = [label isEqualToString:@"dot"];
+
+ BOOL disable = (isCSV || isXML || isHTML || isPDF || isDot);
+
+ [exportFilePerTableCheck setHidden:(isSQL || isDot)];
+ [exportFilePerTableNote setHidden:(isSQL || isDot)];
+
+ [exportTableList setEnabled:(!isDot)];
+ [exportSelectAllTablesButton setEnabled:(!isDot)];
+ [exportDeselectAllTablesButton setEnabled:(!isDot)];
+ [exportRefreshTablesButton setEnabled:(!isDot)];
+
+ [[exportInputMatrix cellAtRow:2 column:0] setEnabled:(!isDot)];
+
+ if (isDot) {
+ // Disable all source checkboxes
+ [[exportInputMatrix cellAtRow:0 column:0] setEnabled:NO];
+ [[exportInputMatrix cellAtRow:1 column:0] setEnabled:NO];
+ }
+ else {
+ // Enable/disable the 'filtered result' and 'query result' options
+ [[exportInputMatrix cellAtRow:0 column:0] setEnabled:((disable) && ([[tableContentInstance currentResult] count] > 1))];
+ [[exportInputMatrix cellAtRow:1 column:0] setEnabled:((disable) && ([[customQueryInstance currentResult] count] > 1))];
+ }
- [exportFilePerTableCheck setHidden:[[sender label] isEqualToString:@"Excel"]];
- [exportFilePerTableNote setHidden:[[sender label] isEqualToString:@"Excel"]];
+ [[exportTableList tableColumnWithIdentifier:@"structure"] setHidden:disable];
+ [[exportTableList tableColumnWithIdentifier:@"drop"] setHidden:disable];
+
+ [[[exportTableList tableColumnWithIdentifier:@"content"] headerCell] setStringValue:(disable) ? @"" : @"C"];
+
+ [exportCSVNULLValuesAsTextField setStringValue:[prefs stringForKey:SPNullValue]];
}
}
/**
- *
+ * Enables/disables and shows/hides various interface controls depending on the selected item.
*/
- (IBAction)switchInput:(id)sender
{
if ([sender isKindOfClass:[NSMatrix class]]) {
- [exportTableList setEnabled:([[sender selectedCell] tag] == 3)];
+
+ BOOL isSelectedTables = ([[sender selectedCell] tag] == SPTableExport);
+
+ [exportFilePerTableCheck setHidden:(!isSelectedTables)];
+ [exportFilePerTableNote setHidden:(!isSelectedTables)];
+
+ [exportTableList setEnabled:isSelectedTables];
+ [exportSelectAllTablesButton setEnabled:isSelectedTables];
+ [exportDeselectAllTablesButton setEnabled:isSelectedTables];
+ [exportRefreshTablesButton setEnabled:isSelectedTables];
+
+ availableFilenameTokens = ([[sender selectedCell] tag] == SPQueryExport) ? @"host,database,date,time" : @"host,database,table,date,time";
}
}
@@ -164,24 +361,161 @@
}
/**
- *
+ * Opens the open panel when user selects to change the output path.
*/
- (IBAction)changeExportOutputPath:(id)sender
{
+ [exportCustomFilenameTokenField setStringValue:@""];
+ [exportCustomFilenameTokensField setStringValue:availableFilenameTokens];
+
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseFiles:NO];
[panel setCanChooseDirectories:YES];
[panel setCanCreateDirectories:YES];
+ [panel setAccessoryView:exportCustomFilenameView];
+
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
- [panel beginSheetForDirectory:NSHomeDirectory()
+ [panel beginSheetForDirectory:([paths count] > 0) ? [paths objectAtIndex:0] : NSHomeDirectory()
file:nil
- modalForWindow:exportWindow
+ modalForWindow:[self window]
modalDelegate:self
didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:)
contextInfo:nil];
}
+/**
+ * Refreshes the table list.
+ */
+- (IBAction)refreshTableList:(id)sender
+{
+ NSUInteger i;
+
+ [tables removeAllObjects];
+
+ // For all modes, retrieve table and view names
+ NSArray *tablesAndViews = [tablesListInstance allTableAndViewNames];
+
+ for (id itemName in tablesAndViews) {
+ [tables addObject:[NSMutableArray arrayWithObjects:
+ itemName,
+ [NSNumber numberWithBool:YES],
+ [NSNumber numberWithBool:YES],
+ [NSNumber numberWithBool:YES],
+ [NSNumber numberWithInt:SPTableTypeTable],
+ nil]];
+ }
+
+ // For SQL only, add procedures and functions
+ if ([[[currentToolbarItem label] lowercaseString] isEqualToString:@"sql"]) {
+ NSArray *procedures = [tablesListInstance allProcedureNames];
+
+ for (id procName in procedures)
+ {
+ [tables addObject:[NSMutableArray arrayWithObjects:
+ procName,
+ [NSNumber numberWithBool:YES],
+ [NSNumber numberWithBool:YES],
+ [NSNumber numberWithBool:YES],
+ [NSNumber numberWithInt:SPTableTypeProc],
+ nil]];
+ }
+
+ NSArray *functions = [tablesListInstance allFunctionNames];
+
+ for (id funcName in functions)
+ {
+ [tables addObject:[NSMutableArray arrayWithObjects:
+ funcName,
+ [NSNumber numberWithBool:YES],
+ [NSNumber numberWithBool:YES],
+ [NSNumber numberWithBool:YES],
+ [NSNumber numberWithInt:SPTableTypeFunc],
+ nil]];
+ }
+ }
+
+ [exportTableList reloadData];
+}
+
+/**
+ * Selects or de-selects all tables.
+ */
+- (IBAction)selectDeselectAllTables:(id)sender
+{
+ [self refreshTableList:self];
+
+ for (NSMutableArray *table in tables)
+ {
+ [table replaceObjectAtIndex:2 withObject:[NSNumber numberWithBool:[sender tag]]];
+ }
+
+ [exportTableList reloadData];
+
+ [self _toggleExportButton];
+}
+
+/**
+ * Toggles the state of the custom filename format token fields.
+ */
+- (IBAction)toggleCustomFilenameFormat:(id)sender
+{
+ [exportCustomFilenameTokenField setEnabled:[sender state]];
+ [exportCustomFilenameTokensField setEnabled:[sender state]];
+}
+
+/**
+ * Toggles the display of the advanced options box.
+ */
+- (IBAction)toggleAdvancedExportOptionsView:(id)sender
+{
+ showAdvancedView = !showAdvancedView;
+
+ if (showAdvancedView) {
+ [exportAdvancedOptionsViewButton setState:NSOnState];
+ [self _resizeWindowByHeightDelta:([exportAdvancedOptionsView frame].size.height + 10)];
+ [exportAdvancedOptionsView setHidden:NO];
+ }
+ else {
+ [exportAdvancedOptionsViewButton setState:NSOffState];
+ [self _resizeWindowByHeightDelta:0];
+ [exportAdvancedOptionsView setHidden:YES];
+ }
+}
+
+/**
+ * Toggles the export button when choosing to include or table structures in an SQL export.
+ */
+- (IBAction)toggleSQLIncludeStructure:(id)sender
+{
+ [[exportTableList tableColumnWithIdentifier:@"structure"] setHidden:(![sender state])];
+
+ [self _toggleExportButton];
+}
+
+/**
+ * Toggles the export button when choosing to include or exclude table contents in an SQL export.
+ */
+- (IBAction)toggleSQLIncludeContent:(id)sender
+{
+ [sender setTag:[sender state]];
+
+ [self selectDeselectAllTables:sender];
+
+ [self _toggleExportButton];
+}
+
+/**
+ * Toggles the export button when choosing to include or exclude table drop syntax in an SQL export.
+ */
+- (IBAction)toggleSQLIncludeDropSyntax:(id)sender
+{
+ [[exportTableList tableColumnWithIdentifier:@"drop"] setHidden:(![sender state])];
+
+ [self _toggleExportButton];
+}
+
#pragma mark -
#pragma mark Table view datasource methods
@@ -190,30 +524,32 @@
return [tables count];
}
-- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
-{
- return NSArrayObjectAtIndex([tables objectAtIndex:rowIndex], ([[aTableColumn identifier] isEqualToString:@"switch"]) ? 0 : 1);
+- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
+{
+ return NSArrayObjectAtIndex([tables objectAtIndex:rowIndex], [exportTableList columnWithIdentifier:[tableColumn identifier]]);
}
-- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
-{
- [[tables objectAtIndex:rowIndex] replaceObjectAtIndex:0 withObject:anObject];
+- (void)tableView:(NSTableView *)tableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
+{
+ [[tables objectAtIndex:rowIndex] replaceObjectAtIndex:[exportTableList columnWithIdentifier:[tableColumn identifier]] withObject:anObject];
+
+ [self _toggleExportButton];
}
#pragma mark -
#pragma mark Table view delegate methods
-- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
+- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)rowIndex
{
- return (aTableView != exportTableList);
+ return (tableView != exportTableList);
}
-- (BOOL)tableView:(NSTableView *)aTableView shouldTrackCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
+- (BOOL)tableView:(NSTableView *)tableView shouldTrackCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
{
- return (aTableView == exportTableList);
+ return (tableView == exportTableList);
}
-- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
+- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
{
[aCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
}
@@ -223,7 +559,7 @@
- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
{
- NSMutableArray *items = [NSMutableArray arrayWithCapacity:6];
+ NSMutableArray *items = [NSMutableArray array];
for (NSToolbarItem *item in [toolbar items])
{
@@ -234,21 +570,16 @@
}
#pragma mark -
-#pragma mark SPExporterDataAccess protocol methods
+#pragma mark Text field delegate methods
-/**
- * This method is part of the SPExporterDataAccess protocol. It is called when an expoter complete it's data
- * conversion process and the operation is effectively complete. The resulting data can be accessed via
- * SPExporter's exportData method.
- */
-- (void)exporterDataConversionProcessComplete:(SPExporter *)exporter
-{
- // Do something with the data...
-
- // If there are no more operations in the queue, close the progress sheet
- if ([[operationQueue operations] count] == 0) {
- [NSApp endSheet:exportProgressWindow returnCode:0];
- [exportProgressWindow orderOut:self];
+- (void)controlTextDidChange:(NSNotification *)notification
+{
+ if ([notification object] == exportCustomFilenameTokenField) {
+
+ // Create the table name, but since this is only an example, use the first table in the list
+ NSString *filename = [self expandCustomFilenameFormatFromString:[exportCustomFilenameTokenField stringValue] usingTableName:[[tablesListInstance tables] objectAtIndex:1]];
+
+ [exportCustomFilenameExampleTextField setStringValue:[NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"Example", @"example label"), filename]];
}
}
@@ -256,7 +587,7 @@
#pragma mark Other
/**
- * Invoked when the user
+ * Invoked when the user dismissing the export dialog and starts the export process if required.
*/
- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
@@ -264,12 +595,12 @@
if (returnCode == NSOKButton) {
// Initialize the export after half a second to give the export sheet a chance to close
- [self performSelector:@selector(_initializeExportUsingSelectedOptions) withObject:nil afterDelay:0.5];
+ [self performSelector:@selector(initializeExportUsingSelectedOptions) withObject:nil afterDelay:0.5];
}
}
/**
- * Invoked when the user dismisses the save panel. Updates the selected directory is they clicked OK.
+ * Invoked when the user dismisses the save panel. Updates the selected directory if they clicked OK.
*/
- (void)savePanelDidEnd:(NSSavePanel *)panel returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
@@ -286,247 +617,104 @@
- (void)dealloc
{
[tables release], tables = nil;
+ [exporters release], exporters = nil;
[operationQueue release], operationQueue = nil;
- for (id retainedObject in nibObjectsToRelease) [retainedObject release];
- [nibObjectsToRelease release], nibObjectsToRelease = nil;
+
+ if (sqlPreviousConnectionEncoding) [sqlPreviousConnectionEncoding release], sqlPreviousConnectionEncoding = nil;
[super dealloc];
}
-@end
-
-@implementation SPExportController (PrivateAPI)
+#pragma mark -
+#pragma mark Private API
/**
- *
+ * Enables or disables the export button based on the state of various interface controls.
*/
-- (void)_initializeExportUsingSelectedOptions
+- (void)_toggleExportButton
{
- // First determine what type of export the user selected
- SPExportType exportType = 0;
+ NSString *label = [[currentToolbarItem label] lowercaseString];
- for (NSToolbarItem *item in [exportToolbar items])
- {
- if ([[item itemIdentifier] isEqualToString:[exportToolbar selectedItemIdentifier]]) {
- exportType = [item tag];
- break;
- }
- }
-
- // Determine what data to use (filtered result, custom query result or selected table(s)) for the export operation
- SPExportSource exportSource = ([exportInputMatrix selectedRow] + 1);
-
- NSMutableArray *exportTables = [NSMutableArray array];
-
- // Get the data depending on the source
- switch (exportSource)
- {
- case SP_FILTERED_EXPORT:
-
- break;
- case SP_CUSTOM_QUERY_EXPORT:
-
- break;
- case SP_TABLE_EXPORT:
- // Create an array of tables to export
- for (NSMutableArray *table in tables)
- {
- if ([[table objectAtIndex:0] boolValue]) {
- [exportTables addObject:[table objectAtIndex:1]];
- }
+ BOOL isSQL = [label isEqualToString:@"sql"];
+ BOOL isCSV = [label isEqualToString:@"csv"];
+ BOOL isXML = [label isEqualToString:@"xml"];
+ BOOL isHTML = [label isEqualToString:@"html"];
+ BOOL isPDF = [label isEqualToString:@"pdf"];
+
+ if (isCSV || isXML || isHTML || isPDF) {
+ [exportButton setEnabled:NO];
+
+ // Only enable the button if at least one table is selected
+ for (NSArray *table in tables)
+ {
+ if ([NSArrayObjectAtIndex(table, 2) boolValue]) {
+ [exportButton setEnabled:YES];
+ break;
}
-
- break;
+ }
}
-
- // Begin the export based on the type
- switch (exportSource)
- {
- case SP_FILTERED_EXPORT:
-
- break;
- case SP_CUSTOM_QUERY_EXPORT:
-
- break;
- case SP_TABLE_EXPORT:
- [self _exportTables:exportTables asType:exportType toMultipleFiles:[exportFilePerTableCheck state]];
- break;
+ else if (isSQL) {
+ BOOL structureEnabled = [exportSQLIncludeStructureCheck state];
+ BOOL contentEnabled = [exportSQLIncludeContentCheck state];
+ BOOL dropEnabled = [exportSQLIncludeDropSyntaxCheck state];
+
+ // Disable if all are unchecked
+ if ((!contentEnabled) && (!structureEnabled) && (!dropEnabled)) {
+ [exportButton setEnabled:NO];
+ }
+ // Disable if structure is unchecked, but content and drop are as dropping a table then trying to insert
+ // into it is obviously an error
+ else if (contentEnabled && (!structureEnabled) && (dropEnabled)) {
+ [exportButton setEnabled:NO];
+ }
+ else {
+ [exportButton setEnabled:(contentEnabled || (structureEnabled || dropEnabled))];
+ }
}
}
/**
- * Exports the contents' of the supplied array of tables. Note that this method currently only supports
- * exporting in CSV and XML formats.
+ * Resizes the export window's height by the supplied delta, while retaining the position of
+ * all interface controls.
*/
-- (BOOL)_exportTables:(NSArray *)exportTables asType:(SPExportType)type toMultipleFiles:(BOOL)multipleFiles
+- (void)_resizeWindowByHeightDelta:(NSInteger)delta
{
- NSUInteger i;
+ NSUInteger scrollMask = [exportTablelistScrollView autoresizingMask];
+ NSUInteger buttonBarMask = [exportTableListButtonBar autoresizingMask];
+ NSUInteger tabBarMask = [exportTabBar autoresizingMask];
+ NSUInteger buttonMask = [exportAdvancedOptionsViewButton autoresizingMask];
+ NSUInteger textFieldMask = [exportAdvancedOptionsViewLabelButton autoresizingMask];
+ NSUInteger advancedViewMask = [exportAdvancedOptionsView autoresizingMask];
- NSMutableString *errors = [NSMutableString string];
+ NSRect frame = [[self window] frame];
- NSDictionary *tableDetails = nil;
- //NSStringEncoding encoding = [[self connection] encoding];
+ [exportTablelistScrollView setAutoresizingMask:NSViewNotSizable | NSViewMinYMargin];
+ [exportTableListButtonBar setAutoresizingMask:NSViewNotSizable | NSViewMinYMargin];
+ [exportTabBar setAutoresizingMask:NSViewNotSizable | NSViewMinYMargin];
+ [exportAdvancedOptionsViewButton setAutoresizingMask:NSViewNotSizable | NSViewMinYMargin];
+ [exportAdvancedOptionsViewLabelButton setAutoresizingMask:NSViewNotSizable | NSViewMinYMargin];
+ [exportAdvancedOptionsView setAutoresizingMask:NSViewNotSizable | NSViewMinYMargin];
- // Reset the interface
- [exportProgressTitle setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Exporting %@", @"text showing that the application is importing a supplied format"), @"CSV"]];
- [exportProgressText setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")];
- [exportProgressText displayIfNeeded];
- [exportProgressIndicator setDoubleValue:0];
- [exportProgressIndicator displayIfNeeded];
+ NSInteger newMinHeight = (windowMinHeigth - heightOffset + delta < windowMinHeigth) ? windowMinHeigth : windowMinHeigth - heightOffset + delta;
- // Open the progress sheet
- [NSApp beginSheet:exportProgressWindow
- modalForWindow:[tableDocumentInstance parentWindow]
- modalDelegate:self
- didEndSelector:nil
- contextInfo:nil];
-
- // Add a dump header to the dump file
- NSMutableString *csvLineEnd = [NSMutableString stringWithString:[exportCSVLinesTerminatedField stringValue]];
-
- [csvLineEnd replaceOccurrencesOfString:@"\\t" withString:@"\t"
- options:NSLiteralSearch
- range:NSMakeRange(0, [csvLineEnd length])];
-
- [csvLineEnd replaceOccurrencesOfString:@"\\n" withString:@"\n"
- options:NSLiteralSearch
- range:NSMakeRange(0, [csvLineEnd length])];
+ [[self window] setMinSize:NSMakeSize(windowMinWidth, newMinHeight)];
- [csvLineEnd replaceOccurrencesOfString:@"\\r" withString:@"\r"
- options:NSLiteralSearch
- range:NSMakeRange(0, [csvLineEnd length])];
+ frame.origin.y += heightOffset;
+ frame.size.height -= heightOffset;
- NSUInteger tableCount = [exportTables count];
+ heightOffset = delta;
- // If
- if ((type == SP_CSV_EXPORT) && (!multipleFiles) && (tableCount > 1)) {
-
- }
+ frame.origin.y -= heightOffset;
+ frame.size.height += heightOffset;
- /*if ([exportTables count] > 1) {
- [infoString setString:[NSString stringWithFormat:@"Host: %@ Database: %@ Generation Time: %@%@%@",
- [tableDocumentInstance host], [tableDocumentInstance database], [NSDate date], csvLineEnd, csvLineEnd]];
- }*/
-
- // Loop through the tables
- for (i = 0 ; i < tableCount; i++)
- {
- if ([self exportCancelled]) break;
-
- // Update the progress text and reset the progress bar to indeterminate status
- NSString *tableName = [exportTables objectAtIndex:i];
-
- [exportProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %lu of %l (%@): fetching data...", @"text showing that app is fetching data for table dump"), (unsigned long)(i + 1), (unsigned long)tableCount, tableName]];
- [exportProgressText displayIfNeeded];
-
- [exportProgressIndicator setIndeterminate:YES];
- [exportProgressIndicator setUsesThreadedAnimation:YES];
- [exportProgressIndicator startAnimation:self];
-
- // For CSV exports of more than one table, output the name of the table
- /*if (tableCount > 1) {
- [fileHandle writeData:[[NSString stringWithFormat:@"Table %@%@%@", tableName, csvLineEnd, csvLineEnd] dataUsingEncoding:encoding]];
- }*/
-
- // Determine whether this table is a table or a view via the create table command, and get the table details
- MCPResult *queryResult = [connection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE %@", [tableName backtickQuotedString]]];
- [queryResult setReturnDataAsStrings:YES];
-
- if ([queryResult numOfRows]) {
- tableDetails = [NSDictionary dictionaryWithDictionary:[queryResult fetchRowAsDictionary]];
-
- tableDetails = [NSDictionary dictionaryWithDictionary:([tableDetails objectForKey:@"Create View"]) ? [tableDataInstance informationForView:tableName] : [tableDataInstance informationForTable:tableName]];
- }
-
- // Retrieve the table details via the data class, and use it to build an array containing column numeric status
- NSMutableArray *tableColumnNumericStatus = [NSMutableArray array];
-
- for (NSDictionary *column in [tableDetails objectForKey:@"columns"])
- {
- NSString *tableColumnTypeGrouping = [column objectForKey:@"typegrouping"];
-
- [tableColumnNumericStatus addObject:[NSNumber numberWithBool:([tableColumnTypeGrouping isEqualToString:@"bit"] ||
- [tableColumnTypeGrouping isEqualToString:@"integer"] ||
- [tableColumnTypeGrouping isEqualToString:@"float"])]];
- }
-
- // Use low memory export?
- BOOL useLowMemoryBlockingStreaming = ([exportProcessLowMemory state] == NSOnState);
-
- // Make a streaming request for the data
- MCPStreamingResult *queryResultStreaming = [connection streamingQueryString:[NSString stringWithFormat:@"SELECT * FROM %@", [tableName backtickQuotedString]] useLowMemoryBlockingStreaming:useLowMemoryBlockingStreaming];
-
- // Note any errors during retrieval
- if ([connection queryErrored]) {
- [errors appendString:[NSString stringWithFormat:@"%@\n", [connection getLastErrorMessage]]];
- }
-
- SPExporter *exporter = nil;
- SPCSVExporter *csvExporter = nil;
-
- // Based on the type of export create a new instance of the corresponding exporter and set it's specific options
- switch (type)
- {
- case SP_SQL_EXPORT:
-
- break;
- case SP_CSV_EXPORT:
- csvExporter = [[SPCSVExporter alloc] initWithDelegate:self];
-
- [csvExporter setCsvOutputFieldNames:[exportCSVIncludeFieldNamesCheck state]];
- [csvExporter setCsvFieldSeparatorString:[exportCSVFieldsTerminatedField stringValue]];
- [csvExporter setCsvEnclosingCharacterString:[exportCSVFieldsWrappedField stringValue]];
- [csvExporter setCsvLineEndingString:[exportCSVLinesTerminatedField stringValue]];
- [csvExporter setCsvEscapeString:[exportCSVFieldsEscapedField stringValue]];
-
- [csvExporter setExportOutputEncoding:[MCPConnection encodingForMySQLEncoding:[[tableDocumentInstance connectionEncoding] UTF8String]]];
- [csvExporter setCsvNULLString:[[NSUserDefaults standardUserDefaults] objectForKey:SPNullValue]];
-
- [csvExporter setCsvTableColumnNumericStatus:tableColumnNumericStatus];
-
- // Assign the data to the exporter
- [csvExporter setCsvDataResult:queryResultStreaming];
-
- exporter = csvExporter;
-
- break;
- case SP_XML_EXPORT:
-
- break;
- case SP_PDF_EXPORT:
-
- break;
- case SP_HTML_EXPORT:
-
- break;
- case SP_EXCEL_EXPORT:
-
- break;
- }
-
- // Update the progress text and set the progress bar back to determinate
- [exportProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %lu of %lu (%@): Writing...", @"text showing that app is writing data for table export"), (unsigned long)(i + 1), (unsigned long)tableCount, tableName]];
- [exportProgressText displayIfNeeded];
-
- [exportProgressIndicator stopAnimation:self];
- [exportProgressIndicator setUsesThreadedAnimation:NO];
- [exportProgressIndicator setIndeterminate:NO];
- [exportProgressIndicator setDoubleValue:0];
- [exportProgressIndicator displayIfNeeded];
-
- // Start the actual data conversion process by placing the exporter on the operation queue.
- // Note that although it is highly likely there is no guarantee that the operation will executed
- // as soon as it's placed on the queue. There may be a delay if the queue is already executing it's
- // maximum number of concurrent operations. See the docs for more details.
- [operationQueue addOperation:exporter];
-
- if (csvExporter) [csvExporter release];
-
- // Add a spacer to the file
- //[fileHandle writeData:[[NSString stringWithFormat:@"%@%@%@", csvLineEnd, csvLineEnd, csvLineEnd] dataUsingEncoding:encoding]];
- }
+ [[self window] setFrame:frame display:YES animate:YES];
- return YES;
+ [exportTablelistScrollView setAutoresizingMask:scrollMask];
+ [exportTableListButtonBar setAutoresizingMask:buttonBarMask];
+ [exportTabBar setAutoresizingMask:tabBarMask];
+ [exportAdvancedOptionsViewButton setAutoresizingMask:buttonMask];
+ [exportAdvancedOptionsViewLabelButton setAutoresizingMask:textFieldMask];
+ [exportAdvancedOptionsView setAutoresizingMask:advancedViewMask];
}
@end