From d62541cf4992bd6b4e1d0f3bee58e8a6367c5e48 Mon Sep 17 00:00:00 2001 From: stuconnolly Date: Thu, 27 May 2010 20:08:13 +0000 Subject: Rename TablesList to SPTablesList. --- Source/CustomQuery.m | 2 +- Source/SPExportController.m | 2 +- Source/SPExportInitializer.m | 2 +- Source/SPExtendedTableInfo.h | 4 +- Source/SPExtendedTableInfo.m | 2 +- Source/SPFieldMapperController.m | 2 +- Source/SPHistoryController.h | 4 +- Source/SPHistoryController.m | 2 +- Source/SPNavigatorController.m | 2 +- Source/SPSQLExporter.m | 2 +- Source/SPTableData.m | 2 +- Source/SPTableInfo.m | 2 +- Source/SPTableRelations.m | 2 +- Source/SPTableTriggers.m | 2 +- Source/SPTableView.m | 4 +- Source/SPTablesList.h | 165 +++ Source/SPTablesList.m | 2433 ++++++++++++++++++++++++++++++++++++++ Source/SPTextView.h | 4 +- Source/SPTextView.m | 4 +- Source/TableContent.m | 2 +- Source/TableDocument.m | 6 +- Source/TableDump.m | 4 +- Source/TableSource.m | 4 +- Source/TablesList.h | 165 --- Source/TablesList.m | 2433 -------------------------------------- 25 files changed, 2628 insertions(+), 2628 deletions(-) create mode 100644 Source/SPTablesList.h create mode 100644 Source/SPTablesList.m delete mode 100644 Source/TablesList.h delete mode 100644 Source/TablesList.m (limited to 'Source') diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m index 6020a255..0e13d344 100644 --- a/Source/CustomQuery.m +++ b/Source/CustomQuery.m @@ -32,7 +32,7 @@ #import "SPDataAdditions.h" #import "SPDataCellFormatter.h" #import "TableDocument.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "RegexKitLite.h" #import "SPFieldEditorController.h" #import "SPTextAndLinkCell.h" diff --git a/Source/SPExportController.m b/Source/SPExportController.m index d87ccf7e..0cf756ef 100644 --- a/Source/SPExportController.m +++ b/Source/SPExportController.m @@ -27,7 +27,7 @@ #import "SPExportController.h" #import "SPExportInitializer.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPTableData.h" #import "TableContent.h" #import "SPArrayAdditions.h" diff --git a/Source/SPExportInitializer.m b/Source/SPExportInitializer.m index 46eaf2b6..7b82de87 100644 --- a/Source/SPExportInitializer.m +++ b/Source/SPExportInitializer.m @@ -29,7 +29,7 @@ #import "SPStringAdditions.h" #import "SPTableData.h" #import "TableDocument.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPGrowlController.h" #import "SPMainThreadTrampoline.h" #import "TableDocument.h" diff --git a/Source/SPExtendedTableInfo.h b/Source/SPExtendedTableInfo.h index aef1a6c4..9a067b0c 100644 --- a/Source/SPExtendedTableInfo.h +++ b/Source/SPExtendedTableInfo.h @@ -26,12 +26,12 @@ #import #import -@class SPTableData, SPDatabaseData, TablesList; +@class SPTableData, SPDatabaseData, SPTablesList; @interface SPExtendedTableInfo : NSObject { IBOutlet id tableDocumentInstance; - IBOutlet TablesList *tablesListInstance; + IBOutlet SPTablesList *tablesListInstance; IBOutlet SPTableData *tableDataInstance; IBOutlet SPDatabaseData *databaseDataInstance; IBOutlet id tableSourceInstance; diff --git a/Source/SPExtendedTableInfo.m b/Source/SPExtendedTableInfo.m index f48ce2ca..3770ecd6 100644 --- a/Source/SPExtendedTableInfo.m +++ b/Source/SPExtendedTableInfo.m @@ -30,7 +30,7 @@ #import "SPStringAdditions.h" #import "SPConstants.h" #import "TableDocument.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPAlertSheets.h" #import "TableSource.h" diff --git a/Source/SPFieldMapperController.m b/Source/SPFieldMapperController.m index 8ba0ebbb..0c54df10 100644 --- a/Source/SPFieldMapperController.m +++ b/Source/SPFieldMapperController.m @@ -25,7 +25,7 @@ #import "SPFieldMapperController.h" #import "SPTableData.h" #import "TableDump.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPArrayAdditions.h" #import "SPStringAdditions.h" #import "SPConstants.h" diff --git a/Source/SPHistoryController.h b/Source/SPHistoryController.h index b0f3e082..fc36f1bf 100644 --- a/Source/SPHistoryController.h +++ b/Source/SPHistoryController.h @@ -24,7 +24,7 @@ #import -@class TableDocument, TableContent, TablesList; +@class TableDocument, TableContent, SPTablesList; @interface SPHistoryController : NSObject { @@ -32,7 +32,7 @@ IBOutlet NSSegmentedControl *historyControl; TableContent *tableContentInstance; - TablesList *tablesListInstance; + SPTablesList *tablesListInstance; NSMutableArray *history; NSMutableDictionary *tableContentStates; NSUInteger historyPosition; diff --git a/Source/SPHistoryController.m b/Source/SPHistoryController.m index b8128a0f..40f99a8a 100644 --- a/Source/SPHistoryController.m +++ b/Source/SPHistoryController.m @@ -24,7 +24,7 @@ #import "TableDocument.h" #import "TableContent.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPConstants.h" #import "SPHistoryController.h" #import "SPStringAdditions.h" diff --git a/Source/SPNavigatorController.m b/Source/SPNavigatorController.m index 7e2ae14d..738430d4 100644 --- a/Source/SPNavigatorController.m +++ b/Source/SPNavigatorController.m @@ -28,7 +28,7 @@ #import "SPConstants.h" #import "ImageAndTextCell.h" #import "TableDocument.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPArrayAdditions.h" #import "SPLogger.h" #import "SPTooltip.h" diff --git a/Source/SPSQLExporter.m b/Source/SPSQLExporter.m index 99e42515..4591325a 100644 --- a/Source/SPSQLExporter.m +++ b/Source/SPSQLExporter.m @@ -26,7 +26,7 @@ #import #import "SPSQLExporter.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPConstants.h" #import "SPArrayAdditions.h" #import "SPStringAdditions.h" diff --git a/Source/SPTableData.m b/Source/SPTableData.m index 117f1a05..05908853 100644 --- a/Source/SPTableData.m +++ b/Source/SPTableData.m @@ -26,7 +26,7 @@ #import "SPTableData.h" #import "SPSQLParser.h" #import "TableDocument.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPStringAdditions.h" #import "SPArrayAdditions.h" #import "SPConstants.h" diff --git a/Source/SPTableInfo.m b/Source/SPTableInfo.m index eb8c0b55..99197a86 100644 --- a/Source/SPTableInfo.m +++ b/Source/SPTableInfo.m @@ -25,7 +25,7 @@ #import "SPTableInfo.h" #import "ImageAndTextCell.h" #import "TableDocument.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPTableData.h" #import "SPConstants.h" #import "SPStringAdditions.h" diff --git a/Source/SPTableRelations.m b/Source/SPTableRelations.m index e37d453d..ce31daee 100644 --- a/Source/SPTableRelations.m +++ b/Source/SPTableRelations.m @@ -25,7 +25,7 @@ #import "SPTableRelations.h" #import "TableDocument.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPTableData.h" #import "SPStringAdditions.h" #import "SPConstants.h" diff --git a/Source/SPTableTriggers.m b/Source/SPTableTriggers.m index e3cdda63..89aa5634 100644 --- a/Source/SPTableTriggers.m +++ b/Source/SPTableTriggers.m @@ -25,7 +25,7 @@ #import "SPTableTriggers.h" #import "TableDocument.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPTableData.h" #import "SPStringAdditions.h" #import "SPConstants.h" diff --git a/Source/SPTableView.m b/Source/SPTableView.m index 58e2206e..a1e44fb6 100644 --- a/Source/SPTableView.m +++ b/Source/SPTableView.m @@ -59,8 +59,8 @@ NSInteger row = [self rowAtPoint:[self convertPoint:[event locationInWindow] fromView:nil]]; if(row >= 0 && row < [self numberOfRows]) { - // Check for TablesList if right-click on header, then suppress context menu - if([[[[self delegate] class] description] isEqualToString:@"TablesList"]) { + // Check for SPTablesList if right-click on header, then suppress context menu + if([[[[self delegate] class] description] isEqualToString:@"SPTablesList"]) { if([NSArrayObjectAtIndex([[self delegate] valueForKeyPath:@"tableTypes"], row) integerValue] == -1) return nil; } diff --git a/Source/SPTablesList.h b/Source/SPTablesList.h new file mode 100644 index 00000000..d85235ab --- /dev/null +++ b/Source/SPTablesList.h @@ -0,0 +1,165 @@ +// +// $Id$ +// +// SPTablesList.h +// sequel-pro +// +// Created by lorenz textor (lorenz@textor.ch) on Wed May 01 2002. +// Copyright (c) 2002-2003 Lorenz Textor. 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 + +#import +#import + +@class SPHistoryController; + +@interface NSObject (NSSplitView) + +- (NSView *)collapsibleSubview; +- (IBAction)toggleCollapse:(id)sender; +- (BOOL)collapsibleSubviewIsCollapsed; +- (void)setCollapsibleSubviewCollapsed:(BOOL)flag; + +@end + +@interface SPTablesList : NSObject +{ + IBOutlet id tableDocumentInstance; + IBOutlet id tableSourceInstance; + IBOutlet id tableContentInstance; + IBOutlet id customQueryInstance; + IBOutlet id tableDumpInstance; + IBOutlet id tableDataInstance; + IBOutlet id extendedTableInfoInstance; + IBOutlet id databaseDataInstance; + IBOutlet id tableInfoInstance; + IBOutlet id tableTriggersInstance; + IBOutlet SPHistoryController *spHistoryControllerInstance; + + IBOutlet id copyTableSheet; + IBOutlet id tablesListView; + IBOutlet id copyTableButton; + IBOutlet id copyTableNameField; + IBOutlet id copyTableMessageField; + IBOutlet id copyTableContentSwitch; + IBOutlet id tabView; + IBOutlet id tableSheet; + IBOutlet id tableNameField; + IBOutlet id tableEncodingButton; + IBOutlet id tableTypeButton; + IBOutlet id toolbarAddButton; + IBOutlet id toolbarActionsButton; + IBOutlet id toolbarReloadButton; + IBOutlet id addTableButton; + IBOutlet id truncateTableButton; + IBOutlet id truncateTableContextButton; + IBOutlet NSSplitView *tableListSplitView; + IBOutlet NSSplitView *tableListFilterSplitView; + IBOutlet NSButton *tableInfoCollapseButton; + + IBOutlet NSSearchField *listFilterField; + + // Table list 'gear' menu items + IBOutlet NSMenuItem *removeTableMenuItem; + IBOutlet NSMenuItem *duplicateTableMenuItem; + IBOutlet NSMenuItem *renameTableMenuItem; + IBOutlet NSMenuItem *separatorTableMenuItem; + IBOutlet NSMenuItem *showCreateSyntaxMenuItem; + IBOutlet NSMenuItem *separatorTableMenuItem2; + + MCPConnection *mySQLConnection; + + // Table list context menu items + IBOutlet NSMenuItem *removeTableContextMenuItem; + IBOutlet NSMenuItem *duplicateTableContextMenuItem; + IBOutlet NSMenuItem *renameTableContextMenuItem; + IBOutlet NSMenuItem *separatorTableContextMenuItem; + IBOutlet NSMenuItem *showCreateSyntaxContextMenuItem; + IBOutlet NSMenuItem *separatorTableContextMenuItem2; + + NSMutableArray *tables; + NSMutableArray *filteredTables; + NSMutableArray *tableTypes; + NSMutableArray *filteredTableTypes; + NSInteger selectedTableType; + NSString *selectedTableName; + BOOL isTableListFiltered; + BOOL tableListIsSelectable; + BOOL tableListContainsViews; + + BOOL structureLoaded, contentLoaded, statusLoaded, triggersLoaded, alertSheetOpened; +} + +// IBAction methods +- (IBAction)updateTables:(id)sender; +- (IBAction)addTable:(id)sender; +- (IBAction)closeSheet:(id)sender; +- (IBAction)removeTable:(id)sender; +- (IBAction)copyTable:(id)sender; +- (IBAction)renameTable:(id)sender; +- (IBAction)truncateTable:(id)sender; +- (IBAction)togglePaneCollapse:(id)sender; + +// Additional methods +- (void)setConnection:(MCPConnection *)theConnection; +- (void)doPerformQueryService:(NSString *)query; +- (void)updateSelectionWithTaskString:(NSString *)taskString; +- (void)updateSelectionTask; +- (void)setSelection:(NSDictionary *)selectionDetails; +- (void)selectTableAtIndex:(NSNumber *)row; +- (void)makeTableListFilterHaveFocus; + +// Getters +- (NSArray *)selectedTableNames; +- (NSArray *)selectedTableItems; +- (NSString *)tableName; +- (NSInteger)tableType; +- (NSArray *)tables; +- (NSArray *)tableTypes; +- (NSArray *)allTableAndViewNames; +- (NSArray *)allTableNames; +- (NSArray *)allViewNames; +- (NSArray *)allFunctionNames; +- (NSArray *)allProcedureNames; +- (NSArray *)allDatabaseNames; +- (NSArray *)allSystemDatabaseNames; +- (NSString *)selectedDatabase; +- (BOOL)structureLoaded; +- (BOOL)contentLoaded; +- (BOOL)statusLoaded; + +// Setters +- (void)setContentRequiresReload:(BOOL)reload; +- (void)setStatusRequiresReload:(BOOL)reload; +- (BOOL)selectItemWithName:(NSString *)theName; + +// Tabview delegate methods +- (void)loadTabTask:(NSTabViewItem *)tabViewItem; + +// Table list filter interaction +- (void) showFilter; +- (void) hideFilter; +- (void) clearFilter; +- (IBAction) updateFilter:(id)sender; + +// Task interaction +- (void) startDocumentTaskForTab:(NSNotification *)aNotification; +- (void) endDocumentTaskForTab:(NSNotification *)aNotification; +- (void) setTableListSelectability:(BOOL)isSelectable; + +@end diff --git a/Source/SPTablesList.m b/Source/SPTablesList.m new file mode 100644 index 00000000..5bab7b65 --- /dev/null +++ b/Source/SPTablesList.m @@ -0,0 +1,2433 @@ +// +// $Id$ +// +// SPTablesList.m +// sequel-pro +// +// Created by lorenz textor (lorenz@textor.ch) on Wed May 01 2002. +// Copyright (c) 2002-2003 Lorenz Textor. 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 + +#import "SPTablesList.h" +#import "TableDocument.h" +#import "TableSource.h" +#import "TableContent.h" +#import "SPTableData.h" +#import "SPTableInfo.h" +#import "TableDump.h" +#import "ImageAndTextCell.h" +#import "SPStringAdditions.h" +#import "SPArrayAdditions.h" +#import "RegexKitLite.h" +#import "SPDatabaseData.h" +#import "NSMutableArray-MultipleSort.h" +#import "NSNotificationAdditions.h" +#import "SPConstants.h" +#import "SPAlertSheets.h" +#import "SPNavigatorController.h" +#import "SPMainThreadTrampoline.h" +#import "SPHistoryController.h" + +@interface SPTablesList (PrivateAPI) + +- (void)removeTable; +- (void)truncateTable; +- (void)addTable; +- (void)copyTable; +- (void)renameTableOfType:(SPTableType)tableType from:(NSString *)oldTableName to:(NSString *)newTableName; +- (BOOL)isTableNameValid:(NSString *)tableName forType:(SPTableType)tableType; +- (BOOL)isTableNameValid:(NSString *)tableName forType:(SPTableType)tableType ignoringSelectedTable:(BOOL)ignoreSelectedTable; + +@end + +@implementation SPTablesList + +#pragma mark - +#pragma mark IBAction methods + +/** + * Loads all table names in array tables and reload the tableView + */ +- (IBAction)updateTables:(id)sender +{ + MCPResult *theResult; + NSArray *resultRow; + NSInteger i; + NSString *previousSelectedTable = nil; + BOOL previousTableListIsSelectable = tableListIsSelectable; + + if (selectedTableName) previousSelectedTable = [[NSString alloc] initWithString:selectedTableName]; + if (isTableListFiltered) { + if (filteredTables) [filteredTables release]; + filteredTables = tables; + if (filteredTableTypes) [filteredTableTypes release]; + filteredTableTypes = tableTypes; + isTableListFiltered = NO; + } + tableListContainsViews = NO; + + tableListIsSelectable = YES; + [[tablesListView onMainThread] deselectAll:self]; + tableListIsSelectable = previousTableListIsSelectable; + [tables removeAllObjects]; + [tableTypes removeAllObjects]; + + if ([tableDocumentInstance database]) { + + // Notify listeners that a query has started + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance]; + + // Select the table list for the current database. On MySQL versions after 5 this will include + // views; on MySQL versions >= 5.0.02 select the "full" list to also select the table type column. + theResult = [mySQLConnection queryString:@"SHOW /*!50002 FULL*/ TABLES"]; + if ([theResult numOfRows]) [theResult dataSeek:0]; + if ([theResult numOfFields] == 1) { + for ( i = 0 ; i < [theResult numOfRows] ; i++ ) { + [tables addObject:[[theResult fetchRowAsArray] objectAtIndex:0]]; + [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeTable]]; + } + } else { + for ( i = 0 ; i < [theResult numOfRows] ; i++ ) { + resultRow = [theResult fetchRowAsArray]; + [tables addObject:[resultRow objectAtIndex:0]]; + if ([[resultRow objectAtIndex:1] isEqualToString:@"VIEW"]) { + [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeView]]; + tableListContainsViews = YES; + } else { + [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeTable]]; + } + } + } + + // Reorder the tables in alphabetical order + [tables sortArrayUsingSelector:@selector(localizedCompare:) withPairedMutableArrays:tableTypes, nil]; + + /* grab the procedures and functions + * + * using information_schema gives us more info (for information window perhaps?) but breaks + * backward compatibility with pre 4 I believe. I left the other methods below, in case. + */ + if ([mySQLConnection serverMajorVersion] >= 5) { + NSString *pQuery = [NSString stringWithFormat:@"SELECT * FROM information_schema.routines WHERE routine_schema = '%@' ORDER BY routine_name",[tableDocumentInstance database]]; + theResult = [mySQLConnection queryString:pQuery]; + + // Check for mysql errors - if information_schema is not accessible for some reasons + // omit adding procedures and functions + if(![mySQLConnection queryErrored] && theResult != nil && [theResult numOfRows] ) { + // add the header row + [tables addObject:NSLocalizedString(@"PROCS & FUNCS",@"header for procs & funcs list")]; + [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeNone]]; + [theResult dataSeek:0]; + + if( [theResult numOfFields] == 1 ) { + for( i = 0; i < [theResult numOfRows]; i++ ) { + [tables addObject:NSArrayObjectAtIndex([theResult fetchRowAsArray],3)]; + if( [NSArrayObjectAtIndex([theResult fetchRowAsArray], 4) isEqualToString:@"PROCEDURE"]) { + [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeProc]]; + } else { + [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeFunc]]; + } + } + } else { + for( i = 0; i < [theResult numOfRows]; i++ ) { + resultRow = [theResult fetchRowAsArray]; + [tables addObject:NSArrayObjectAtIndex(resultRow, 3)]; + if( [NSArrayObjectAtIndex(resultRow, 4) isEqualToString:@"PROCEDURE"] ) { + [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeProc]]; + } else { + [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeFunc]]; + } + } + } + } + } + /* + BOOL addedPFHeader = FALSE; + NSString *pQuery = [NSString stringWithFormat:@"SHOW PROCEDURE STATUS WHERE db = '%@'",[tableDocumentInstance database]]; + theResult = [mySQLConnection queryString:pQuery]; + + if( [theResult numOfRows] ) { + // add the header row + [tables addObject:NSLocalizedString(@"PROCS & FUNCS",@"header for procs & funcs list")]; + [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeNone]]; + addedPFHeader = TRUE; + [theResult dataSeek:0]; + + if( [theResult numOfFields] == 1 ) { + for( i = 0; i < [theResult numOfRows]; i++ ) { + [tables addObject:[[theResult fetchRowAsArray] objectAtIndex:1]]; + [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeProc]]; + } + } else { + for( i = 0; i < [theResult numOfRows]; i++ ) { + resultRow = [theResult fetchRowAsArray]; + [tables addObject:[resultRow objectAtIndex:1]]; + [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeProc]]; + } + } + } + + pQuery = [NSString stringWithFormat:@"SHOW FUNCTION STATUS WHERE db = '%@'",[tableDocumentInstance database]]; + theResult = [mySQLConnection queryString:pQuery]; + + if( [theResult numOfRows] ) { + if( !addedPFHeader ) { + // add the header row + [tables addObject:NSLocalizedString(@"PROCS & FUNCS",@"header for procs & funcs list")]; + [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeNone]]; + } + [theResult dataSeek:0]; + + if( [theResult numOfFields] == 1 ) { + for( i = 0; i < [theResult numOfRows]; i++ ) { + [tables addObject:[[theResult fetchRowAsArray] objectAtIndex:1]]; + [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeFunc]]; + } + } else { + for( i = 0; i < [theResult numOfRows]; i++ ) { + resultRow = [theResult fetchRowAsArray]; + [tables addObject:[resultRow objectAtIndex:1]]; + [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeFunc]]; + } + } + } + */ + // Notify listeners that the query has finished + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; + } + + // Add the table headers even if no tables were found + if (tableListContainsViews) { + [tables insertObject:NSLocalizedString(@"TABLES & VIEWS",@"header for table & views list") atIndex:0]; + } else { + [tables insertObject:NSLocalizedString(@"TABLES",@"header for table list") atIndex:0]; + } + [tableTypes insertObject:[NSNumber numberWithInteger:SPTableTypeNone] atIndex:0]; + + [[tablesListView onMainThread] reloadData]; + + // if the previous selected table still exists, select it + // but not if the update was called from SPTableData since it calls that method + // if a selected table doesn't exist - this happens if a table was deleted/renamed by an other user + // or if the table name contains characters which are not supported by the current set encoding + if( ![sender isKindOfClass:[SPTableData class]] && previousSelectedTable != nil && [tables indexOfObject:previousSelectedTable] < [tables count]) { + NSInteger itemToReselect = [tables indexOfObject:previousSelectedTable]; + tableListIsSelectable = YES; + [[tablesListView onMainThread] selectRowIndexes:[NSIndexSet indexSetWithIndex:itemToReselect] byExtendingSelection:NO]; + tableListIsSelectable = previousTableListIsSelectable; + if (selectedTableName) [selectedTableName release]; + selectedTableName = [[NSString alloc] initWithString:[tables objectAtIndex:itemToReselect]]; + selectedTableType = [[tableTypes objectAtIndex:itemToReselect] integerValue]; + } else { + if (selectedTableName) [selectedTableName release]; + selectedTableName = nil; + selectedTableType = SPTableTypeNone; + } + + // Determine whether or not to show the list filter based on the number of tables, and clear it + [[self onMainThread] clearFilter]; + if ([tables count] > 20) [self showFilter]; + else [self hideFilter]; + + // Set the filter placeholder text + if ([tableDocumentInstance database]) { + [[[listFilterField cell] onMainThread] setPlaceholderString:NSLocalizedString(@"Filter", @"Filter placeholder")]; + } + + if (previousSelectedTable) [previousSelectedTable release]; + + // Query the structure of all databases in the background + if(sender == self) + // Invoked by SP + [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:nil]; + else + // User press refresh button ergo force update + [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", [NSNumber numberWithBool:YES], @"cancelQuerying", nil]]; + + +} + +/** + * Adds a new table to the tables-array (no changes in mysql-db) + */ +- (IBAction)addTable:(id)sender +{ + if ((![tableSourceInstance saveRowOnDeselect]) || (![tableContentInstance saveRowOnDeselect]) || (![tableDocumentInstance database])) { + return; + } + + [[tableDocumentInstance parentWindow] endEditingFor:nil]; + + // Populate the table type (engine) popup button + [tableTypeButton removeAllItems]; + + NSArray *engines = [databaseDataInstance getDatabaseStorageEngines]; + + // Add default menu item + [tableTypeButton addItemWithTitle:@"Default"]; + [[tableTypeButton menu] addItem:[NSMenuItem separatorItem]]; + + for (NSDictionary *engine in engines) + { + [tableTypeButton addItemWithTitle:[engine objectForKey:@"Engine"]]; + } + + [NSApp beginSheet:tableSheet + modalForWindow:[tableDocumentInstance parentWindow] + modalDelegate:self + didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) + contextInfo:@"addTable"]; +} + +/** + * Closes the current sheet and stops the modal session + */ +- (IBAction)closeSheet:(id)sender +{ + [NSApp endSheet:[sender window] returnCode:[sender tag]]; + [[sender window] orderOut:self]; +} + +/** + * Invoked when user hits the remove button alert sheet to ask user if he really wants to delete the table. + */ +- (IBAction)removeTable:(id)sender +{ + if (![tablesListView numberOfSelectedRows]) + return; + + [[tableDocumentInstance parentWindow] endEditingFor:nil]; + + NSAlert *alert = [NSAlert alertWithMessageText:@"" defaultButton:NSLocalizedString(@"Delete", @"delete button") alternateButton:NSLocalizedString(@"Cancel", @"cancel button") otherButton:nil informativeTextWithFormat:@""]; + + [alert setAlertStyle:NSCriticalAlertStyle]; + + NSArray *buttons = [alert buttons]; + + // Change the alert's cancel button to have the key equivalent of return + [[buttons objectAtIndex:0] setKeyEquivalent:@"d"]; + [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; + [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; + + NSIndexSet *indexes = [tablesListView selectedRowIndexes]; + + NSString *tblTypes = @""; + NSUInteger currentIndex = [indexes lastIndex]; + + if ([tablesListView numberOfSelectedRows] == 1) { + if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeView) + tblTypes = NSLocalizedString(@"view", @"view"); + else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeTable) + tblTypes = NSLocalizedString(@"table", @"table"); + else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeProc) + tblTypes = NSLocalizedString(@"procedure", @"procedure"); + else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeFunc) + tblTypes = NSLocalizedString(@"function", @"function"); + + [alert setMessageText:[NSString stringWithFormat:NSLocalizedString(@"Delete %@ '%@'?", @"delete table/view message"), tblTypes, [filteredTables objectAtIndex:[tablesListView selectedRow]]]]; + [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete the %@ '%@'? This operation cannot be undone.", @"delete table/view informative message"), tblTypes, [filteredTables objectAtIndex:[tablesListView selectedRow]]]]; + } + else { + + BOOL areTableTypeEqual = YES; + NSInteger lastType = [[filteredTableTypes objectAtIndex:currentIndex] integerValue]; + while (currentIndex != NSNotFound) + { + if([[filteredTableTypes objectAtIndex:currentIndex] integerValue]!=lastType) + { + areTableTypeEqual = NO; + break; + } + currentIndex = [indexes indexLessThanIndex:currentIndex]; + } + if(areTableTypeEqual) + { + switch(lastType) { + case SPTableTypeTable: + tblTypes = NSLocalizedString(@"tables", @"tables"); + break; + case SPTableTypeView: + tblTypes = NSLocalizedString(@"views", @"views"); + break; + case SPTableTypeProc: + tblTypes = NSLocalizedString(@"procedures", @"procedures"); + break; + case SPTableTypeFunc: + tblTypes = NSLocalizedString(@"functions", @"functions"); + break; + } + + } else + tblTypes = NSLocalizedString(@"items", @"items"); + + [alert setMessageText:[NSString stringWithFormat:NSLocalizedString(@"Delete selected %@?", @"delete tables/views message"), tblTypes]]; + [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete the selected %@? This operation cannot be undone.", @"delete tables/views informative message"), tblTypes]]; + } + + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removeRow"]; +} + +/** + * Copies a table/view/proc/func, if desired with content + */ +- (IBAction)copyTable:(id)sender +{ + NSString *tableType = @""; + + if ([tablesListView numberOfSelectedRows] != 1) return; + if (![tableSourceInstance saveRowOnDeselect] || ![tableContentInstance saveRowOnDeselect]) return; + + [[tableDocumentInstance parentWindow] endEditingFor:nil]; + + // Detect table type: table or view + NSInteger tblType = [[filteredTableTypes objectAtIndex:[tablesListView selectedRow]] integerValue]; + + switch (tblType){ + case SPTableTypeTable: + tableType = NSLocalizedString(@"table",@"table"); + [copyTableContentSwitch setEnabled:YES]; + break; + case SPTableTypeView: + tableType = NSLocalizedString(@"view",@"view"); + [copyTableContentSwitch setEnabled:NO]; + break; + case SPTableTypeProc: + tableType = NSLocalizedString(@"procedure",@"procedure"); + [copyTableContentSwitch setEnabled:NO]; + break; + case SPTableTypeFunc: + tableType = NSLocalizedString(@"function",@"function"); + [copyTableContentSwitch setEnabled:NO]; + break; + } + + [copyTableMessageField setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Duplicate %@ '%@' to:", @"duplicate object message"), tableType, [self tableName]]]; + + //open copyTableSheet + [copyTableNameField setStringValue:[NSString stringWithFormat:@"%@_copy", [filteredTables objectAtIndex:[tablesListView selectedRow]]]]; + [copyTableContentSwitch setState:NSOffState]; + + [NSApp beginSheet:copyTableSheet + modalForWindow:[tableDocumentInstance parentWindow] + modalDelegate:self + didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) + contextInfo:@"copyTable"]; +} + +/** + * This action starts editing the table name in the table list + */ +- (IBAction)renameTable:(id)sender +{ + if ((![tableSourceInstance saveRowOnDeselect]) || (![tableContentInstance saveRowOnDeselect]) || (![tableDocumentInstance database])) { + return; + } + + [[tableDocumentInstance parentWindow] endEditingFor:nil]; + + if ([tablesListView numberOfSelectedRows] != 1) return; + if (![[self tableName] length]) return; + + [tablesListView editColumn:0 row:[tablesListView selectedRow] withEvent:nil select:YES]; + + /* + + [tableRenameField setStringValue:[self tableName]]; + [renameTableButton setEnabled:NO]; + + NSString *tableType; + + switch([self tableType]){ + case SPTableTypeTable: + tableType = NSLocalizedString(@"table",@"table"); + break; + case SPTableTypeView: + tableType = NSLocalizedString(@"view",@"view"); + break; + case SPTableTypeProc: + tableType = NSLocalizedString(@"procedure",@"procedure"); + break; + case SPTableTypeFunc: + tableType = NSLocalizedString(@"function",@"function"); + break; + } + + [tableRenameText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Rename %@ '%@' to:",@"rename item name to:"), tableType, [self tableName]]]; + + + [NSApp beginSheet:tableRenameSheet + modalForWindow:[tableDocumentInstance parentWindow] + modalDelegate:self + didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) + contextInfo:@"renameTable"]; + */ +} + +/** + * Truncates the currently selected table(s). + */ +- (IBAction)truncateTable:(id)sender +{ + if (![tablesListView numberOfSelectedRows]) + return; + + [[tableDocumentInstance parentWindow] endEditingFor:nil]; + + NSAlert *alert = [NSAlert alertWithMessageText:@"" + defaultButton:NSLocalizedString(@"Truncate", @"truncate button") + alternateButton:NSLocalizedString(@"Cancel", @"cancel button") + otherButton:nil + informativeTextWithFormat:@""]; + + [alert setAlertStyle:NSCriticalAlertStyle]; + + NSArray *buttons = [alert buttons]; + + // Change the alert's cancel button to have the key equivalent of return + [[buttons objectAtIndex:0] setKeyEquivalent:@"t"]; + [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; + [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; + + if ([tablesListView numberOfSelectedRows] == 1) { + [alert setMessageText:[NSString stringWithFormat:NSLocalizedString(@"Truncate table '%@'?", @"truncate table message"), [filteredTables objectAtIndex:[tablesListView selectedRow]]]]; + [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete ALL records in the table '%@'? This operation cannot be undone.", @"truncate table informative message"), [filteredTables objectAtIndex:[tablesListView selectedRow]]]]; + } + else { + [alert setMessageText:NSLocalizedString(@"Truncate selected tables?", @"truncate tables message")]; + [alert setInformativeText:NSLocalizedString(@"Are you sure you want to delete ALL records in the selected tables? This operation cannot be undone.", @"truncate tables informative message")]; + } + + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"truncateTable"]; +} + +/** + * Toggle whether the splitview is collapsed. + */ +- (IBAction)togglePaneCollapse:(id)sender +{ + [tableListSplitView toggleCollapse:sender]; + [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:([tableInfoCollapseButton state] == NSOffState)] forKey:SPTableInformationPanelCollapsed]; + [tableInfoCollapseButton setToolTip:([tableInfoCollapseButton state] == NSOffState) ? NSLocalizedString(@"Show Table Information", @"Show Table Information") : NSLocalizedString(@"Hide Table Information", @"Hide Table Information")]; +} + +#pragma mark - +#pragma mark Alert sheet methods + +/** + * Method for alert sheets. + */ +- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo +{ + // Order out current sheet to suppress overlapping of sheets + if ([sheet respondsToSelector:@selector(orderOut:)]) + [sheet orderOut:nil]; + else if ([sheet respondsToSelector:@selector(window)]) + [[sheet window] orderOut:nil]; + + if ([contextInfo isEqualToString:@"addRow"]) { + alertSheetOpened = NO; + } + else if ([contextInfo isEqualToString:@"removeRow"]) { + if (returnCode == NSAlertDefaultReturn) { + [self performSelector:@selector(removeTable) withObject:nil afterDelay:0.0]; + } + } + else if ([contextInfo isEqualToString:@"truncateTable"]) { + if (returnCode == NSAlertDefaultReturn) { + [self truncateTable]; + } + } + else if ([contextInfo isEqualToString:@"addTable"]) { + if (returnCode == NSOKButton) { + [self addTable]; + } + } + else if ([contextInfo isEqualToString:@"copyTable"]) { + if (returnCode == NSOKButton) { + [self copyTable]; + } + } +} + +#pragma mark - +#pragma mark Additional methods + +/** + * Sets the connection (received from TableDocument) and makes things that have to be done only once + */ +- (void)setConnection:(MCPConnection *)theConnection +{ + mySQLConnection = theConnection; + [self updateTables:self]; +} + +/** + * Selects customQuery tab and passes query to customQueryInstance + */ +- (void)doPerformQueryService:(NSString *)query +{ + [tabView selectTabViewItemAtIndex:2]; + [customQueryInstance doPerformQueryService:query]; +} + +/** + * Performs interface validation for various controls. + */ +- (void)controlTextDidChange:(NSNotification *)notification +{ + id object = [notification object]; + + if (object == tableNameField) { + [addTableButton setEnabled:[self isTableNameValid:[tableNameField stringValue] forType: SPTableTypeTable]]; + } + + else if (object == copyTableNameField) { + [copyTableButton setEnabled:[self isTableNameValid:[copyTableNameField stringValue] forType:[self tableType]]]; + } +} + +/* + * Controls the NSTextField's press RETURN event of Add/Rename/Duplicate sheets + */ +- (void)controlTextDidEndEditing:(NSNotification *)notification +{ + id object = [notification object]; + + // Only RETURN/ENTER will be recognized for Add/Rename/Duplicate sheets to + // activate the Add/Rename/Duplicate buttons + if([[[notification userInfo] objectForKey:@"NSTextMovement"] integerValue] != 0) + return; + + if (object == tableNameField) { + [addTableButton performClick:object]; + } + else if (object == copyTableNameField) { + [copyTableButton performClick:object]; + } +} + +/** + * Updates the current table selection. Triggered most times tableViewSelectionDidChange: + * fires, and also as a result of certain table actions. + */ +- (void)updateSelectionWithTaskString:(NSString *)taskString +{ + if (![mySQLConnection isConnected]) return; + + // If there is a multiple or blank selection, clear all views directly. + if ( [tablesListView numberOfSelectedRows] != 1 || ![(NSString *)[filteredTables objectAtIndex:[tablesListView selectedRow]] length] ) { + + // Update the selection variables and the interface + [self performSelectorOnMainThread:@selector(setSelection:) withObject:nil waitUntilDone:YES]; + + // Add a history entry + [spHistoryControllerInstance updateHistoryEntries]; + + // Notify listeners of the table change now that the state is fully set up + [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPTableChangedNotification object:tableDocumentInstance]; + + return; + } + + // Otherwise, set up a task + [tableDocumentInstance startTaskWithDescription:taskString]; + + // If on the main thread, fire up a thread to deal with view changes and data loading, else perform inline + if ([NSThread isMainThread]) { + [NSThread detachNewThreadSelector:@selector(updateSelectionTask) toTarget:self withObject:nil]; + } else { + [self updateSelectionTask]; + } +} + +- (void) updateSelectionTask +{ + NSAutoreleasePool *selectionChangePool = [[NSAutoreleasePool alloc] init]; + NSString *tableEncoding = nil; + + // Update selection variables and interface + NSDictionary *selectionDetails = [NSDictionary dictionaryWithObjectsAndKeys: + [filteredTables objectAtIndex:[tablesListView selectedRow]], @"name", + [filteredTableTypes objectAtIndex:[tablesListView selectedRow]], @"type", + nil]; + [self performSelectorOnMainThread:@selector(setSelection:) withObject:selectionDetails waitUntilDone:YES]; + + // Check the encoding if appropriate to determine if an encoding change and reset is required + if( selectedTableType == SPTableTypeView || selectedTableType == SPTableTypeTable) { + + // tableEncoding == nil indicates that there was an error while retrieving table data + tableEncoding = [tableDataInstance tableEncoding]; + + // If encoding is set to Autodetect, update the connection character set encoding + // based on the newly selected table's encoding - but only if it differs from the current encoding. + if ([[[NSUserDefaults standardUserDefaults] objectForKey:SPDefaultEncoding] isEqualToString:@"Autodetect"]) { + if (tableEncoding != nil && ![tableEncoding isEqualToString:[tableDocumentInstance connectionEncoding]]) { + [tableDocumentInstance setConnectionEncoding:tableEncoding reloadingViews:NO]; + [tableDataInstance resetAllData]; + tableEncoding = [tableDataInstance tableEncoding]; + } + } + } + + // Ensure status information is cached on the working thread + [tableDataInstance updateStatusInformationForCurrentTable]; + + // Notify listeners of the table change now that the state is fully set up. + [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPTableChangedNotification object:tableDocumentInstance]; + + // Restore view states as appropriate + [spHistoryControllerInstance restoreViewStates]; + + structureLoaded = NO; + contentLoaded = NO; + statusLoaded = NO; + triggersLoaded = NO; + if( selectedTableType == SPTableTypeView || selectedTableType == SPTableTypeTable) { + if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewStructure ) { + [tableSourceInstance loadTable:selectedTableName]; + structureLoaded = YES; + } else if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewContent ) { + if(tableEncoding == nil) { + [tableContentInstance loadTable:nil]; + } else { + [tableContentInstance loadTable:selectedTableName]; + } + contentLoaded = YES; + } else if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewStatus ) { + [[extendedTableInfoInstance onMainThread] loadTable:selectedTableName]; + statusLoaded = YES; + } else if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewTriggers ) { + [[tableTriggersInstance onMainThread] loadTriggers]; + triggersLoaded = YES; + } + } else { + + // if we are not looking at a table or view, clear these + [tableSourceInstance loadTable:nil]; + [tableContentInstance loadTable:nil]; + [[extendedTableInfoInstance onMainThread] loadTable:nil]; + [[tableTriggersInstance onMainThread] loadTriggers]; + } + + // Update the "Show Create Syntax" window if it's already opened + // according to the selected table/view/proc/func + if([[tableDocumentInstance getCreateTableSyntaxWindow] isVisible]) + [tableDocumentInstance performSelectorOnMainThread:@selector(showCreateTableSyntax:) withObject:self waitUntilDone:YES]; + + // Add a history entry + [spHistoryControllerInstance updateHistoryEntries]; + + // Empty the loading pool and exit the thread + [tableDocumentInstance endTask]; + [selectionChangePool drain]; +} + +/** + * Takes a dictionary of selection details, containing the selection name + * and type, and updates stored variables and the table list interface to + * match. + * Should be called on the main thread. + */ +- (void)setSelection:(NSDictionary *)selectionDetails +{ + // First handle empty or multiple selections + if (!selectionDetails || ![selectionDetails objectForKey:@"name"]) { + NSIndexSet *indexes = [tablesListView selectedRowIndexes]; + + // Update the selected table name and type + if (selectedTableName) [selectedTableName release]; + + if ([indexes count]) { + selectedTableName = [[NSString alloc] initWithString:@""]; + } + else { + selectedTableName = nil; + } + + selectedTableType = SPTableTypeNone; + + [tableSourceInstance loadTable:nil]; + [tableContentInstance loadTable:nil]; + [extendedTableInfoInstance loadTable:nil]; + [tableTriggersInstance loadTriggers]; + + structureLoaded = NO; + contentLoaded = NO; + statusLoaded = NO; + triggersLoaded = NO; + + // Set gear menu items Remove/Duplicate table/view according to the table types + // if at least one item is selected + if ([indexes count]) { + + NSUInteger currentIndex = [indexes lastIndex]; + BOOL areTableTypeEqual = YES; + NSInteger lastType = [[filteredTableTypes objectAtIndex:currentIndex] integerValue]; + + while (currentIndex != NSNotFound) + { + if ([[filteredTableTypes objectAtIndex:currentIndex] integerValue] != lastType) { + areTableTypeEqual = NO; + break; + } + + currentIndex = [indexes indexLessThanIndex:currentIndex]; + } + + if (areTableTypeEqual) { + switch (lastType) { + case SPTableTypeTable: + [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Tables", @"delete tables menu title")]; + [truncateTableButton setTitle:NSLocalizedString(@"Truncate Tables", @"truncate tables menu item")]; + [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Tables", @"delete tables menu title")]; + [truncateTableContextButton setTitle:NSLocalizedString(@"Truncate Tables", @"truncate tables menu item")]; + [truncateTableButton setHidden:NO]; + [truncateTableContextButton setHidden:NO]; + break; + case SPTableTypeView: + [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Views", @"delete views menu title")]; + [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Views", @"delete views menu title")]; + [truncateTableButton setHidden:YES]; + [truncateTableContextButton setHidden:YES]; + break; + case SPTableTypeProc: + [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Procedures", @"delete procedures menu title")]; + [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Procedures", @"delete procedures menu title")]; + [truncateTableButton setHidden:YES]; + [truncateTableContextButton setHidden:YES]; + break; + case SPTableTypeFunc: + [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Functions", @"delete functions menu title")]; + [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Functions", @"delete functions menu title")]; + [truncateTableButton setHidden:YES]; + [truncateTableContextButton setHidden:YES]; + break; + } + + } else { + [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Items", @"delete items menu title")]; + [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Items", @"delete items menu title")]; + [truncateTableButton setHidden:YES]; + [truncateTableContextButton setHidden:YES]; + } + } + + // Context menu + [renameTableContextMenuItem setHidden:YES]; + [duplicateTableContextMenuItem setHidden:YES]; + [separatorTableContextMenuItem setHidden:YES]; + [separatorTableContextMenuItem2 setHidden:YES]; + [showCreateSyntaxContextMenuItem setHidden:YES]; + + // 'Gear' menu + [renameTableMenuItem setHidden:YES]; + [duplicateTableMenuItem setHidden:YES]; + [separatorTableMenuItem setHidden:YES]; + [separatorTableMenuItem2 setHidden:YES]; + [showCreateSyntaxMenuItem setHidden:YES]; + + NSMenu *tableSubMenu = [[[NSApp mainMenu] itemWithTitle:@"Table"] submenu]; + + [[tableSubMenu itemAtIndex:6] setTitle:NSLocalizedString(@"Check Selected Items", @"check selected items menu item")]; + [[tableSubMenu itemAtIndex:7] setTitle:NSLocalizedString(@"Repair Selected Items", @"repair selected items menu item")]; + + [[tableSubMenu itemAtIndex:9] setTitle:NSLocalizedString(@"Analyze Selected Items", @"analyze selected items menu item")]; + [[tableSubMenu itemAtIndex:10] setTitle:NSLocalizedString(@"Optimize Selected Items", @"optimize selected items menu item")]; + + [[tableSubMenu itemAtIndex:11] setTitle:NSLocalizedString(@"Flush Selected Items", @"flush selected items menu item")]; + [[tableSubMenu itemAtIndex:12] setTitle:NSLocalizedString(@"Checksum Selected Items", @"checksum selected items menu item")]; + + [[tableSubMenu itemAtIndex:3] setHidden:NO]; + [[tableSubMenu itemAtIndex:4] setHidden:NO]; + [[tableSubMenu itemAtIndex:5] setHidden:NO]; + [[tableSubMenu itemAtIndex:6] setHidden:NO]; + [[tableSubMenu itemAtIndex:7] setHidden:NO]; + [[tableSubMenu itemAtIndex:8] setHidden:NO]; + [[tableSubMenu itemAtIndex:9] setHidden:NO]; + [[tableSubMenu itemAtIndex:10] setHidden:NO]; + + // set window title + [tableDocumentInstance updateWindowTitle:self]; + + return; + } + + // If a new selection has been provided, store variables and update the interface to match + NSString *selectedItemName = [selectionDetails objectForKey:@"name"]; + NSInteger selectedItemType = [[selectionDetails objectForKey:@"type"] integerValue]; + + // Update the selected table name and type + if (selectedTableName) [selectedTableName release]; + selectedTableName = [[NSString alloc] initWithString:selectedItemName]; + selectedTableType = selectedItemType; + + // Remove the "current selection" item for filtered lists if appropriate + if (isTableListFiltered && [tablesListView selectedRow] < [filteredTables count] - 2 && [filteredTables count] > 2 + && [[filteredTableTypes objectAtIndex:[filteredTableTypes count]-2] integerValue] == SPTableTypeNone + && [[filteredTables objectAtIndex:[filteredTables count]-2] isEqualToString:NSLocalizedString(@"CURRENT SELECTION",@"header for current selection in filtered list")]) + { + [filteredTables removeObjectsInRange:NSMakeRange([filteredTables count]-2, 2)]; + [filteredTableTypes removeObjectsInRange:NSMakeRange([filteredTableTypes count]-2, 2)]; + [tablesListView reloadData]; + } + + // Reset the table information caches + [tableDataInstance resetAllData]; + + // Show menu separatoes + [separatorTableMenuItem setHidden:NO]; + [separatorTableContextMenuItem setHidden:NO]; + [separatorTableMenuItem2 setHidden:NO]; + [separatorTableContextMenuItem2 setHidden:NO]; + + // Set gear menu items Remove/Duplicate table/view and mainMenu > Table items + // according to the table types + NSMenu *tableSubMenu = [[[NSApp mainMenu] itemWithTitle:@"Table"] submenu]; + + // Enable/disable the various menu items depending on the selected item. Also update their titles. + // Note, that this should ideally be moved to menu item validation as opposed to using fixed item positions. + if (selectedTableType == SPTableTypeView) + { + // Change mainMenu > Table > ... according to table type + [[tableSubMenu itemAtIndex:3] setTitle:NSLocalizedString(@"Copy Create View Syntax", @"copy create view syntax menu item")]; + [[tableSubMenu itemAtIndex:4] setTitle:NSLocalizedString(@"Show Create View Syntax...", @"show create view syntax menu item")]; + [[tableSubMenu itemAtIndex:5] setHidden:NO]; // Divider + [[tableSubMenu itemAtIndex:6] setHidden:NO]; + [[tableSubMenu itemAtIndex:6] setTitle:NSLocalizedString(@"Check View", @"check view menu item")]; + [[tableSubMenu itemAtIndex:7] setHidden:YES]; // Repair + [[tableSubMenu itemAtIndex:8] setHidden:YES]; // Divider + [[tableSubMenu itemAtIndex:9] setHidden:YES]; // Analyse + [[tableSubMenu itemAtIndex:10] setHidden:YES]; // Optimize + [[tableSubMenu itemAtIndex:11] setHidden:NO]; + [[tableSubMenu itemAtIndex:11] setTitle:NSLocalizedString(@"Flush View", @"flush view menu item")]; + [[tableSubMenu itemAtIndex:12] setHidden:YES]; // Checksum + + [renameTableMenuItem setHidden:NO]; // we don't have to check the mysql version + [renameTableMenuItem setTitle:NSLocalizedString(@"Rename View...", @"rename view menu title")]; + [duplicateTableMenuItem setHidden:NO]; + [duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate View...", @"duplicate view menu title")]; + [truncateTableButton setHidden:YES]; + [removeTableMenuItem setTitle:NSLocalizedString(@"Delete View", @"delete view menu title")]; + [showCreateSyntaxMenuItem setHidden:NO]; + [showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create View Syntax...", @"show create view syntax menu item")]; + + [renameTableContextMenuItem setHidden:NO]; // we don't have to check the mysql version + [renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename View...", @"rename view menu title")]; + [duplicateTableContextMenuItem setHidden:NO]; + [duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate View...", @"duplicate view menu title")]; + [truncateTableContextButton setHidden:YES]; + [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete View", @"delete view menu title")]; + [showCreateSyntaxContextMenuItem setHidden:NO]; + [showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create View Syntax...", @"show create view syntax menu item")]; + } + else if (selectedTableType == SPTableTypeTable) { + [[tableSubMenu itemAtIndex:3] setTitle:NSLocalizedString(@"Copy Create Table Syntax", @"copy create table syntax menu item")]; + [[tableSubMenu itemAtIndex:4] setTitle:NSLocalizedString(@"Show Create Table Syntax...", @"show create table syntax menu item")]; + [[tableSubMenu itemAtIndex:5] setHidden:NO]; // divider + [[tableSubMenu itemAtIndex:6] setHidden:NO]; + [[tableSubMenu itemAtIndex:6] setTitle:NSLocalizedString(@"Check Table", @"check table menu item")]; + [[tableSubMenu itemAtIndex:7] setHidden:NO]; + [[tableSubMenu itemAtIndex:7] setTitle:NSLocalizedString(@"Repair Table", @"repair table menu item")]; + [[tableSubMenu itemAtIndex:8] setHidden:NO]; // divider + [[tableSubMenu itemAtIndex:9] setHidden:NO]; + [[tableSubMenu itemAtIndex:9] setTitle:NSLocalizedString(@"Analyze Table", @"analyze table menu item")]; + [[tableSubMenu itemAtIndex:10] setHidden:NO]; + [[tableSubMenu itemAtIndex:10] setTitle:NSLocalizedString(@"Optimize Table", @"optimize table menu item")]; + [[tableSubMenu itemAtIndex:11] setHidden:NO]; + [[tableSubMenu itemAtIndex:11] setTitle:NSLocalizedString(@"Flush Table", @"flush table menu item")]; + [[tableSubMenu itemAtIndex:12] setHidden:NO]; + [[tableSubMenu itemAtIndex:12] setTitle:NSLocalizedString(@"Checksum Table", @"checksum table menu item")]; + + [renameTableMenuItem setHidden:NO]; + [renameTableMenuItem setTitle:NSLocalizedString(@"Rename Table...", @"rename table menu title")]; + [duplicateTableMenuItem setHidden:NO]; + [duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate Table...", @"duplicate table menu title")]; + [truncateTableButton setHidden:NO]; + [truncateTableButton setTitle:NSLocalizedString(@"Truncate Table", @"truncate table menu title")]; + [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Table", @"delete table menu title")]; + [showCreateSyntaxMenuItem setHidden:NO]; + [showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create Table Syntax...", @"show create table syntax menu item")]; + + [renameTableContextMenuItem setHidden:NO]; + [renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename Table...", @"rename table menu title")]; + [duplicateTableContextMenuItem setHidden:NO]; + [duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate Table...", @"duplicate table menu title")]; + [truncateTableContextButton setHidden:NO]; + [truncateTableContextButton setTitle:NSLocalizedString(@"Truncate Table", @"truncate table menu title")]; + [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Table", @"delete table menu title")]; + [showCreateSyntaxContextMenuItem setHidden:NO]; + [showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create Table Syntax...", @"show create table syntax menu item")]; + } + else if (selectedTableType == SPTableTypeProc) { + [[tableSubMenu itemAtIndex:3] setTitle:NSLocalizedString(@"Copy Create Procedure Syntax", @"copy create proc syntax menu item")]; + [[tableSubMenu itemAtIndex:4] setTitle:NSLocalizedString(@"Show Create Procedure Syntax...", @"show create proc syntax menu item")]; + [[tableSubMenu itemAtIndex:5] setHidden:YES]; // divider + [[tableSubMenu itemAtIndex:6] setHidden:YES]; // copy columns + [[tableSubMenu itemAtIndex:7] setHidden:YES]; // divider + [[tableSubMenu itemAtIndex:8] setHidden:YES]; + [[tableSubMenu itemAtIndex:9] setHidden:YES]; + [[tableSubMenu itemAtIndex:10] setHidden:YES]; // divider + [[tableSubMenu itemAtIndex:11] setHidden:YES]; + [[tableSubMenu itemAtIndex:12] setHidden:YES]; + + [renameTableMenuItem setHidden:NO]; + [renameTableMenuItem setTitle:NSLocalizedString(@"Rename Procedure...", @"rename proc menu title")]; + [duplicateTableMenuItem setHidden:NO]; + [duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate Procedure...", @"duplicate proc menu title")]; + [truncateTableButton setHidden:YES]; + [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Procedure", @"delete proc menu title")]; + [showCreateSyntaxMenuItem setHidden:NO]; + [showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create Procedure Syntax...", @"show create proc syntax menu item")]; + + [renameTableContextMenuItem setHidden:NO]; + [renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename Procedure...", @"rename proc menu title")]; + [duplicateTableContextMenuItem setHidden:NO]; + [duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate Procedure...", @"duplicate proc menu title")]; + [truncateTableContextButton setHidden:YES]; + [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Procedure", @"delete proc menu title")]; + [showCreateSyntaxContextMenuItem setHidden:NO]; + [showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create Procedure Syntax...", @"show create proc syntax menu item")]; + } + else if (selectedTableType == SPTableTypeFunc) { + [[tableSubMenu itemAtIndex:3] setTitle:NSLocalizedString(@"Copy Create Function Syntax", @"copy create func syntax menu item")]; + [[tableSubMenu itemAtIndex:4] setTitle:NSLocalizedString(@"Show Create Function Syntax...", @"show create func syntax menu item")]; + [[tableSubMenu itemAtIndex:5] setHidden:YES]; // divider + [[tableSubMenu itemAtIndex:6] setHidden:YES]; // copy columns + [[tableSubMenu itemAtIndex:7] setHidden:YES]; // divider + [[tableSubMenu itemAtIndex:8] setHidden:YES]; + [[tableSubMenu itemAtIndex:9] setHidden:YES]; + [[tableSubMenu itemAtIndex:10] setHidden:YES]; // divider + [[tableSubMenu itemAtIndex:11] setHidden:YES]; + [[tableSubMenu itemAtIndex:12] setHidden:YES]; + + [renameTableMenuItem setHidden:NO]; + [renameTableMenuItem setTitle:NSLocalizedString(@"Rename Function...", @"rename func menu title")]; + [duplicateTableMenuItem setHidden:NO]; + [duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate Function...", @"duplicate func menu title")]; + [truncateTableButton setHidden:YES]; + [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Function", @"delete func menu title")]; + [showCreateSyntaxMenuItem setHidden:NO]; + [showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create Function Syntax...", @"show create func syntax menu item")]; + + [renameTableContextMenuItem setHidden:NO]; + [renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename Function...", @"rename func menu title")]; + [duplicateTableContextMenuItem setHidden:NO]; + [duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate Function...", @"duplicate func menu title")]; + [truncateTableContextButton setHidden:YES]; + [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Function", @"delete func menu title")]; + [showCreateSyntaxContextMenuItem setHidden:NO]; + [showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create Function Syntax...", @"show create func syntax menu item")]; + } + + // set window title + [tableDocumentInstance updateWindowTitle:self]; +} + +#pragma mark - +#pragma mark Getter methods + + +- (NSArray *)selectedTableNames +{ + NSIndexSet *indexes = [tablesListView selectedRowIndexes]; + + NSUInteger currentIndex = [indexes firstIndex]; + NSMutableArray *selTables = [NSMutableArray array]; + + while (currentIndex != NSNotFound) { + if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeTable) + [selTables addObject:[filteredTables objectAtIndex:currentIndex]]; + currentIndex = [indexes indexGreaterThanIndex:currentIndex]; + } + return selTables; +} + +- (NSArray *)selectedTableItems +{ + NSIndexSet *indexes = [tablesListView selectedRowIndexes]; + + NSUInteger currentIndex = [indexes firstIndex]; + NSMutableArray *selTables = [NSMutableArray array]; + + while (currentIndex != NSNotFound) { + [selTables addObject:[filteredTables objectAtIndex:currentIndex]]; + currentIndex = [indexes indexGreaterThanIndex:currentIndex]; + } + return selTables; +} + +/** + * Returns the currently selected table or nil if no table or mulitple tables are selected + */ +- (NSString *)tableName +{ + return selectedTableName; +} + +/* + * Returns the currently selected table type, or -1 if no table or multiple tables are selected + */ +- (NSInteger) tableType +{ + return selectedTableType; +} + +/** + * Database tables accessor + */ +- (NSArray *)tables +{ + return tables; +} + +/** + * Database tables accessors for a given table type + */ +- (NSArray *)allTableAndViewNames +{ + NSMutableArray *returnArray = [NSMutableArray array]; + NSInteger i; + NSInteger cnt = [[self tables] count]; + for(i=0; i= [filteredTables count]) return @""; + + return [filteredTables objectAtIndex:rowIndex]; +} + +/** + * Prevent table renames while tasks are active + */ +- (BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex +{ + return ![tableDocumentInstance isWorking]; +} + +/** + * Renames a table (in tables-array and mysql-db). + */ +- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex +{ + //first trim whitespace whitespace + NSString *newTableName = [anObject stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + if ([selectedTableName isEqualToString:newTableName]) { + // No changes in table name + return; + } + + if ([newTableName isEqualToString:@""]) { + // empty table names are not allowed + // don't annoy the user about it, just ignore this + // this is also how the MacOS Finder handles renaming files + return; + } + + if (![self isTableNameValid:newTableName forType:selectedTableType ignoringSelectedTable:YES]) { + // Table has invalid name + // Since we trimmed whitespace and checked for empty string, this means there is already a table with that name + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, + @selector(sheetDidEnd:returnCode:contextInfo:), nil, + [NSString stringWithFormat: NSLocalizedString(@"The name '%@' is already used.", @"message when trying to rename a table/view/proc/etc to an already used name"), newTableName]); + return; + } + + @try { + // first: update the database + [self renameTableOfType:selectedTableType from:selectedTableName to:newTableName]; + + // second: update the table list + if (isTableListFiltered) { + NSInteger unfilteredIndex = [tables indexOfObject:[filteredTables objectAtIndex:rowIndex]]; + [tables replaceObjectAtIndex:unfilteredIndex withObject:newTableName]; + } + [filteredTables replaceObjectAtIndex:rowIndex withObject:newTableName]; + if (selectedTableName) [selectedTableName release]; + selectedTableName = [[NSString alloc] initWithString:newTableName]; + + // if the 'table' is a view or a table, reload the currently selected view + if (selectedTableType == SPTableTypeTable || selectedTableType == SPTableTypeView) + { + statusLoaded = NO; + structureLoaded = NO; + contentLoaded = NO; + triggersLoaded = NO; + switch ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]]) { + case SPTableViewStructure: + [tableSourceInstance loadTable:newTableName]; + structureLoaded = YES; + break; + case SPTableViewContent: + [tableContentInstance loadTable:newTableName]; + contentLoaded = YES; + break; + case SPTableViewStatus: + [extendedTableInfoInstance loadTable:newTableName]; + statusLoaded = YES; + break; + case SPTableViewTriggers: + [tableTriggersInstance loadTriggers]; + triggersLoaded = YES; + break; + } + } + } + @catch (NSException * myException) { + SPBeginAlertSheet( NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, [myException reason]); + } + + // Set window title to reflect the new table name + [tableDocumentInstance updateWindowTitle:self]; + + // Query the structure of all databases in the background (mainly for completion) + [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", nil]]; +} + +#pragma mark - +#pragma mark TableView delegate methods + +/** + * Traps enter and esc and edit/cancel without entering next row + */ +- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command +{ + if ( [textView methodForSelector:command] == [textView methodForSelector:@selector(insertNewline:)] ) { + //save current line + [[control window] makeFirstResponder:control]; + return TRUE; + + } else if ( [[control window] methodForSelector:command] == [[control window] methodForSelector:@selector(_cancelKey:)] || + [textView methodForSelector:command] == [textView methodForSelector:@selector(complete:)] ) { + + //abort editing + [control abortEditing]; + + return TRUE; + } else{ + return FALSE; + } +} + +/** + * Table view delegate method + */ +- (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView +{ + + // Don't allow selection changes while performing a task. + if (!tableListIsSelectable) return NO; + + // End editing (otherwise problems when user hits reload button) + [[tableDocumentInstance parentWindow] endEditingFor:nil]; + + if ( alertSheetOpened ) { + return NO; + } + + // We have to be sure that document views have finished editing + return [tableDocumentInstance couldCommitCurrentViewActions]; +} + +/** + * Loads a table in content or source view (if tab selected) + */ +- (void)tableViewSelectionDidChange:(NSNotification *)aNotification +{ + + // Reset selectability after change if necessary + if ([tableDocumentInstance isWorking]) tableListIsSelectable = NO; + + // Perform no action if the selected table hasn't actually changed - reselection etc + if ([tablesListView numberOfSelectedRows] == 1 + && [(NSString *)[filteredTables objectAtIndex:[tablesListView selectedRow]] length] + && [selectedTableName isEqualToString:[filteredTables objectAtIndex:[tablesListView selectedRow]]] + && selectedTableType == [[filteredTableTypes objectAtIndex:[tablesListView selectedRow]] integerValue]) + { + return; + } + + // Save existing scroll position and details + [spHistoryControllerInstance updateHistoryEntries]; + + NSString *tableName = @"data"; + if ([tablesListView numberOfSelectedRows] == 1 && [(NSString *)[filteredTables objectAtIndex:[tablesListView selectedRow]] length]) + tableName = [filteredTables objectAtIndex:[tablesListView selectedRow]]; + [self updateSelectionWithTaskString:[NSString stringWithFormat:NSLocalizedString(@"Loading %@...", @"Loading table task string"), tableName]]; + + if([[SPNavigatorController sharedNavigatorController] syncMode] && [tablesListView numberOfSelectedRows] == 1) { + NSMutableString *schemaPath = [NSMutableString string]; + [schemaPath setString:[tableDocumentInstance connectionID]]; + if([tableDocumentInstance database] && [[tableDocumentInstance database] length]) { + [schemaPath appendString:SPUniqueSchemaDelimiter]; + [schemaPath appendString:[tableDocumentInstance database]]; + if(tableName && [tableName length]) { + [schemaPath appendString:SPUniqueSchemaDelimiter]; + [schemaPath appendString:tableName]; + } + } + [[SPNavigatorController sharedNavigatorController] selectPath:schemaPath]; + } +} + +/** + * Table view delegate method + */ +- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex +{ + + // Disallow selection while the document is working on a task + if ([tableDocumentInstance isWorking]) return NO; + + //return (rowIndex != 0); + if( [filteredTableTypes count] == 0 ) + return (rowIndex != 0 ); + return ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] != SPTableTypeNone ); +} + +/** + * Table view delegate method + */ +- (BOOL)tableView:(NSTableView *)aTableView isGroupRow:(NSInteger)rowIndex +{ + // For empty tables - title still present - or while lists are being altered + if (rowIndex >= [filteredTableTypes count]) return (rowIndex == 0 ); + + return ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeNone ); +} + +/** + * Table view delegate method + */ +- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex +{ + if (rowIndex > 0 && rowIndex < [filteredTableTypes count] + && [[aTableColumn identifier] isEqualToString:@"tables"]) { + if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeView) { + [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"table-view-small"]]; + } else if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeTable) { + [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"table-small"]]; + } else if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeProc) { + [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"proc-small"]]; + } else if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeFunc) { + [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"func-small"]]; + } + + if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeNone) { + [(ImageAndTextCell*)aCell setImage:nil]; + [(ImageAndTextCell*)aCell setIndentationLevel:0]; + } else { + [(ImageAndTextCell*)aCell setIndentationLevel:1]; + [(ImageAndTextCell*)aCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + } + } else { + [(ImageAndTextCell*)aCell setImage:nil]; + [(ImageAndTextCell*)aCell setIndentationLevel:0]; + } +} + +/** + * Table view delegate method + */ +- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row +{ + return (row == 0) ? 25 : 17; +} + +#pragma mark - +#pragma mark TabView delegate methods + +/** + * Loads structure or source if tab selected the first time, + * using a threaded load if currently on the main thread. + */ +- (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem +{ + [tableDocumentInstance startTaskWithDescription:[NSString stringWithFormat:NSLocalizedString(@"Loading %@...", @"Loading table task string"), selectedTableName]]; + if ([NSThread isMainThread]) { + [NSThread detachNewThreadSelector:@selector(loadTabTask:) toTarget:self withObject:tabViewItem]; + } else { + [self loadTabTask:tabViewItem]; + } +} +- (void)loadTabTask:(NSTabViewItem *)tabViewItem +{ + NSAutoreleasePool *tabLoadPool = [[NSAutoreleasePool alloc] init]; + + if ([tablesListView numberOfSelectedRows] == 1 + && ([self tableType] == SPTableTypeTable || [self tableType] == SPTableTypeView) ) + { + + if ( ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewStructure) && !structureLoaded ) { + [tableSourceInstance loadTable:selectedTableName]; + structureLoaded = YES; + } + + if ( ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewContent) && !contentLoaded ) { + [tableContentInstance loadTable:selectedTableName]; + contentLoaded = YES; + } + + if ( ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewStatus) && !statusLoaded ) { + [[extendedTableInfoInstance onMainThread] loadTable:selectedTableName]; + statusLoaded = YES; + } + + if ( ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewTriggers) && !triggersLoaded ) { + [[tableTriggersInstance onMainThread] loadTriggers]; + triggersLoaded = YES; + } + } + else { + [tableSourceInstance loadTable:nil]; + [tableContentInstance loadTable:nil]; + } + + [tableDocumentInstance endTask]; + [tabLoadPool drain]; +} + +/** + * Menu item interface validation + */ +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem +{ + // popup button below table list + if ([menuItem action] == @selector(copyTable:)) { + return (([tablesListView numberOfSelectedRows] == 1) && [[self tableName] length] && [tablesListView numberOfSelectedRows] > 0); + } + + if ([menuItem action] == @selector(removeTable:) || [menuItem action] == @selector(truncateTable:)) { + return ([tablesListView numberOfSelectedRows] > 0); + } + + if ([menuItem action] == @selector(renameTable:)) { + return (([tablesListView numberOfSelectedRows] == 1) && [[self tableName] length]); + } + + return [super validateMenuItem:menuItem]; +} + +#pragma mark - +#pragma mark Table list filter interaction + +/** + * Show the filter box if it's currently hidden. Use a delay to ensure + * action is executed on first load. + */ +- (void) showFilter +{ + if ([tableListFilterSplitView collapsibleSubviewIsCollapsed]) + [tableListFilterSplitView performSelectorOnMainThread:@selector(toggleCollapse:) withObject:nil waitUntilDone:NO]; +} + +/** + * Hide the filter box if it's currently shown. Use a delay to ensure + * action is executed on first load. + */ +- (void) hideFilter +{ + if (![tableListFilterSplitView collapsibleSubviewIsCollapsed]) + [tableListFilterSplitView performSelectorOnMainThread:@selector(toggleCollapse:) withObject:nil waitUntilDone:NO]; +} + +/** + * Clear the current content of the filter box + */ +- (void) clearFilter +{ + [listFilterField setStringValue:@""]; +} + +/** + * Set focus to table list filter search field + */ +- (void) makeTableListFilterHaveFocus +{ + if([tables count] > 20) { + [[tableDocumentInstance parentWindow] makeFirstResponder:listFilterField]; + } + else if([tables count] > 2) { + [[tableDocumentInstance parentWindow] makeFirstResponder:tablesListView]; + if([tablesListView numberOfSelectedRows] < 1) + [tablesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO]; + + } +} + +/** + * Update the filter search. + */ +- (IBAction) updateFilter:(id)sender +{ + + // Don't try and maintain selections of multiple rows through filtering + if ([tablesListView numberOfSelectedRows] > 1) { + [tablesListView deselectAll:self]; + if (selectedTableName) [selectedTableName release], selectedTableName = nil; + } + + if ([[listFilterField stringValue] length]) { + if (isTableListFiltered) { + [filteredTables release]; + [filteredTableTypes release]; + } + filteredTables = [[NSMutableArray alloc] init]; + filteredTableTypes = [[NSMutableArray alloc] init]; + + NSInteger i, lastTableType = NSNotFound, tableType; + NSRange substringRange; + for (i = 0; i < [tables count]; i++) { + tableType = [[tableTypes objectAtIndex:i] integerValue]; + if (tableType == SPTableTypeNone) continue; + substringRange = [[tables objectAtIndex:i] rangeOfString:[listFilterField stringValue] options:NSCaseInsensitiveSearch]; + if (substringRange.location == NSNotFound) continue; + + // Add a title if necessary + if ((tableType == SPTableTypeTable || tableType == SPTableTypeView) && lastTableType == NSNotFound) + { + if (tableListContainsViews) { + [filteredTables addObject:NSLocalizedString(@"TABLES & VIEWS",@"header for table & views list")]; + } else { + [filteredTables addObject:NSLocalizedString(@"TABLES",@"header for table list")]; + } + [filteredTableTypes addObject:[NSNumber numberWithInteger:SPTableTypeNone]]; + } else if ((tableType == SPTableTypeProc || tableType == SPTableTypeFunc) + && (lastTableType == NSNotFound || lastTableType == SPTableTypeTable || lastTableType == SPTableTypeView)) + { + [filteredTables addObject:NSLocalizedString(@"PROCS & FUNCS",@"header for procs & funcs list")]; + [filteredTableTypes addObject:[NSNumber numberWithInteger:SPTableTypeNone]]; + } + lastTableType = tableType; + + // Add the item + [filteredTables addObject:[tables objectAtIndex:i]]; + [filteredTableTypes addObject:[tableTypes objectAtIndex:i]]; + } + + // Add a "no matches" title if nothing matches the current filter settings + if (![filteredTables count]) { + [filteredTables addObject:NSLocalizedString(@"NO MATCHES",@"header for no matches in filtered list")]; + [filteredTableTypes addObject:[NSNumber numberWithInteger:SPTableTypeNone]]; + } + + // If the currently selected table isn't present in the filter list, add it as a special entry + if (selectedTableName && [filteredTables indexOfObject:selectedTableName] == NSNotFound) { + [filteredTables addObject:NSLocalizedString(@"CURRENT SELECTION",@"header for current selection in filtered list")]; + [filteredTableTypes addObject:[NSNumber numberWithInteger:SPTableTypeNone]]; + [filteredTables addObject:selectedTableName]; + [filteredTableTypes addObject:[NSNumber numberWithInteger:selectedTableType]]; + } + + isTableListFiltered = YES; + } else if (isTableListFiltered) { + isTableListFiltered = NO; + [filteredTables release]; + filteredTables = tables; + [filteredTableTypes release]; + filteredTableTypes = tableTypes; + } + + // Reselect correct row and reload the table view display + if ([tablesListView numberOfRows] < [filteredTables count]) [tablesListView noteNumberOfRowsChanged]; + if (selectedTableName) [tablesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:[filteredTables indexOfObject:selectedTableName]] byExtendingSelection:NO]; + [tablesListView reloadData]; +} + +/** + * Select the supplied row index; added for convenience to allow + * use with performSelector:withObject:afterDelay: for re-selection. + */ +- (void) selectTableAtIndex:(NSNumber *)row +{ + NSInteger rowIndex = [row integerValue]; + if (rowIndex == NSNotFound || rowIndex > [filteredTables count] || [[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeNone) + return; + + [tablesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:rowIndex] byExtendingSelection:NO]; +} + +#pragma mark - +#pragma mark Task interaction + +/** + * Disable all table list interactive elements during an ongoing task. + */ +- (void) startDocumentTaskForTab:(NSNotification *)aNotification +{ + tableListIsSelectable = NO; + [toolbarAddButton setEnabled:NO]; + [toolbarActionsButton setEnabled:NO]; + [toolbarReloadButton setEnabled:NO]; +} + +/** + * Enable all table list interactive elements after an ongoing task. + */ +- (void) endDocumentTaskForTab:(NSNotification *)aNotification +{ + tableListIsSelectable = YES; + [toolbarAddButton setEnabled:YES]; + [toolbarActionsButton setEnabled:YES]; + [toolbarReloadButton setEnabled:YES]; +} + +/** + * Set the table list to selectable or not during the task process. + */ +- (void) setTableListSelectability:(BOOL)isSelectable +{ + tableListIsSelectable = isSelectable; +} + +#pragma mark - +#pragma mark SplitView Delegate Methods + +- (NSRect)splitView:(NSSplitView *)splitView effectiveRect:(NSRect)proposedEffectiveRect forDrawnRect:(NSRect)drawnRect ofDividerAtIndex:(NSInteger)dividerIndex +{ + return (splitView == tableListSplitView ? NSZeroRect : proposedEffectiveRect); +} + + +#pragma mark - +#pragma mark Other + +/** + * Standard init method. Performs various ivar initialisations. + */ +- (id)init +{ + if ((self = [super init])) { + tables = [[NSMutableArray alloc] init]; + filteredTables = tables; + tableTypes = [[NSMutableArray alloc] init]; + filteredTableTypes = tableTypes; + structureLoaded = NO; + contentLoaded = NO; + statusLoaded = NO; + triggersLoaded = NO; + isTableListFiltered = NO; + tableListIsSelectable = YES; + tableListContainsViews = NO; + selectedTableType = SPTableTypeNone; + selectedTableName = nil; + [tables addObject:NSLocalizedString(@"TABLES",@"header for table list")]; + } + + return self; +} + +/** + * Standard awakeFromNib method for interface loading. + */ +- (void)awakeFromNib +{ + + // Collapse the table information pane if preference to do so is set + if ([[[NSUserDefaults standardUserDefaults] objectForKey:SPTableInformationPanelCollapsed] boolValue] + && [tableListSplitView collapsibleSubview]) { + [tableInfoCollapseButton setNextState]; + [tableInfoCollapseButton setToolTip:NSLocalizedString(@"Show Table Information",@"Show Table Information")]; + [tableListSplitView setValue:[NSNumber numberWithFloat:[tableListSplitView collapsibleSubview].frame.size.height] forKey:@"uncollapsedSize"]; + [[tableListSplitView collapsibleSubview] setFrameSize:NSMakeSize([tableListSplitView collapsibleSubview].frame.size.width, 0)]; + [tableListSplitView setCollapsibleSubviewCollapsed:YES]; + } else { + [tableInfoCollapseButton setToolTip:NSLocalizedString(@"Hide Table Information",@"Hide Table Information")]; + } + + // Start the table filter list collapsed + if ([tableListFilterSplitView collapsibleSubview]) { + [tableListFilterSplitView setValue:[NSNumber numberWithFloat:[tableListFilterSplitView collapsibleSubview].frame.size.height] forKey:@"uncollapsedSize"]; + // Set search bar view to the height of 1 instead of 0 to ensure that the view will be visible + // after opening a next connection window which has more than 20 tables + [[tableListFilterSplitView collapsibleSubview] setFrameSize:NSMakeSize([tableListFilterSplitView collapsibleSubview].frame.size.width, 1)]; + [tableListFilterSplitView setCollapsibleSubviewCollapsed:YES]; + } + + // Add observers for document task activity + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(startDocumentTaskForTab:) + name:SPDocumentTaskStartNotification + object:tableDocumentInstance]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(endDocumentTaskForTab:) + name:SPDocumentTaskEndNotification + object:tableDocumentInstance]; +} + +/** + * Standard dealloc method. + */ +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [tables release]; + [tableTypes release]; + if (isTableListFiltered && filteredTables) [filteredTables release]; + if (isTableListFiltered && filteredTableTypes) [filteredTableTypes release]; + if (selectedTableName) [selectedTableName release]; + + [super dealloc]; +} + +@end + +@implementation SPTablesList (PrivateAPI) + +/** + * Removes the selected object (table, view, procedure, function, etc.) from the database and tableView. + */ +- (void)removeTable +{ + NSIndexSet *indexes = [tablesListView selectedRowIndexes]; + [tablesListView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; + + // get last index + NSUInteger currentIndex = [indexes lastIndex]; + + while (currentIndex != NSNotFound) + { + if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeView) { + [mySQLConnection queryString: [NSString stringWithFormat: @"DROP VIEW %@", + [[filteredTables objectAtIndex:currentIndex] backtickQuotedString] + ]]; + } else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeTable) { + [mySQLConnection queryString: [NSString stringWithFormat: @"DROP TABLE %@", + [[filteredTables objectAtIndex:currentIndex] backtickQuotedString] + ]]; + } else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeProc) { + [mySQLConnection queryString: [NSString stringWithFormat: @"DROP PROCEDURE %@", + [[filteredTables objectAtIndex:currentIndex] backtickQuotedString] + ]]; + } else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeFunc) { + [mySQLConnection queryString: [NSString stringWithFormat: @"DROP FUNCTION %@", + [[filteredTables objectAtIndex:currentIndex] backtickQuotedString] + ]]; + } + + // If no error is recorded, the table was successfully dropped - remove it from the list + if (![mySQLConnection queryErrored]) { + //dropped table with success + if (isTableListFiltered) { + NSInteger unfilteredIndex = [tables indexOfObject:[filteredTables objectAtIndex:currentIndex]]; + [tables removeObjectAtIndex:unfilteredIndex]; + [tableTypes removeObjectAtIndex:unfilteredIndex]; + } + [filteredTables removeObjectAtIndex:currentIndex]; + [filteredTableTypes removeObjectAtIndex:currentIndex]; + + // Get next index (beginning from the end) + currentIndex = [indexes indexLessThanIndex:currentIndex]; + + // Otherwise, display an alert - and if there's tables left, ask whether to proceed + } else { + + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + if ([indexes indexLessThanIndex:currentIndex] == NSNotFound) { + [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button")]; + } else { + [alert addButtonWithTitle:NSLocalizedString(@"Continue", @"continue button")]; + [alert addButtonWithTitle:NSLocalizedString(@"Stop", @"stop button")]; + } + [alert setMessageText:NSLocalizedString(@"Error", @"error")]; + [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Couldn't delete '%@'.\nMySQL said: %@", @"message of panel when an item cannot be deleted"), [tables objectAtIndex:currentIndex], [mySQLConnection getLastErrorMessage]]]; + [alert setAlertStyle:NSWarningAlertStyle]; + if ([indexes indexLessThanIndex:currentIndex] == NSNotFound) { + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; + currentIndex = NSNotFound; + } else { + NSInteger choice = [alert runModal]; + if (choice == NSAlertFirstButtonReturn) { + currentIndex = [indexes indexLessThanIndex:currentIndex]; + } else { + currentIndex = NSNotFound; + } + } + } + } + + // Remove the isolated "current selection" item for filtered lists if appropriate + if (isTableListFiltered && [filteredTables count] > 1 + && [[filteredTableTypes objectAtIndex:[filteredTableTypes count]-1] integerValue] == SPTableTypeNone + && [[filteredTables objectAtIndex:[filteredTables count]-1] isEqualToString:NSLocalizedString(@"CURRENT SELECTION",@"header for current selection in filtered list")]) + { + [filteredTables removeLastObject]; + [filteredTableTypes removeLastObject]; + } + + [tablesListView reloadData]; + + // set window title + [tableDocumentInstance updateWindowTitle:self]; + + [tablesListView deselectAll:self]; + + // Query the structure of all databases in the background (mainly for completion) + [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", nil]]; + +} + +/** + * Trucates the selected table(s). + */ +- (void)truncateTable +{ + NSIndexSet *indexes = [tablesListView selectedRowIndexes]; + + // Get last index + NSUInteger currentIndex = [indexes lastIndex]; + + while (currentIndex != NSNotFound) + { + [mySQLConnection queryString:[NSString stringWithFormat: @"TRUNCATE TABLE %@", [[filteredTables objectAtIndex:currentIndex] backtickQuotedString]]]; + + // Couldn't truncate table + if ([mySQLConnection queryErrored]) { + NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Error truncating table", @"error truncating table message") + defaultButton:NSLocalizedString(@"OK", @"OK button") + alternateButton:nil + otherButton:nil + informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"An error occurred while trying to truncate the table '%@'.\n\nMySQL said: %@", @"error truncating table informative message"), + [filteredTables objectAtIndex:currentIndex], [mySQLConnection getLastErrorMessage]]]; + + [alert setAlertStyle:NSCriticalAlertStyle]; + // NSArray *buttons = [alert buttons]; + // // Change the alert's cancel button to have the key equivalent of return + // [[buttons objectAtIndex:0] setKeyEquivalent:@"t"]; + // [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; + // [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; + [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"truncateTableError"]; + } + + // Get next index (beginning from the end) + currentIndex = [indexes indexLessThanIndex:currentIndex]; + } + + // Ensure the the table's content view is updated to show that it has been truncated + if ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewContent) { + [tableContentInstance reloadTable:self]; + } else { + [self setContentRequiresReload:YES]; + } + + [tableDataInstance resetStatusData]; +} + +/** + * Adds a new table table to the database using the selected character set encoding and storage engine. + */ +- (void)addTable +{ + NSString *charSetStatement = @""; + NSString *engineStatement = @""; + + NSString *tableType = [tableTypeButton title]; + NSString *tableName = [tableNameField stringValue]; + + // If there is an encoding selected other than the default we must specify it in CREATE TABLE statement + if ([tableEncodingButton indexOfSelectedItem] > 0) { + charSetStatement = [NSString stringWithFormat:@"DEFAULT CHARACTER SET %@", [[tableDocumentInstance mysqlEncodingFromDisplayEncoding:[tableEncodingButton title]] backtickQuotedString]]; + } + + // If there is a type selected other than the default we must specify it in CREATE TABLE statement + if ([tableTypeButton indexOfSelectedItem] > 0) { + engineStatement = [NSString stringWithFormat:@"ENGINE = %@", [tableType backtickQuotedString]]; + } + + NSString *createStatement = [NSString stringWithFormat:@"CREATE TABLE %@ (%@) %@ %@", [tableName backtickQuotedString], ([tableType isEqualToString:@"CSV"]) ? @"id INT NOT NULL" : @"id INT", charSetStatement, engineStatement]; + + // Create the table + [mySQLConnection queryString:createStatement]; + + if (![mySQLConnection queryErrored]) { + + // Table creation was successful - insert the new item into the tables list and select it. + NSInteger addItemAtIndex = NSNotFound; + + for (NSInteger i = 0; i < [tables count]; i++) + { + NSInteger tableType = [[tableTypes objectAtIndex:i] integerValue]; + + if (tableType == SPTableTypeNone) continue; + if (tableType == SPTableTypeProc || tableType == SPTableTypeFunc) { + addItemAtIndex = (i - 1); + break; + } + + if ([tableName localizedCompare:[tables objectAtIndex:i]] == NSOrderedAscending) { + addItemAtIndex = i; + break; + } + } + + if (addItemAtIndex == NSNotFound) { + [tables addObject:tableName]; + [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeTable]]; + } + else { + [tables insertObject:tableName atIndex:addItemAtIndex]; + [tableTypes insertObject:[NSNumber numberWithInteger:SPTableTypeTable] atIndex:addItemAtIndex]; + } + + // Set the selected table name and type, and then use updateFilter and updateSelection to update the filter list and selection. + if (selectedTableName) [selectedTableName release]; + + selectedTableName = [[NSString alloc] initWithString:tableName]; + selectedTableType = SPTableTypeTable; + + [self updateFilter:self]; + + [tablesListView scrollRowToVisible:[tablesListView selectedRow]]; + + [self updateSelectionWithTaskString:[NSString stringWithFormat:NSLocalizedString(@"Loading %@...", @"Loading table task string"), selectedTableName]]; + + // Query the structure of all databases in the background (mainly for completion) + [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", nil]]; + + } + else { + // Error while creating new table + alertSheetOpened = YES; + + SPBeginAlertSheet(NSLocalizedString(@"Error adding new table", @"error adding new table message"), + NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, + @selector(sheetDidEnd:returnCode:contextInfo:), @"addRow", + [NSString stringWithFormat:NSLocalizedString(@"An error occurred while trying to add the new table '%@'.\n\nMySQL said: %@", @"error adding new table informative message"), tableName, [mySQLConnection getLastErrorMessage]]); + + [tablesListView reloadData]; + } + + // Clear table name + [tableNameField setStringValue:@""]; +} + +/** + * Copies the currently selected object (table, view, procedure, function, etc.). + */ +- (void)copyTable +{ + NSString *tableType = @""; + + if ([[copyTableNameField stringValue] isEqualToString:@""]) { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, NSLocalizedString(@"Table must have a name.", @"message of panel when no name is given for table")); + return; + } + + BOOL copyTableContent = ([copyTableContentSwitch state] == NSOnState); + + NSInteger tblType = [[filteredTableTypes objectAtIndex:[tablesListView selectedRow]] integerValue]; + + switch (tblType){ + case SPTableTypeTable: + tableType = NSLocalizedString(@"table",@"table"); + [copyTableContentSwitch setEnabled:YES]; + break; + case SPTableTypeView: + tableType = NSLocalizedString(@"view",@"view"); + [copyTableContentSwitch setEnabled:NO]; + break; + case SPTableTypeProc: + tableType = NSLocalizedString(@"procedure",@"procedure"); + [copyTableContentSwitch setEnabled:NO]; + break; + case SPTableTypeFunc: + tableType = NSLocalizedString(@"function",@"function"); + [copyTableContentSwitch setEnabled:NO]; + break; + } + + // Get table/view structure + MCPResult *queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE %@ %@", + [tableType uppercaseString], + [[filteredTables objectAtIndex:[tablesListView selectedRow]] backtickQuotedString] + ]]; + [queryResult setReturnDataAsStrings:YES]; + + if ( ![queryResult numOfRows] ) { + //error while getting table structure + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"Couldn't get create syntax.\nMySQL said: %@", @"message of panel when table information cannot be retrieved"), [mySQLConnection getLastErrorMessage]]); + + } else { + //insert new table name in create syntax and create new table + NSScanner *scanner; + NSString *scanString; + + if(tblType == SPTableTypeView){ + scanner = [[NSScanner alloc] initWithString:[[queryResult fetchRowAsDictionary] objectForKey:@"Create View"]]; + [scanner scanUpToString:@"AS" intoString:nil]; + [scanner scanUpToString:@"" intoString:&scanString]; + [scanner release]; + [mySQLConnection queryString:[NSString stringWithFormat:@"CREATE VIEW %@ %@", [[copyTableNameField stringValue] backtickQuotedString], scanString]]; + } + else if(tblType == SPTableTypeTable){ + scanner = [[NSScanner alloc] initWithString:[[queryResult fetchRowAsDictionary] objectForKey:@"Create Table"]]; + [scanner scanUpToString:@"(" intoString:nil]; + [scanner scanUpToString:@"" intoString:&scanString]; + [scanner release]; + + // If there are any InnoDB referencial constraints we need to strip out the names as they must be unique. + // MySQL will generate the new names based on the new table name. + scanString = [scanString stringByReplacingOccurrencesOfRegex:[NSString stringWithFormat:@"CONSTRAINT `[^`]+` "] withString:@""]; + + // If we're not copying the tables content as well then we need to strip out any AUTO_INCREMENT presets. + if (!copyTableContent) { + scanString = [scanString stringByReplacingOccurrencesOfRegex:[NSString stringWithFormat:@"AUTO_INCREMENT=[0-9]+ "] withString:@""]; + } + + [mySQLConnection queryString:[NSString stringWithFormat:@"CREATE TABLE %@ %@", [[copyTableNameField stringValue] backtickQuotedString], scanString]]; + } + else if(tblType == SPTableTypeFunc || tblType == SPTableTypeProc) + { + // get the create syntax + MCPResult *theResult; + if(selectedTableType == SPTableTypeProc) + theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE PROCEDURE %@", [selectedTableName backtickQuotedString]]]; + else if([self tableType] == SPTableTypeFunc) + theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE FUNCTION %@", [selectedTableName backtickQuotedString]]]; + else + return; + + // Check for errors, only displaying if the connection hasn't been terminated + if ([mySQLConnection queryErrored]) { + if ([mySQLConnection isConnected]) { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"An error occured while retrieving the create syntax for '%@'.\nMySQL said: %@", @"message of panel when create syntax cannot be retrieved"), selectedTableName, [mySQLConnection getLastErrorMessage]]); + } + return; + } + + [theResult setReturnDataAsStrings:YES]; + NSString *tableSyntax = [[theResult fetchRowAsArray] objectAtIndex:2]; + + // replace the old name by the new one and drop the old one + [mySQLConnection queryString:[tableSyntax stringByReplacingOccurrencesOfRegex:[NSString stringWithFormat:@"(?<=%@ )(`[^`]+?`)", [tableType uppercaseString]] withString:[[copyTableNameField stringValue] backtickQuotedString]]]; + + if ([mySQLConnection queryErrored]) { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"Couldn't duplicate '%@'.\nMySQL said: %@", @"message of panel when an item cannot be renamed"), [copyTableNameField stringValue], [mySQLConnection getLastErrorMessage]]); + } + + } + + if ([mySQLConnection queryErrored]) { + //error while creating new table + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"Couldn't create '%@'.\nMySQL said: %@", @"message of panel when table cannot be created"), [copyTableNameField stringValue], [mySQLConnection getLastErrorMessage]]); + } else { + + if (copyTableContent) { + //copy table content + [mySQLConnection queryString:[NSString stringWithFormat: + @"INSERT INTO %@ SELECT * FROM %@", + [[copyTableNameField stringValue] backtickQuotedString], + [selectedTableName backtickQuotedString] + ]]; + + if ([mySQLConnection queryErrored]) { + SPBeginAlertSheet( + NSLocalizedString(@"Warning", @"warning"), + NSLocalizedString(@"OK", @"OK button"), + nil, + nil, + [tableDocumentInstance parentWindow], + self, + nil, + nil, + NSLocalizedString(@"There have been errors while copying table content. Please control the new table.", @"message of panel when table content cannot be copied") + ); + } + } + + // Insert the new item into the tables list and select it. + NSInteger addItemAtIndex = NSNotFound; + for (NSInteger i = 0; i < [tables count]; i++) { + NSInteger theTableType = [[tableTypes objectAtIndex:i] integerValue]; + if (theTableType == SPTableTypeNone) continue; + if ((theTableType == SPTableTypeView || theTableType == SPTableTypeTable) + && (tblType == SPTableTypeProc || tblType == SPTableTypeFunc)) { + continue; + } + if ((theTableType == SPTableTypeProc || theTableType == SPTableTypeFunc) + && (tblType == SPTableTypeView || tblType == SPTableTypeTable)) { + addItemAtIndex = i - 1; + break; + } + if ([[copyTableNameField stringValue] localizedCompare:[tables objectAtIndex:i]] == NSOrderedAscending) { + addItemAtIndex = i; + break; + } + } + if (addItemAtIndex == NSNotFound) { + [tables addObject:[copyTableNameField stringValue]]; + [tableTypes addObject:[NSNumber numberWithInteger:tblType]]; + } else { + [tables insertObject:[copyTableNameField stringValue] atIndex:addItemAtIndex]; + [tableTypes insertObject:[NSNumber numberWithInteger:tblType] atIndex:addItemAtIndex]; + } + + // Set the selected table name and type, and use updateFilter to update the filter list and selection + if (selectedTableName) [selectedTableName release]; + selectedTableName = [[NSString alloc] initWithString:[copyTableNameField stringValue]]; + selectedTableType = tblType; + [self updateFilter:self]; + [tablesListView scrollRowToVisible:[tablesListView selectedRow]]; + [self updateSelectionWithTaskString:[NSString stringWithFormat:NSLocalizedString(@"Loading %@...", @"Loading table task string"), selectedTableName]]; + + // Query the structure of all databases in the background (mainly for completion) + [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", nil]]; + + } + } +} + +/* + * Renames a table, view, procedure or function. Also handles only changes in case! + * This function ONLY changes the database. It does NOT refresh the views etc. + * CAREFUL: This function raises an exception if renaming fails, and does not show an error message. + */ +- (void)renameTableOfType:(SPTableType)tableType from:(NSString *)oldTableName to:(NSString *)newTableName +{ + // check if the name really changed + if ([oldTableName isEqualToString:newTableName]) return; + + // check if only the case changed - then we have to do two renames, see http://code.google.com/p/sequel-pro/issues/detail?id=484 + if ([[oldTableName lowercaseString] isEqualToString:[newTableName lowercaseString]]) + { + // first try finding an unused temporary name + // this code should be improved in case we find out that something uses table names like mytable-1, mytable-2, etc. + NSString* tempTableName; + int tempNumber; + for(tempNumber=2; tempNumber<100; tempNumber++) { + tempTableName = [NSString stringWithFormat:@"%@-%d",selectedTableName,tempNumber]; + if ([self isTableNameValid:tempTableName forType:tableType]) break; + } + if (tempNumber==100) { + // we couldn't find a temporary name + [NSException raise:@"No Tempname found" format:NSLocalizedString(@"An error occured while renaming '%@'. No temporary name could be found. Please try renaming to something else first.", @"rename table error - no temporary name found"), oldTableName]; + } + + [self renameTableOfType:tableType from:oldTableName to:tempTableName]; + [self renameTableOfType:tableType from:tempTableName to:newTableName]; + return; + } + + //check if we are trying to rename a TABLE or a VIEW + if (tableType == SPTableTypeView || tableType == SPTableTypeTable) { + // we can use the rename table statement + [mySQLConnection queryString:[NSString stringWithFormat:@"RENAME TABLE %@ TO %@", [oldTableName backtickQuotedString], [newTableName backtickQuotedString]]]; + // check for errors + if ([mySQLConnection queryErrored]) { + [NSException raise:@"MySQL Error" format:NSLocalizedString(@"An error occured while renaming '%@'.\n\nMySQL said: %@", @"rename table error informative message"), oldTableName, [mySQLConnection getLastErrorMessage]]; + } + return; + } + + //check if we are trying to rename a PROCEDURE or a FUNCTION + if (tableType == SPTableTypeProc || tableType == SPTableTypeFunc) { + // procedures and functions can only be renamed if one creates a new one and deletes the old one + + // first get the create syntax + NSString *stringTableType = @""; + + switch (tableType){ + case SPTableTypeProc: stringTableType = @"PROCEDURE"; break; + case SPTableTypeFunc: stringTableType = @"FUNCTION"; break; + } + + MCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE %@ %@", stringTableType, [oldTableName backtickQuotedString] ] ]; + if ([mySQLConnection queryErrored]) { + [NSException raise:@"MySQL Error" format:NSLocalizedString(@"An error occured while renaming. I couldn't retrieve the syntax for '%@'.\n\nMySQL said: %@", @"rename precedure/function error - can't retrieve syntax"), oldTableName, [mySQLConnection getLastErrorMessage]]; + } + [theResult setReturnDataAsStrings:YES]; + NSString *oldCreateSyntax = [[theResult fetchRowAsArray] objectAtIndex:2]; + + // replace the old name with the new name + NSRange rangeOfProcedureName = [oldCreateSyntax rangeOfString: [NSString stringWithFormat:@"%@ %@", stringTableType, [oldTableName backtickQuotedString] ] ]; + if (rangeOfProcedureName.length == 0) { + [NSException raise:@"Unknown Syntax" format:NSLocalizedString(@"An error occured while renaming. The CREATE syntax of '%@' could not be parsed.", @"rename error - invalid create syntax"), oldTableName]; + } + NSString *newCreateSyntax = [oldCreateSyntax stringByReplacingCharactersInRange: rangeOfProcedureName + withString: [NSString stringWithFormat:@"%@ %@", stringTableType, [newTableName backtickQuotedString] ] ]; + [mySQLConnection queryString: newCreateSyntax]; + if ([mySQLConnection queryErrored]) { + [NSException raise:@"MySQL Error" format:NSLocalizedString(@"An error occured while renaming. I couldn't recreate '%@'.\n\nMySQL said: %@", @"rename precedure/function error - can't recreate procedure"), oldTableName, [mySQLConnection getLastErrorMessage]]; + } + + [mySQLConnection queryString: [NSString stringWithFormat: @"DROP %@ %@", stringTableType, [oldTableName backtickQuotedString]]]; + if ([mySQLConnection queryErrored]) { + [NSException raise:@"MySQL Error" format:NSLocalizedString(@"An error occured while renaming. I couldn't delete '%@'.\n\nMySQL said: %@", @"rename precedure/function error - can't delete old procedure"), oldTableName, [mySQLConnection getLastErrorMessage]]; + } + return; + } + + [NSException raise:@"Object of unknown type" format:NSLocalizedString(@"An error occured while renaming. '%@' is of an unknown type.", @"rename error - don't know what type the renamed thing is"), oldTableName]; +} + + + +/* + * Check tableName for length and if the tableName doesn't match + * against current database table/view names (case-insensitive). + */ +- (BOOL)isTableNameValid:(NSString *)tableName forType:(SPTableType)tableType +{ + return [self isTableNameValid:tableName forType:tableType ignoringSelectedTable:NO]; +} + +- (BOOL)isTableNameValid:(NSString *)tableName forType:(SPTableType)tableType ignoringSelectedTable:(BOOL)ignoreSelectedTable +{ + BOOL isValid = YES; + + // delete trailing whitespaces since 'foo ' or ' ' are not valid table names + NSString *fieldStr = [tableName stringByMatching:@"(.*?)\\s*$" capture:1]; + NSString *lowercaseFieldStr = [fieldStr lowercaseString]; + + // If table name has trailing whitespaces return 'no valid' + if([fieldStr length] != [tableName length]) return NO; + + // empty table names are invalid + if([fieldStr length] == 0) return NO; + + + NSArray *similarTables; + switch (tableType) { + case SPTableTypeView: + case SPTableTypeTable: + similarTables = [self allTableAndViewNames]; + break; + case SPTableTypeProc: + similarTables = [self allProcedureNames]; + break; + case SPTableTypeFunc: + similarTables = [self allFunctionNames]; + break; + default: + // if some other table type is given, just return yes + // better a mysql error than not being able to change something at all + return YES; + } + + for(id table in similarTables) { + //compare case insensitive here + if([lowercaseFieldStr isEqualToString:[table lowercaseString]]) { + if (ignoreSelectedTable) { + // if table is the selectedTable, ignore it + // we must compare CASE SENSITIVE here! + if ([table isEqualToString:selectedTableName]) continue; + } + isValid = NO; + break; + } + } + return isValid; +} + +@end diff --git a/Source/SPTextView.h b/Source/SPTextView.h index f89bbc06..f58304b9 100644 --- a/Source/SPTextView.h +++ b/Source/SPTextView.h @@ -30,12 +30,12 @@ #define SP_TEXT_SIZE_TRIGGER_FOR_PARTLY_PARSING 10000 -@class SPNarrowDownCompletion, TableDocument, TablesList, CustomQuery; +@class SPNarrowDownCompletion, TableDocument, SPTablesList, CustomQuery; @interface SPTextView : NSTextView { IBOutlet TableDocument *tableDocumentInstance; - IBOutlet TablesList *tablesListInstance; + IBOutlet SPTablesList *tablesListInstance; IBOutlet CustomQuery *customQueryInstance; BOOL autoindentEnabled; diff --git a/Source/SPTextView.m b/Source/SPTextView.m index 5a096b93..cf468cc7 100644 --- a/Source/SPTextView.m +++ b/Source/SPTextView.m @@ -32,7 +32,7 @@ #import "SPConstants.h" #import "SPQueryController.h" #import "SPTooltip.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPNavigatorController.h" #import "SPAlertSheets.h" @@ -303,7 +303,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) if(!isDictMode && [mySQLConnection isConnected]) { - // Add structural db/table/field data to completions list or fallback to gathering TablesList data + // Add structural db/table/field data to completions list or fallback to gathering SPTablesList data NSString* connectionID; if(tableDocumentInstance) diff --git a/Source/TableContent.m b/Source/TableContent.m index dee9e430..2aa20c07 100644 --- a/Source/TableContent.m +++ b/Source/TableContent.m @@ -29,7 +29,7 @@ #import "TableDocument.h" #import "TableSource.h" #import "SPTableInfo.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPImageView.h" #import "CMCopyTable.h" #import "SPDataCellFormatter.h" diff --git a/Source/TableDocument.m b/Source/TableDocument.m index ce87a1ef..deabdc2f 100644 --- a/Source/TableDocument.m +++ b/Source/TableDocument.m @@ -26,7 +26,7 @@ // More info at #import "TableDocument.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "TableSource.h" #import "TableContent.h" #import "CustomQuery.h" @@ -2544,7 +2544,7 @@ } /** - * Returns the currently selected table (passing the request to TablesList) + * Returns the currently selected table (passing the request to SPTablesList) */ - (NSString *)table { @@ -4458,7 +4458,7 @@ [[tablesListInstance onMainThread] setTableListSelectability:NO]; } - // Set the connection of TablesList and TablesDump to reload tables in db + // Set the connection of SPTablesList and TablesDump to reload tables in db [tablesListInstance setConnection:mySQLConnection]; [tableDumpInstance setConnection:mySQLConnection]; diff --git a/Source/TableDump.m b/Source/TableDump.m index 3527ed35..5725f0a5 100644 --- a/Source/TableDump.m +++ b/Source/TableDump.m @@ -25,7 +25,7 @@ #import "TableDump.h" #import "TableDocument.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "TableSource.h" #import "TableContent.h" #import "CustomQuery.h" @@ -50,7 +50,7 @@ /** * Update the table lists with the list of tables, retrieved from the - * tablesList. If the user has pressed the reload button, trigger a reload + * SPTablesList. If the user has pressed the reload button, trigger a reload * from the server; otherwise used the cached lists. * Retrieve only tables for all modes except SQL. */ diff --git a/Source/TableSource.m b/Source/TableSource.m index 1ae93a00..bc61b86d 100644 --- a/Source/TableSource.m +++ b/Source/TableSource.m @@ -26,7 +26,7 @@ #import "TableSource.h" #import "TableDocument.h" #import "SPTableInfo.h" -#import "TablesList.h" +#import "SPTablesList.h" #import "SPTableData.h" #import "SPSQLParser.h" #import "SPStringAdditions.h" @@ -664,7 +664,7 @@ closes the keySheet NSString *selTable = nil; - // if selectedTable is nil try to get the name from tablesList + // if selectedTable is nil try to get the name from SPTablesList if(selectedTable == nil || ![selectedTable length]) selTable = [tablesListInstance tableName]; else diff --git a/Source/TablesList.h b/Source/TablesList.h deleted file mode 100644 index 6a313d44..00000000 --- a/Source/TablesList.h +++ /dev/null @@ -1,165 +0,0 @@ -// -// $Id$ -// -// TablesList.h -// sequel-pro -// -// Created by lorenz textor (lorenz@textor.ch) on Wed May 01 2002. -// Copyright (c) 2002-2003 Lorenz Textor. 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 - -#import -#import - -@class SPHistoryController; - -@interface NSObject (NSSplitView) - -- (NSView *)collapsibleSubview; -- (IBAction)toggleCollapse:(id)sender; -- (BOOL)collapsibleSubviewIsCollapsed; -- (void)setCollapsibleSubviewCollapsed:(BOOL)flag; - -@end - -@interface TablesList : NSObject -{ - IBOutlet id tableDocumentInstance; - IBOutlet id tableSourceInstance; - IBOutlet id tableContentInstance; - IBOutlet id customQueryInstance; - IBOutlet id tableDumpInstance; - IBOutlet id tableDataInstance; - IBOutlet id extendedTableInfoInstance; - IBOutlet id databaseDataInstance; - IBOutlet id tableInfoInstance; - IBOutlet id tableTriggersInstance; - IBOutlet SPHistoryController *spHistoryControllerInstance; - - IBOutlet id copyTableSheet; - IBOutlet id tablesListView; - IBOutlet id copyTableButton; - IBOutlet id copyTableNameField; - IBOutlet id copyTableMessageField; - IBOutlet id copyTableContentSwitch; - IBOutlet id tabView; - IBOutlet id tableSheet; - IBOutlet id tableNameField; - IBOutlet id tableEncodingButton; - IBOutlet id tableTypeButton; - IBOutlet id toolbarAddButton; - IBOutlet id toolbarActionsButton; - IBOutlet id toolbarReloadButton; - IBOutlet id addTableButton; - IBOutlet id truncateTableButton; - IBOutlet id truncateTableContextButton; - IBOutlet NSSplitView *tableListSplitView; - IBOutlet NSSplitView *tableListFilterSplitView; - IBOutlet NSButton *tableInfoCollapseButton; - - IBOutlet NSSearchField *listFilterField; - - // Table list 'gear' menu items - IBOutlet NSMenuItem *removeTableMenuItem; - IBOutlet NSMenuItem *duplicateTableMenuItem; - IBOutlet NSMenuItem *renameTableMenuItem; - IBOutlet NSMenuItem *separatorTableMenuItem; - IBOutlet NSMenuItem *showCreateSyntaxMenuItem; - IBOutlet NSMenuItem *separatorTableMenuItem2; - - MCPConnection *mySQLConnection; - - // Table list context menu items - IBOutlet NSMenuItem *removeTableContextMenuItem; - IBOutlet NSMenuItem *duplicateTableContextMenuItem; - IBOutlet NSMenuItem *renameTableContextMenuItem; - IBOutlet NSMenuItem *separatorTableContextMenuItem; - IBOutlet NSMenuItem *showCreateSyntaxContextMenuItem; - IBOutlet NSMenuItem *separatorTableContextMenuItem2; - - NSMutableArray *tables; - NSMutableArray *filteredTables; - NSMutableArray *tableTypes; - NSMutableArray *filteredTableTypes; - NSInteger selectedTableType; - NSString *selectedTableName; - BOOL isTableListFiltered; - BOOL tableListIsSelectable; - BOOL tableListContainsViews; - - BOOL structureLoaded, contentLoaded, statusLoaded, triggersLoaded, alertSheetOpened; -} - -// IBAction methods -- (IBAction)updateTables:(id)sender; -- (IBAction)addTable:(id)sender; -- (IBAction)closeSheet:(id)sender; -- (IBAction)removeTable:(id)sender; -- (IBAction)copyTable:(id)sender; -- (IBAction)renameTable:(id)sender; -- (IBAction)truncateTable:(id)sender; -- (IBAction)togglePaneCollapse:(id)sender; - -// Additional methods -- (void)setConnection:(MCPConnection *)theConnection; -- (void)doPerformQueryService:(NSString *)query; -- (void)updateSelectionWithTaskString:(NSString *)taskString; -- (void)updateSelectionTask; -- (void)setSelection:(NSDictionary *)selectionDetails; -- (void)selectTableAtIndex:(NSNumber *)row; -- (void)makeTableListFilterHaveFocus; - -// Getters -- (NSArray *)selectedTableNames; -- (NSArray *)selectedTableItems; -- (NSString *)tableName; -- (NSInteger)tableType; -- (NSArray *)tables; -- (NSArray *)tableTypes; -- (NSArray *)allTableAndViewNames; -- (NSArray *)allTableNames; -- (NSArray *)allViewNames; -- (NSArray *)allFunctionNames; -- (NSArray *)allProcedureNames; -- (NSArray *)allDatabaseNames; -- (NSArray *)allSystemDatabaseNames; -- (NSString *)selectedDatabase; -- (BOOL)structureLoaded; -- (BOOL)contentLoaded; -- (BOOL)statusLoaded; - -// Setters -- (void)setContentRequiresReload:(BOOL)reload; -- (void)setStatusRequiresReload:(BOOL)reload; -- (BOOL)selectItemWithName:(NSString *)theName; - -// Tabview delegate methods -- (void)loadTabTask:(NSTabViewItem *)tabViewItem; - -// Table list filter interaction -- (void) showFilter; -- (void) hideFilter; -- (void) clearFilter; -- (IBAction) updateFilter:(id)sender; - -// Task interaction -- (void) startDocumentTaskForTab:(NSNotification *)aNotification; -- (void) endDocumentTaskForTab:(NSNotification *)aNotification; -- (void) setTableListSelectability:(BOOL)isSelectable; - -@end diff --git a/Source/TablesList.m b/Source/TablesList.m deleted file mode 100644 index be617aa2..00000000 --- a/Source/TablesList.m +++ /dev/null @@ -1,2433 +0,0 @@ -// -// $Id$ -// -// TablesList.m -// sequel-pro -// -// Created by lorenz textor (lorenz@textor.ch) on Wed May 01 2002. -// Copyright (c) 2002-2003 Lorenz Textor. 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 - -#import "TablesList.h" -#import "TableDocument.h" -#import "TableSource.h" -#import "TableContent.h" -#import "SPTableData.h" -#import "SPTableInfo.h" -#import "TableDump.h" -#import "ImageAndTextCell.h" -#import "SPStringAdditions.h" -#import "SPArrayAdditions.h" -#import "RegexKitLite.h" -#import "SPDatabaseData.h" -#import "NSMutableArray-MultipleSort.h" -#import "NSNotificationAdditions.h" -#import "SPConstants.h" -#import "SPAlertSheets.h" -#import "SPNavigatorController.h" -#import "SPMainThreadTrampoline.h" -#import "SPHistoryController.h" - -@interface TablesList (PrivateAPI) - -- (void)removeTable; -- (void)truncateTable; -- (void)addTable; -- (void)copyTable; -- (void)renameTableOfType:(SPTableType)tableType from:(NSString *)oldTableName to:(NSString *)newTableName; -- (BOOL)isTableNameValid:(NSString *)tableName forType:(SPTableType)tableType; -- (BOOL)isTableNameValid:(NSString *)tableName forType:(SPTableType)tableType ignoringSelectedTable:(BOOL)ignoreSelectedTable; - -@end - -@implementation TablesList - -#pragma mark - -#pragma mark IBAction methods - -/** - * Loads all table names in array tables and reload the tableView - */ -- (IBAction)updateTables:(id)sender -{ - MCPResult *theResult; - NSArray *resultRow; - NSInteger i; - NSString *previousSelectedTable = nil; - BOOL previousTableListIsSelectable = tableListIsSelectable; - - if (selectedTableName) previousSelectedTable = [[NSString alloc] initWithString:selectedTableName]; - if (isTableListFiltered) { - if (filteredTables) [filteredTables release]; - filteredTables = tables; - if (filteredTableTypes) [filteredTableTypes release]; - filteredTableTypes = tableTypes; - isTableListFiltered = NO; - } - tableListContainsViews = NO; - - tableListIsSelectable = YES; - [[tablesListView onMainThread] deselectAll:self]; - tableListIsSelectable = previousTableListIsSelectable; - [tables removeAllObjects]; - [tableTypes removeAllObjects]; - - if ([tableDocumentInstance database]) { - - // Notify listeners that a query has started - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance]; - - // Select the table list for the current database. On MySQL versions after 5 this will include - // views; on MySQL versions >= 5.0.02 select the "full" list to also select the table type column. - theResult = [mySQLConnection queryString:@"SHOW /*!50002 FULL*/ TABLES"]; - if ([theResult numOfRows]) [theResult dataSeek:0]; - if ([theResult numOfFields] == 1) { - for ( i = 0 ; i < [theResult numOfRows] ; i++ ) { - [tables addObject:[[theResult fetchRowAsArray] objectAtIndex:0]]; - [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeTable]]; - } - } else { - for ( i = 0 ; i < [theResult numOfRows] ; i++ ) { - resultRow = [theResult fetchRowAsArray]; - [tables addObject:[resultRow objectAtIndex:0]]; - if ([[resultRow objectAtIndex:1] isEqualToString:@"VIEW"]) { - [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeView]]; - tableListContainsViews = YES; - } else { - [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeTable]]; - } - } - } - - // Reorder the tables in alphabetical order - [tables sortArrayUsingSelector:@selector(localizedCompare:) withPairedMutableArrays:tableTypes, nil]; - - /* grab the procedures and functions - * - * using information_schema gives us more info (for information window perhaps?) but breaks - * backward compatibility with pre 4 I believe. I left the other methods below, in case. - */ - if ([mySQLConnection serverMajorVersion] >= 5) { - NSString *pQuery = [NSString stringWithFormat:@"SELECT * FROM information_schema.routines WHERE routine_schema = '%@' ORDER BY routine_name",[tableDocumentInstance database]]; - theResult = [mySQLConnection queryString:pQuery]; - - // Check for mysql errors - if information_schema is not accessible for some reasons - // omit adding procedures and functions - if(![mySQLConnection queryErrored] && theResult != nil && [theResult numOfRows] ) { - // add the header row - [tables addObject:NSLocalizedString(@"PROCS & FUNCS",@"header for procs & funcs list")]; - [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeNone]]; - [theResult dataSeek:0]; - - if( [theResult numOfFields] == 1 ) { - for( i = 0; i < [theResult numOfRows]; i++ ) { - [tables addObject:NSArrayObjectAtIndex([theResult fetchRowAsArray],3)]; - if( [NSArrayObjectAtIndex([theResult fetchRowAsArray], 4) isEqualToString:@"PROCEDURE"]) { - [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeProc]]; - } else { - [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeFunc]]; - } - } - } else { - for( i = 0; i < [theResult numOfRows]; i++ ) { - resultRow = [theResult fetchRowAsArray]; - [tables addObject:NSArrayObjectAtIndex(resultRow, 3)]; - if( [NSArrayObjectAtIndex(resultRow, 4) isEqualToString:@"PROCEDURE"] ) { - [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeProc]]; - } else { - [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeFunc]]; - } - } - } - } - } - /* - BOOL addedPFHeader = FALSE; - NSString *pQuery = [NSString stringWithFormat:@"SHOW PROCEDURE STATUS WHERE db = '%@'",[tableDocumentInstance database]]; - theResult = [mySQLConnection queryString:pQuery]; - - if( [theResult numOfRows] ) { - // add the header row - [tables addObject:NSLocalizedString(@"PROCS & FUNCS",@"header for procs & funcs list")]; - [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeNone]]; - addedPFHeader = TRUE; - [theResult dataSeek:0]; - - if( [theResult numOfFields] == 1 ) { - for( i = 0; i < [theResult numOfRows]; i++ ) { - [tables addObject:[[theResult fetchRowAsArray] objectAtIndex:1]]; - [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeProc]]; - } - } else { - for( i = 0; i < [theResult numOfRows]; i++ ) { - resultRow = [theResult fetchRowAsArray]; - [tables addObject:[resultRow objectAtIndex:1]]; - [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeProc]]; - } - } - } - - pQuery = [NSString stringWithFormat:@"SHOW FUNCTION STATUS WHERE db = '%@'",[tableDocumentInstance database]]; - theResult = [mySQLConnection queryString:pQuery]; - - if( [theResult numOfRows] ) { - if( !addedPFHeader ) { - // add the header row - [tables addObject:NSLocalizedString(@"PROCS & FUNCS",@"header for procs & funcs list")]; - [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeNone]]; - } - [theResult dataSeek:0]; - - if( [theResult numOfFields] == 1 ) { - for( i = 0; i < [theResult numOfRows]; i++ ) { - [tables addObject:[[theResult fetchRowAsArray] objectAtIndex:1]]; - [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeFunc]]; - } - } else { - for( i = 0; i < [theResult numOfRows]; i++ ) { - resultRow = [theResult fetchRowAsArray]; - [tables addObject:[resultRow objectAtIndex:1]]; - [tableTypes addObject:[NSNumber numberWithInt:SPTableTypeFunc]]; - } - } - } - */ - // Notify listeners that the query has finished - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; - } - - // Add the table headers even if no tables were found - if (tableListContainsViews) { - [tables insertObject:NSLocalizedString(@"TABLES & VIEWS",@"header for table & views list") atIndex:0]; - } else { - [tables insertObject:NSLocalizedString(@"TABLES",@"header for table list") atIndex:0]; - } - [tableTypes insertObject:[NSNumber numberWithInteger:SPTableTypeNone] atIndex:0]; - - [[tablesListView onMainThread] reloadData]; - - // if the previous selected table still exists, select it - // but not if the update was called from SPTableData since it calls that method - // if a selected table doesn't exist - this happens if a table was deleted/renamed by an other user - // or if the table name contains characters which are not supported by the current set encoding - if( ![sender isKindOfClass:[SPTableData class]] && previousSelectedTable != nil && [tables indexOfObject:previousSelectedTable] < [tables count]) { - NSInteger itemToReselect = [tables indexOfObject:previousSelectedTable]; - tableListIsSelectable = YES; - [[tablesListView onMainThread] selectRowIndexes:[NSIndexSet indexSetWithIndex:itemToReselect] byExtendingSelection:NO]; - tableListIsSelectable = previousTableListIsSelectable; - if (selectedTableName) [selectedTableName release]; - selectedTableName = [[NSString alloc] initWithString:[tables objectAtIndex:itemToReselect]]; - selectedTableType = [[tableTypes objectAtIndex:itemToReselect] integerValue]; - } else { - if (selectedTableName) [selectedTableName release]; - selectedTableName = nil; - selectedTableType = SPTableTypeNone; - } - - // Determine whether or not to show the list filter based on the number of tables, and clear it - [[self onMainThread] clearFilter]; - if ([tables count] > 20) [self showFilter]; - else [self hideFilter]; - - // Set the filter placeholder text - if ([tableDocumentInstance database]) { - [[[listFilterField cell] onMainThread] setPlaceholderString:NSLocalizedString(@"Filter", @"Filter placeholder")]; - } - - if (previousSelectedTable) [previousSelectedTable release]; - - // Query the structure of all databases in the background - if(sender == self) - // Invoked by SP - [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:nil]; - else - // User press refresh button ergo force update - [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", [NSNumber numberWithBool:YES], @"cancelQuerying", nil]]; - - -} - -/** - * Adds a new table to the tables-array (no changes in mysql-db) - */ -- (IBAction)addTable:(id)sender -{ - if ((![tableSourceInstance saveRowOnDeselect]) || (![tableContentInstance saveRowOnDeselect]) || (![tableDocumentInstance database])) { - return; - } - - [[tableDocumentInstance parentWindow] endEditingFor:nil]; - - // Populate the table type (engine) popup button - [tableTypeButton removeAllItems]; - - NSArray *engines = [databaseDataInstance getDatabaseStorageEngines]; - - // Add default menu item - [tableTypeButton addItemWithTitle:@"Default"]; - [[tableTypeButton menu] addItem:[NSMenuItem separatorItem]]; - - for (NSDictionary *engine in engines) - { - [tableTypeButton addItemWithTitle:[engine objectForKey:@"Engine"]]; - } - - [NSApp beginSheet:tableSheet - modalForWindow:[tableDocumentInstance parentWindow] - modalDelegate:self - didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) - contextInfo:@"addTable"]; -} - -/** - * Closes the current sheet and stops the modal session - */ -- (IBAction)closeSheet:(id)sender -{ - [NSApp endSheet:[sender window] returnCode:[sender tag]]; - [[sender window] orderOut:self]; -} - -/** - * Invoked when user hits the remove button alert sheet to ask user if he really wants to delete the table. - */ -- (IBAction)removeTable:(id)sender -{ - if (![tablesListView numberOfSelectedRows]) - return; - - [[tableDocumentInstance parentWindow] endEditingFor:nil]; - - NSAlert *alert = [NSAlert alertWithMessageText:@"" defaultButton:NSLocalizedString(@"Delete", @"delete button") alternateButton:NSLocalizedString(@"Cancel", @"cancel button") otherButton:nil informativeTextWithFormat:@""]; - - [alert setAlertStyle:NSCriticalAlertStyle]; - - NSArray *buttons = [alert buttons]; - - // Change the alert's cancel button to have the key equivalent of return - [[buttons objectAtIndex:0] setKeyEquivalent:@"d"]; - [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; - [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - - NSIndexSet *indexes = [tablesListView selectedRowIndexes]; - - NSString *tblTypes = @""; - NSUInteger currentIndex = [indexes lastIndex]; - - if ([tablesListView numberOfSelectedRows] == 1) { - if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeView) - tblTypes = NSLocalizedString(@"view", @"view"); - else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeTable) - tblTypes = NSLocalizedString(@"table", @"table"); - else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeProc) - tblTypes = NSLocalizedString(@"procedure", @"procedure"); - else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeFunc) - tblTypes = NSLocalizedString(@"function", @"function"); - - [alert setMessageText:[NSString stringWithFormat:NSLocalizedString(@"Delete %@ '%@'?", @"delete table/view message"), tblTypes, [filteredTables objectAtIndex:[tablesListView selectedRow]]]]; - [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete the %@ '%@'? This operation cannot be undone.", @"delete table/view informative message"), tblTypes, [filteredTables objectAtIndex:[tablesListView selectedRow]]]]; - } - else { - - BOOL areTableTypeEqual = YES; - NSInteger lastType = [[filteredTableTypes objectAtIndex:currentIndex] integerValue]; - while (currentIndex != NSNotFound) - { - if([[filteredTableTypes objectAtIndex:currentIndex] integerValue]!=lastType) - { - areTableTypeEqual = NO; - break; - } - currentIndex = [indexes indexLessThanIndex:currentIndex]; - } - if(areTableTypeEqual) - { - switch(lastType) { - case SPTableTypeTable: - tblTypes = NSLocalizedString(@"tables", @"tables"); - break; - case SPTableTypeView: - tblTypes = NSLocalizedString(@"views", @"views"); - break; - case SPTableTypeProc: - tblTypes = NSLocalizedString(@"procedures", @"procedures"); - break; - case SPTableTypeFunc: - tblTypes = NSLocalizedString(@"functions", @"functions"); - break; - } - - } else - tblTypes = NSLocalizedString(@"items", @"items"); - - [alert setMessageText:[NSString stringWithFormat:NSLocalizedString(@"Delete selected %@?", @"delete tables/views message"), tblTypes]]; - [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete the selected %@? This operation cannot be undone.", @"delete tables/views informative message"), tblTypes]]; - } - - [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removeRow"]; -} - -/** - * Copies a table/view/proc/func, if desired with content - */ -- (IBAction)copyTable:(id)sender -{ - NSString *tableType = @""; - - if ([tablesListView numberOfSelectedRows] != 1) return; - if (![tableSourceInstance saveRowOnDeselect] || ![tableContentInstance saveRowOnDeselect]) return; - - [[tableDocumentInstance parentWindow] endEditingFor:nil]; - - // Detect table type: table or view - NSInteger tblType = [[filteredTableTypes objectAtIndex:[tablesListView selectedRow]] integerValue]; - - switch (tblType){ - case SPTableTypeTable: - tableType = NSLocalizedString(@"table",@"table"); - [copyTableContentSwitch setEnabled:YES]; - break; - case SPTableTypeView: - tableType = NSLocalizedString(@"view",@"view"); - [copyTableContentSwitch setEnabled:NO]; - break; - case SPTableTypeProc: - tableType = NSLocalizedString(@"procedure",@"procedure"); - [copyTableContentSwitch setEnabled:NO]; - break; - case SPTableTypeFunc: - tableType = NSLocalizedString(@"function",@"function"); - [copyTableContentSwitch setEnabled:NO]; - break; - } - - [copyTableMessageField setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Duplicate %@ '%@' to:", @"duplicate object message"), tableType, [self tableName]]]; - - //open copyTableSheet - [copyTableNameField setStringValue:[NSString stringWithFormat:@"%@_copy", [filteredTables objectAtIndex:[tablesListView selectedRow]]]]; - [copyTableContentSwitch setState:NSOffState]; - - [NSApp beginSheet:copyTableSheet - modalForWindow:[tableDocumentInstance parentWindow] - modalDelegate:self - didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) - contextInfo:@"copyTable"]; -} - -/** - * This action starts editing the table name in the table list - */ -- (IBAction)renameTable:(id)sender -{ - if ((![tableSourceInstance saveRowOnDeselect]) || (![tableContentInstance saveRowOnDeselect]) || (![tableDocumentInstance database])) { - return; - } - - [[tableDocumentInstance parentWindow] endEditingFor:nil]; - - if ([tablesListView numberOfSelectedRows] != 1) return; - if (![[self tableName] length]) return; - - [tablesListView editColumn:0 row:[tablesListView selectedRow] withEvent:nil select:YES]; - - /* - - [tableRenameField setStringValue:[self tableName]]; - [renameTableButton setEnabled:NO]; - - NSString *tableType; - - switch([self tableType]){ - case SPTableTypeTable: - tableType = NSLocalizedString(@"table",@"table"); - break; - case SPTableTypeView: - tableType = NSLocalizedString(@"view",@"view"); - break; - case SPTableTypeProc: - tableType = NSLocalizedString(@"procedure",@"procedure"); - break; - case SPTableTypeFunc: - tableType = NSLocalizedString(@"function",@"function"); - break; - } - - [tableRenameText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Rename %@ '%@' to:",@"rename item name to:"), tableType, [self tableName]]]; - - - [NSApp beginSheet:tableRenameSheet - modalForWindow:[tableDocumentInstance parentWindow] - modalDelegate:self - didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) - contextInfo:@"renameTable"]; - */ -} - -/** - * Truncates the currently selected table(s). - */ -- (IBAction)truncateTable:(id)sender -{ - if (![tablesListView numberOfSelectedRows]) - return; - - [[tableDocumentInstance parentWindow] endEditingFor:nil]; - - NSAlert *alert = [NSAlert alertWithMessageText:@"" - defaultButton:NSLocalizedString(@"Truncate", @"truncate button") - alternateButton:NSLocalizedString(@"Cancel", @"cancel button") - otherButton:nil - informativeTextWithFormat:@""]; - - [alert setAlertStyle:NSCriticalAlertStyle]; - - NSArray *buttons = [alert buttons]; - - // Change the alert's cancel button to have the key equivalent of return - [[buttons objectAtIndex:0] setKeyEquivalent:@"t"]; - [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; - [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - - if ([tablesListView numberOfSelectedRows] == 1) { - [alert setMessageText:[NSString stringWithFormat:NSLocalizedString(@"Truncate table '%@'?", @"truncate table message"), [filteredTables objectAtIndex:[tablesListView selectedRow]]]]; - [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to delete ALL records in the table '%@'? This operation cannot be undone.", @"truncate table informative message"), [filteredTables objectAtIndex:[tablesListView selectedRow]]]]; - } - else { - [alert setMessageText:NSLocalizedString(@"Truncate selected tables?", @"truncate tables message")]; - [alert setInformativeText:NSLocalizedString(@"Are you sure you want to delete ALL records in the selected tables? This operation cannot be undone.", @"truncate tables informative message")]; - } - - [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"truncateTable"]; -} - -/** - * Toggle whether the splitview is collapsed. - */ -- (IBAction)togglePaneCollapse:(id)sender -{ - [tableListSplitView toggleCollapse:sender]; - [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:([tableInfoCollapseButton state] == NSOffState)] forKey:SPTableInformationPanelCollapsed]; - [tableInfoCollapseButton setToolTip:([tableInfoCollapseButton state] == NSOffState) ? NSLocalizedString(@"Show Table Information", @"Show Table Information") : NSLocalizedString(@"Hide Table Information", @"Hide Table Information")]; -} - -#pragma mark - -#pragma mark Alert sheet methods - -/** - * Method for alert sheets. - */ -- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo -{ - // Order out current sheet to suppress overlapping of sheets - if ([sheet respondsToSelector:@selector(orderOut:)]) - [sheet orderOut:nil]; - else if ([sheet respondsToSelector:@selector(window)]) - [[sheet window] orderOut:nil]; - - if ([contextInfo isEqualToString:@"addRow"]) { - alertSheetOpened = NO; - } - else if ([contextInfo isEqualToString:@"removeRow"]) { - if (returnCode == NSAlertDefaultReturn) { - [self performSelector:@selector(removeTable) withObject:nil afterDelay:0.0]; - } - } - else if ([contextInfo isEqualToString:@"truncateTable"]) { - if (returnCode == NSAlertDefaultReturn) { - [self truncateTable]; - } - } - else if ([contextInfo isEqualToString:@"addTable"]) { - if (returnCode == NSOKButton) { - [self addTable]; - } - } - else if ([contextInfo isEqualToString:@"copyTable"]) { - if (returnCode == NSOKButton) { - [self copyTable]; - } - } -} - -#pragma mark - -#pragma mark Additional methods - -/** - * Sets the connection (received from TableDocument) and makes things that have to be done only once - */ -- (void)setConnection:(MCPConnection *)theConnection -{ - mySQLConnection = theConnection; - [self updateTables:self]; -} - -/** - * Selects customQuery tab and passes query to customQueryInstance - */ -- (void)doPerformQueryService:(NSString *)query -{ - [tabView selectTabViewItemAtIndex:2]; - [customQueryInstance doPerformQueryService:query]; -} - -/** - * Performs interface validation for various controls. - */ -- (void)controlTextDidChange:(NSNotification *)notification -{ - id object = [notification object]; - - if (object == tableNameField) { - [addTableButton setEnabled:[self isTableNameValid:[tableNameField stringValue] forType: SPTableTypeTable]]; - } - - else if (object == copyTableNameField) { - [copyTableButton setEnabled:[self isTableNameValid:[copyTableNameField stringValue] forType:[self tableType]]]; - } -} - -/* - * Controls the NSTextField's press RETURN event of Add/Rename/Duplicate sheets - */ -- (void)controlTextDidEndEditing:(NSNotification *)notification -{ - id object = [notification object]; - - // Only RETURN/ENTER will be recognized for Add/Rename/Duplicate sheets to - // activate the Add/Rename/Duplicate buttons - if([[[notification userInfo] objectForKey:@"NSTextMovement"] integerValue] != 0) - return; - - if (object == tableNameField) { - [addTableButton performClick:object]; - } - else if (object == copyTableNameField) { - [copyTableButton performClick:object]; - } -} - -/** - * Updates the current table selection. Triggered most times tableViewSelectionDidChange: - * fires, and also as a result of certain table actions. - */ -- (void)updateSelectionWithTaskString:(NSString *)taskString -{ - if (![mySQLConnection isConnected]) return; - - // If there is a multiple or blank selection, clear all views directly. - if ( [tablesListView numberOfSelectedRows] != 1 || ![(NSString *)[filteredTables objectAtIndex:[tablesListView selectedRow]] length] ) { - - // Update the selection variables and the interface - [self performSelectorOnMainThread:@selector(setSelection:) withObject:nil waitUntilDone:YES]; - - // Add a history entry - [spHistoryControllerInstance updateHistoryEntries]; - - // Notify listeners of the table change now that the state is fully set up - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPTableChangedNotification object:tableDocumentInstance]; - - return; - } - - // Otherwise, set up a task - [tableDocumentInstance startTaskWithDescription:taskString]; - - // If on the main thread, fire up a thread to deal with view changes and data loading, else perform inline - if ([NSThread isMainThread]) { - [NSThread detachNewThreadSelector:@selector(updateSelectionTask) toTarget:self withObject:nil]; - } else { - [self updateSelectionTask]; - } -} - -- (void) updateSelectionTask -{ - NSAutoreleasePool *selectionChangePool = [[NSAutoreleasePool alloc] init]; - NSString *tableEncoding = nil; - - // Update selection variables and interface - NSDictionary *selectionDetails = [NSDictionary dictionaryWithObjectsAndKeys: - [filteredTables objectAtIndex:[tablesListView selectedRow]], @"name", - [filteredTableTypes objectAtIndex:[tablesListView selectedRow]], @"type", - nil]; - [self performSelectorOnMainThread:@selector(setSelection:) withObject:selectionDetails waitUntilDone:YES]; - - // Check the encoding if appropriate to determine if an encoding change and reset is required - if( selectedTableType == SPTableTypeView || selectedTableType == SPTableTypeTable) { - - // tableEncoding == nil indicates that there was an error while retrieving table data - tableEncoding = [tableDataInstance tableEncoding]; - - // If encoding is set to Autodetect, update the connection character set encoding - // based on the newly selected table's encoding - but only if it differs from the current encoding. - if ([[[NSUserDefaults standardUserDefaults] objectForKey:SPDefaultEncoding] isEqualToString:@"Autodetect"]) { - if (tableEncoding != nil && ![tableEncoding isEqualToString:[tableDocumentInstance connectionEncoding]]) { - [tableDocumentInstance setConnectionEncoding:tableEncoding reloadingViews:NO]; - [tableDataInstance resetAllData]; - tableEncoding = [tableDataInstance tableEncoding]; - } - } - } - - // Ensure status information is cached on the working thread - [tableDataInstance updateStatusInformationForCurrentTable]; - - // Notify listeners of the table change now that the state is fully set up. - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPTableChangedNotification object:tableDocumentInstance]; - - // Restore view states as appropriate - [spHistoryControllerInstance restoreViewStates]; - - structureLoaded = NO; - contentLoaded = NO; - statusLoaded = NO; - triggersLoaded = NO; - if( selectedTableType == SPTableTypeView || selectedTableType == SPTableTypeTable) { - if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewStructure ) { - [tableSourceInstance loadTable:selectedTableName]; - structureLoaded = YES; - } else if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewContent ) { - if(tableEncoding == nil) { - [tableContentInstance loadTable:nil]; - } else { - [tableContentInstance loadTable:selectedTableName]; - } - contentLoaded = YES; - } else if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewStatus ) { - [[extendedTableInfoInstance onMainThread] loadTable:selectedTableName]; - statusLoaded = YES; - } else if ( [tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewTriggers ) { - [[tableTriggersInstance onMainThread] loadTriggers]; - triggersLoaded = YES; - } - } else { - - // if we are not looking at a table or view, clear these - [tableSourceInstance loadTable:nil]; - [tableContentInstance loadTable:nil]; - [[extendedTableInfoInstance onMainThread] loadTable:nil]; - [[tableTriggersInstance onMainThread] loadTriggers]; - } - - // Update the "Show Create Syntax" window if it's already opened - // according to the selected table/view/proc/func - if([[tableDocumentInstance getCreateTableSyntaxWindow] isVisible]) - [tableDocumentInstance performSelectorOnMainThread:@selector(showCreateTableSyntax:) withObject:self waitUntilDone:YES]; - - // Add a history entry - [spHistoryControllerInstance updateHistoryEntries]; - - // Empty the loading pool and exit the thread - [tableDocumentInstance endTask]; - [selectionChangePool drain]; -} - -/** - * Takes a dictionary of selection details, containing the selection name - * and type, and updates stored variables and the table list interface to - * match. - * Should be called on the main thread. - */ -- (void)setSelection:(NSDictionary *)selectionDetails -{ - // First handle empty or multiple selections - if (!selectionDetails || ![selectionDetails objectForKey:@"name"]) { - NSIndexSet *indexes = [tablesListView selectedRowIndexes]; - - // Update the selected table name and type - if (selectedTableName) [selectedTableName release]; - - if ([indexes count]) { - selectedTableName = [[NSString alloc] initWithString:@""]; - } - else { - selectedTableName = nil; - } - - selectedTableType = SPTableTypeNone; - - [tableSourceInstance loadTable:nil]; - [tableContentInstance loadTable:nil]; - [extendedTableInfoInstance loadTable:nil]; - [tableTriggersInstance loadTriggers]; - - structureLoaded = NO; - contentLoaded = NO; - statusLoaded = NO; - triggersLoaded = NO; - - // Set gear menu items Remove/Duplicate table/view according to the table types - // if at least one item is selected - if ([indexes count]) { - - NSUInteger currentIndex = [indexes lastIndex]; - BOOL areTableTypeEqual = YES; - NSInteger lastType = [[filteredTableTypes objectAtIndex:currentIndex] integerValue]; - - while (currentIndex != NSNotFound) - { - if ([[filteredTableTypes objectAtIndex:currentIndex] integerValue] != lastType) { - areTableTypeEqual = NO; - break; - } - - currentIndex = [indexes indexLessThanIndex:currentIndex]; - } - - if (areTableTypeEqual) { - switch (lastType) { - case SPTableTypeTable: - [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Tables", @"delete tables menu title")]; - [truncateTableButton setTitle:NSLocalizedString(@"Truncate Tables", @"truncate tables menu item")]; - [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Tables", @"delete tables menu title")]; - [truncateTableContextButton setTitle:NSLocalizedString(@"Truncate Tables", @"truncate tables menu item")]; - [truncateTableButton setHidden:NO]; - [truncateTableContextButton setHidden:NO]; - break; - case SPTableTypeView: - [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Views", @"delete views menu title")]; - [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Views", @"delete views menu title")]; - [truncateTableButton setHidden:YES]; - [truncateTableContextButton setHidden:YES]; - break; - case SPTableTypeProc: - [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Procedures", @"delete procedures menu title")]; - [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Procedures", @"delete procedures menu title")]; - [truncateTableButton setHidden:YES]; - [truncateTableContextButton setHidden:YES]; - break; - case SPTableTypeFunc: - [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Functions", @"delete functions menu title")]; - [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Functions", @"delete functions menu title")]; - [truncateTableButton setHidden:YES]; - [truncateTableContextButton setHidden:YES]; - break; - } - - } else { - [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Items", @"delete items menu title")]; - [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Items", @"delete items menu title")]; - [truncateTableButton setHidden:YES]; - [truncateTableContextButton setHidden:YES]; - } - } - - // Context menu - [renameTableContextMenuItem setHidden:YES]; - [duplicateTableContextMenuItem setHidden:YES]; - [separatorTableContextMenuItem setHidden:YES]; - [separatorTableContextMenuItem2 setHidden:YES]; - [showCreateSyntaxContextMenuItem setHidden:YES]; - - // 'Gear' menu - [renameTableMenuItem setHidden:YES]; - [duplicateTableMenuItem setHidden:YES]; - [separatorTableMenuItem setHidden:YES]; - [separatorTableMenuItem2 setHidden:YES]; - [showCreateSyntaxMenuItem setHidden:YES]; - - NSMenu *tableSubMenu = [[[NSApp mainMenu] itemWithTitle:@"Table"] submenu]; - - [[tableSubMenu itemAtIndex:6] setTitle:NSLocalizedString(@"Check Selected Items", @"check selected items menu item")]; - [[tableSubMenu itemAtIndex:7] setTitle:NSLocalizedString(@"Repair Selected Items", @"repair selected items menu item")]; - - [[tableSubMenu itemAtIndex:9] setTitle:NSLocalizedString(@"Analyze Selected Items", @"analyze selected items menu item")]; - [[tableSubMenu itemAtIndex:10] setTitle:NSLocalizedString(@"Optimize Selected Items", @"optimize selected items menu item")]; - - [[tableSubMenu itemAtIndex:11] setTitle:NSLocalizedString(@"Flush Selected Items", @"flush selected items menu item")]; - [[tableSubMenu itemAtIndex:12] setTitle:NSLocalizedString(@"Checksum Selected Items", @"checksum selected items menu item")]; - - [[tableSubMenu itemAtIndex:3] setHidden:NO]; - [[tableSubMenu itemAtIndex:4] setHidden:NO]; - [[tableSubMenu itemAtIndex:5] setHidden:NO]; - [[tableSubMenu itemAtIndex:6] setHidden:NO]; - [[tableSubMenu itemAtIndex:7] setHidden:NO]; - [[tableSubMenu itemAtIndex:8] setHidden:NO]; - [[tableSubMenu itemAtIndex:9] setHidden:NO]; - [[tableSubMenu itemAtIndex:10] setHidden:NO]; - - // set window title - [tableDocumentInstance updateWindowTitle:self]; - - return; - } - - // If a new selection has been provided, store variables and update the interface to match - NSString *selectedItemName = [selectionDetails objectForKey:@"name"]; - NSInteger selectedItemType = [[selectionDetails objectForKey:@"type"] integerValue]; - - // Update the selected table name and type - if (selectedTableName) [selectedTableName release]; - selectedTableName = [[NSString alloc] initWithString:selectedItemName]; - selectedTableType = selectedItemType; - - // Remove the "current selection" item for filtered lists if appropriate - if (isTableListFiltered && [tablesListView selectedRow] < [filteredTables count] - 2 && [filteredTables count] > 2 - && [[filteredTableTypes objectAtIndex:[filteredTableTypes count]-2] integerValue] == SPTableTypeNone - && [[filteredTables objectAtIndex:[filteredTables count]-2] isEqualToString:NSLocalizedString(@"CURRENT SELECTION",@"header for current selection in filtered list")]) - { - [filteredTables removeObjectsInRange:NSMakeRange([filteredTables count]-2, 2)]; - [filteredTableTypes removeObjectsInRange:NSMakeRange([filteredTableTypes count]-2, 2)]; - [tablesListView reloadData]; - } - - // Reset the table information caches - [tableDataInstance resetAllData]; - - // Show menu separatoes - [separatorTableMenuItem setHidden:NO]; - [separatorTableContextMenuItem setHidden:NO]; - [separatorTableMenuItem2 setHidden:NO]; - [separatorTableContextMenuItem2 setHidden:NO]; - - // Set gear menu items Remove/Duplicate table/view and mainMenu > Table items - // according to the table types - NSMenu *tableSubMenu = [[[NSApp mainMenu] itemWithTitle:@"Table"] submenu]; - - // Enable/disable the various menu items depending on the selected item. Also update their titles. - // Note, that this should ideally be moved to menu item validation as opposed to using fixed item positions. - if (selectedTableType == SPTableTypeView) - { - // Change mainMenu > Table > ... according to table type - [[tableSubMenu itemAtIndex:3] setTitle:NSLocalizedString(@"Copy Create View Syntax", @"copy create view syntax menu item")]; - [[tableSubMenu itemAtIndex:4] setTitle:NSLocalizedString(@"Show Create View Syntax...", @"show create view syntax menu item")]; - [[tableSubMenu itemAtIndex:5] setHidden:NO]; // Divider - [[tableSubMenu itemAtIndex:6] setHidden:NO]; - [[tableSubMenu itemAtIndex:6] setTitle:NSLocalizedString(@"Check View", @"check view menu item")]; - [[tableSubMenu itemAtIndex:7] setHidden:YES]; // Repair - [[tableSubMenu itemAtIndex:8] setHidden:YES]; // Divider - [[tableSubMenu itemAtIndex:9] setHidden:YES]; // Analyse - [[tableSubMenu itemAtIndex:10] setHidden:YES]; // Optimize - [[tableSubMenu itemAtIndex:11] setHidden:NO]; - [[tableSubMenu itemAtIndex:11] setTitle:NSLocalizedString(@"Flush View", @"flush view menu item")]; - [[tableSubMenu itemAtIndex:12] setHidden:YES]; // Checksum - - [renameTableMenuItem setHidden:NO]; // we don't have to check the mysql version - [renameTableMenuItem setTitle:NSLocalizedString(@"Rename View...", @"rename view menu title")]; - [duplicateTableMenuItem setHidden:NO]; - [duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate View...", @"duplicate view menu title")]; - [truncateTableButton setHidden:YES]; - [removeTableMenuItem setTitle:NSLocalizedString(@"Delete View", @"delete view menu title")]; - [showCreateSyntaxMenuItem setHidden:NO]; - [showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create View Syntax...", @"show create view syntax menu item")]; - - [renameTableContextMenuItem setHidden:NO]; // we don't have to check the mysql version - [renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename View...", @"rename view menu title")]; - [duplicateTableContextMenuItem setHidden:NO]; - [duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate View...", @"duplicate view menu title")]; - [truncateTableContextButton setHidden:YES]; - [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete View", @"delete view menu title")]; - [showCreateSyntaxContextMenuItem setHidden:NO]; - [showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create View Syntax...", @"show create view syntax menu item")]; - } - else if (selectedTableType == SPTableTypeTable) { - [[tableSubMenu itemAtIndex:3] setTitle:NSLocalizedString(@"Copy Create Table Syntax", @"copy create table syntax menu item")]; - [[tableSubMenu itemAtIndex:4] setTitle:NSLocalizedString(@"Show Create Table Syntax...", @"show create table syntax menu item")]; - [[tableSubMenu itemAtIndex:5] setHidden:NO]; // divider - [[tableSubMenu itemAtIndex:6] setHidden:NO]; - [[tableSubMenu itemAtIndex:6] setTitle:NSLocalizedString(@"Check Table", @"check table menu item")]; - [[tableSubMenu itemAtIndex:7] setHidden:NO]; - [[tableSubMenu itemAtIndex:7] setTitle:NSLocalizedString(@"Repair Table", @"repair table menu item")]; - [[tableSubMenu itemAtIndex:8] setHidden:NO]; // divider - [[tableSubMenu itemAtIndex:9] setHidden:NO]; - [[tableSubMenu itemAtIndex:9] setTitle:NSLocalizedString(@"Analyze Table", @"analyze table menu item")]; - [[tableSubMenu itemAtIndex:10] setHidden:NO]; - [[tableSubMenu itemAtIndex:10] setTitle:NSLocalizedString(@"Optimize Table", @"optimize table menu item")]; - [[tableSubMenu itemAtIndex:11] setHidden:NO]; - [[tableSubMenu itemAtIndex:11] setTitle:NSLocalizedString(@"Flush Table", @"flush table menu item")]; - [[tableSubMenu itemAtIndex:12] setHidden:NO]; - [[tableSubMenu itemAtIndex:12] setTitle:NSLocalizedString(@"Checksum Table", @"checksum table menu item")]; - - [renameTableMenuItem setHidden:NO]; - [renameTableMenuItem setTitle:NSLocalizedString(@"Rename Table...", @"rename table menu title")]; - [duplicateTableMenuItem setHidden:NO]; - [duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate Table...", @"duplicate table menu title")]; - [truncateTableButton setHidden:NO]; - [truncateTableButton setTitle:NSLocalizedString(@"Truncate Table", @"truncate table menu title")]; - [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Table", @"delete table menu title")]; - [showCreateSyntaxMenuItem setHidden:NO]; - [showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create Table Syntax...", @"show create table syntax menu item")]; - - [renameTableContextMenuItem setHidden:NO]; - [renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename Table...", @"rename table menu title")]; - [duplicateTableContextMenuItem setHidden:NO]; - [duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate Table...", @"duplicate table menu title")]; - [truncateTableContextButton setHidden:NO]; - [truncateTableContextButton setTitle:NSLocalizedString(@"Truncate Table", @"truncate table menu title")]; - [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Table", @"delete table menu title")]; - [showCreateSyntaxContextMenuItem setHidden:NO]; - [showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create Table Syntax...", @"show create table syntax menu item")]; - } - else if (selectedTableType == SPTableTypeProc) { - [[tableSubMenu itemAtIndex:3] setTitle:NSLocalizedString(@"Copy Create Procedure Syntax", @"copy create proc syntax menu item")]; - [[tableSubMenu itemAtIndex:4] setTitle:NSLocalizedString(@"Show Create Procedure Syntax...", @"show create proc syntax menu item")]; - [[tableSubMenu itemAtIndex:5] setHidden:YES]; // divider - [[tableSubMenu itemAtIndex:6] setHidden:YES]; // copy columns - [[tableSubMenu itemAtIndex:7] setHidden:YES]; // divider - [[tableSubMenu itemAtIndex:8] setHidden:YES]; - [[tableSubMenu itemAtIndex:9] setHidden:YES]; - [[tableSubMenu itemAtIndex:10] setHidden:YES]; // divider - [[tableSubMenu itemAtIndex:11] setHidden:YES]; - [[tableSubMenu itemAtIndex:12] setHidden:YES]; - - [renameTableMenuItem setHidden:NO]; - [renameTableMenuItem setTitle:NSLocalizedString(@"Rename Procedure...", @"rename proc menu title")]; - [duplicateTableMenuItem setHidden:NO]; - [duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate Procedure...", @"duplicate proc menu title")]; - [truncateTableButton setHidden:YES]; - [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Procedure", @"delete proc menu title")]; - [showCreateSyntaxMenuItem setHidden:NO]; - [showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create Procedure Syntax...", @"show create proc syntax menu item")]; - - [renameTableContextMenuItem setHidden:NO]; - [renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename Procedure...", @"rename proc menu title")]; - [duplicateTableContextMenuItem setHidden:NO]; - [duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate Procedure...", @"duplicate proc menu title")]; - [truncateTableContextButton setHidden:YES]; - [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Procedure", @"delete proc menu title")]; - [showCreateSyntaxContextMenuItem setHidden:NO]; - [showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create Procedure Syntax...", @"show create proc syntax menu item")]; - } - else if (selectedTableType == SPTableTypeFunc) { - [[tableSubMenu itemAtIndex:3] setTitle:NSLocalizedString(@"Copy Create Function Syntax", @"copy create func syntax menu item")]; - [[tableSubMenu itemAtIndex:4] setTitle:NSLocalizedString(@"Show Create Function Syntax...", @"show create func syntax menu item")]; - [[tableSubMenu itemAtIndex:5] setHidden:YES]; // divider - [[tableSubMenu itemAtIndex:6] setHidden:YES]; // copy columns - [[tableSubMenu itemAtIndex:7] setHidden:YES]; // divider - [[tableSubMenu itemAtIndex:8] setHidden:YES]; - [[tableSubMenu itemAtIndex:9] setHidden:YES]; - [[tableSubMenu itemAtIndex:10] setHidden:YES]; // divider - [[tableSubMenu itemAtIndex:11] setHidden:YES]; - [[tableSubMenu itemAtIndex:12] setHidden:YES]; - - [renameTableMenuItem setHidden:NO]; - [renameTableMenuItem setTitle:NSLocalizedString(@"Rename Function...", @"rename func menu title")]; - [duplicateTableMenuItem setHidden:NO]; - [duplicateTableMenuItem setTitle:NSLocalizedString(@"Duplicate Function...", @"duplicate func menu title")]; - [truncateTableButton setHidden:YES]; - [removeTableMenuItem setTitle:NSLocalizedString(@"Delete Function", @"delete func menu title")]; - [showCreateSyntaxMenuItem setHidden:NO]; - [showCreateSyntaxMenuItem setTitle:NSLocalizedString(@"Show Create Function Syntax...", @"show create func syntax menu item")]; - - [renameTableContextMenuItem setHidden:NO]; - [renameTableContextMenuItem setTitle:NSLocalizedString(@"Rename Function...", @"rename func menu title")]; - [duplicateTableContextMenuItem setHidden:NO]; - [duplicateTableContextMenuItem setTitle:NSLocalizedString(@"Duplicate Function...", @"duplicate func menu title")]; - [truncateTableContextButton setHidden:YES]; - [removeTableContextMenuItem setTitle:NSLocalizedString(@"Delete Function", @"delete func menu title")]; - [showCreateSyntaxContextMenuItem setHidden:NO]; - [showCreateSyntaxContextMenuItem setTitle:NSLocalizedString(@"Show Create Function Syntax...", @"show create func syntax menu item")]; - } - - // set window title - [tableDocumentInstance updateWindowTitle:self]; -} - -#pragma mark - -#pragma mark Getter methods - - -- (NSArray *)selectedTableNames -{ - NSIndexSet *indexes = [tablesListView selectedRowIndexes]; - - NSUInteger currentIndex = [indexes firstIndex]; - NSMutableArray *selTables = [NSMutableArray array]; - - while (currentIndex != NSNotFound) { - if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeTable) - [selTables addObject:[filteredTables objectAtIndex:currentIndex]]; - currentIndex = [indexes indexGreaterThanIndex:currentIndex]; - } - return selTables; -} - -- (NSArray *)selectedTableItems -{ - NSIndexSet *indexes = [tablesListView selectedRowIndexes]; - - NSUInteger currentIndex = [indexes firstIndex]; - NSMutableArray *selTables = [NSMutableArray array]; - - while (currentIndex != NSNotFound) { - [selTables addObject:[filteredTables objectAtIndex:currentIndex]]; - currentIndex = [indexes indexGreaterThanIndex:currentIndex]; - } - return selTables; -} - -/** - * Returns the currently selected table or nil if no table or mulitple tables are selected - */ -- (NSString *)tableName -{ - return selectedTableName; -} - -/* - * Returns the currently selected table type, or -1 if no table or multiple tables are selected - */ -- (NSInteger) tableType -{ - return selectedTableType; -} - -/** - * Database tables accessor - */ -- (NSArray *)tables -{ - return tables; -} - -/** - * Database tables accessors for a given table type - */ -- (NSArray *)allTableAndViewNames -{ - NSMutableArray *returnArray = [NSMutableArray array]; - NSInteger i; - NSInteger cnt = [[self tables] count]; - for(i=0; i= [filteredTables count]) return @""; - - return [filteredTables objectAtIndex:rowIndex]; -} - -/** - * Prevent table renames while tasks are active - */ -- (BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex -{ - return ![tableDocumentInstance isWorking]; -} - -/** - * Renames a table (in tables-array and mysql-db). - */ -- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex -{ - //first trim whitespace whitespace - NSString *newTableName = [anObject stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - - if ([selectedTableName isEqualToString:newTableName]) { - // No changes in table name - return; - } - - if ([newTableName isEqualToString:@""]) { - // empty table names are not allowed - // don't annoy the user about it, just ignore this - // this is also how the MacOS Finder handles renaming files - return; - } - - if (![self isTableNameValid:newTableName forType:selectedTableType ignoringSelectedTable:YES]) { - // Table has invalid name - // Since we trimmed whitespace and checked for empty string, this means there is already a table with that name - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, - @selector(sheetDidEnd:returnCode:contextInfo:), nil, - [NSString stringWithFormat: NSLocalizedString(@"The name '%@' is already used.", @"message when trying to rename a table/view/proc/etc to an already used name"), newTableName]); - return; - } - - @try { - // first: update the database - [self renameTableOfType:selectedTableType from:selectedTableName to:newTableName]; - - // second: update the table list - if (isTableListFiltered) { - NSInteger unfilteredIndex = [tables indexOfObject:[filteredTables objectAtIndex:rowIndex]]; - [tables replaceObjectAtIndex:unfilteredIndex withObject:newTableName]; - } - [filteredTables replaceObjectAtIndex:rowIndex withObject:newTableName]; - if (selectedTableName) [selectedTableName release]; - selectedTableName = [[NSString alloc] initWithString:newTableName]; - - // if the 'table' is a view or a table, reload the currently selected view - if (selectedTableType == SPTableTypeTable || selectedTableType == SPTableTypeView) - { - statusLoaded = NO; - structureLoaded = NO; - contentLoaded = NO; - triggersLoaded = NO; - switch ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]]) { - case SPTableViewStructure: - [tableSourceInstance loadTable:newTableName]; - structureLoaded = YES; - break; - case SPTableViewContent: - [tableContentInstance loadTable:newTableName]; - contentLoaded = YES; - break; - case SPTableViewStatus: - [extendedTableInfoInstance loadTable:newTableName]; - statusLoaded = YES; - break; - case SPTableViewTriggers: - [tableTriggersInstance loadTriggers]; - triggersLoaded = YES; - break; - } - } - } - @catch (NSException * myException) { - SPBeginAlertSheet( NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, [myException reason]); - } - - // Set window title to reflect the new table name - [tableDocumentInstance updateWindowTitle:self]; - - // Query the structure of all databases in the background (mainly for completion) - [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", nil]]; -} - -#pragma mark - -#pragma mark TableView delegate methods - -/** - * Traps enter and esc and edit/cancel without entering next row - */ -- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command -{ - if ( [textView methodForSelector:command] == [textView methodForSelector:@selector(insertNewline:)] ) { - //save current line - [[control window] makeFirstResponder:control]; - return TRUE; - - } else if ( [[control window] methodForSelector:command] == [[control window] methodForSelector:@selector(_cancelKey:)] || - [textView methodForSelector:command] == [textView methodForSelector:@selector(complete:)] ) { - - //abort editing - [control abortEditing]; - - return TRUE; - } else{ - return FALSE; - } -} - -/** - * Table view delegate method - */ -- (BOOL)selectionShouldChangeInTableView:(NSTableView *)aTableView -{ - - // Don't allow selection changes while performing a task. - if (!tableListIsSelectable) return NO; - - // End editing (otherwise problems when user hits reload button) - [[tableDocumentInstance parentWindow] endEditingFor:nil]; - - if ( alertSheetOpened ) { - return NO; - } - - // We have to be sure that document views have finished editing - return [tableDocumentInstance couldCommitCurrentViewActions]; -} - -/** - * Loads a table in content or source view (if tab selected) - */ -- (void)tableViewSelectionDidChange:(NSNotification *)aNotification -{ - - // Reset selectability after change if necessary - if ([tableDocumentInstance isWorking]) tableListIsSelectable = NO; - - // Perform no action if the selected table hasn't actually changed - reselection etc - if ([tablesListView numberOfSelectedRows] == 1 - && [(NSString *)[filteredTables objectAtIndex:[tablesListView selectedRow]] length] - && [selectedTableName isEqualToString:[filteredTables objectAtIndex:[tablesListView selectedRow]]] - && selectedTableType == [[filteredTableTypes objectAtIndex:[tablesListView selectedRow]] integerValue]) - { - return; - } - - // Save existing scroll position and details - [spHistoryControllerInstance updateHistoryEntries]; - - NSString *tableName = @"data"; - if ([tablesListView numberOfSelectedRows] == 1 && [(NSString *)[filteredTables objectAtIndex:[tablesListView selectedRow]] length]) - tableName = [filteredTables objectAtIndex:[tablesListView selectedRow]]; - [self updateSelectionWithTaskString:[NSString stringWithFormat:NSLocalizedString(@"Loading %@...", @"Loading table task string"), tableName]]; - - if([[SPNavigatorController sharedNavigatorController] syncMode] && [tablesListView numberOfSelectedRows] == 1) { - NSMutableString *schemaPath = [NSMutableString string]; - [schemaPath setString:[tableDocumentInstance connectionID]]; - if([tableDocumentInstance database] && [[tableDocumentInstance database] length]) { - [schemaPath appendString:SPUniqueSchemaDelimiter]; - [schemaPath appendString:[tableDocumentInstance database]]; - if(tableName && [tableName length]) { - [schemaPath appendString:SPUniqueSchemaDelimiter]; - [schemaPath appendString:tableName]; - } - } - [[SPNavigatorController sharedNavigatorController] selectPath:schemaPath]; - } -} - -/** - * Table view delegate method - */ -- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex -{ - - // Disallow selection while the document is working on a task - if ([tableDocumentInstance isWorking]) return NO; - - //return (rowIndex != 0); - if( [filteredTableTypes count] == 0 ) - return (rowIndex != 0 ); - return ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] != SPTableTypeNone ); -} - -/** - * Table view delegate method - */ -- (BOOL)tableView:(NSTableView *)aTableView isGroupRow:(NSInteger)rowIndex -{ - // For empty tables - title still present - or while lists are being altered - if (rowIndex >= [filteredTableTypes count]) return (rowIndex == 0 ); - - return ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeNone ); -} - -/** - * Table view delegate method - */ -- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex -{ - if (rowIndex > 0 && rowIndex < [filteredTableTypes count] - && [[aTableColumn identifier] isEqualToString:@"tables"]) { - if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeView) { - [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"table-view-small"]]; - } else if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeTable) { - [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"table-small"]]; - } else if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeProc) { - [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"proc-small"]]; - } else if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeFunc) { - [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"func-small"]]; - } - - if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeNone) { - [(ImageAndTextCell*)aCell setImage:nil]; - [(ImageAndTextCell*)aCell setIndentationLevel:0]; - } else { - [(ImageAndTextCell*)aCell setIndentationLevel:1]; - [(ImageAndTextCell*)aCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - } - } else { - [(ImageAndTextCell*)aCell setImage:nil]; - [(ImageAndTextCell*)aCell setIndentationLevel:0]; - } -} - -/** - * Table view delegate method - */ -- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row -{ - return (row == 0) ? 25 : 17; -} - -#pragma mark - -#pragma mark TabView delegate methods - -/** - * Loads structure or source if tab selected the first time, - * using a threaded load if currently on the main thread. - */ -- (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem -{ - [tableDocumentInstance startTaskWithDescription:[NSString stringWithFormat:NSLocalizedString(@"Loading %@...", @"Loading table task string"), selectedTableName]]; - if ([NSThread isMainThread]) { - [NSThread detachNewThreadSelector:@selector(loadTabTask:) toTarget:self withObject:tabViewItem]; - } else { - [self loadTabTask:tabViewItem]; - } -} -- (void)loadTabTask:(NSTabViewItem *)tabViewItem -{ - NSAutoreleasePool *tabLoadPool = [[NSAutoreleasePool alloc] init]; - - if ([tablesListView numberOfSelectedRows] == 1 - && ([self tableType] == SPTableTypeTable || [self tableType] == SPTableTypeView) ) - { - - if ( ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewStructure) && !structureLoaded ) { - [tableSourceInstance loadTable:selectedTableName]; - structureLoaded = YES; - } - - if ( ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewContent) && !contentLoaded ) { - [tableContentInstance loadTable:selectedTableName]; - contentLoaded = YES; - } - - if ( ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewStatus) && !statusLoaded ) { - [[extendedTableInfoInstance onMainThread] loadTable:selectedTableName]; - statusLoaded = YES; - } - - if ( ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewTriggers) && !triggersLoaded ) { - [[tableTriggersInstance onMainThread] loadTriggers]; - triggersLoaded = YES; - } - } - else { - [tableSourceInstance loadTable:nil]; - [tableContentInstance loadTable:nil]; - } - - [tableDocumentInstance endTask]; - [tabLoadPool drain]; -} - -/** - * Menu item interface validation - */ -- (BOOL)validateMenuItem:(NSMenuItem *)menuItem -{ - // popup button below table list - if ([menuItem action] == @selector(copyTable:)) { - return (([tablesListView numberOfSelectedRows] == 1) && [[self tableName] length] && [tablesListView numberOfSelectedRows] > 0); - } - - if ([menuItem action] == @selector(removeTable:) || [menuItem action] == @selector(truncateTable:)) { - return ([tablesListView numberOfSelectedRows] > 0); - } - - if ([menuItem action] == @selector(renameTable:)) { - return (([tablesListView numberOfSelectedRows] == 1) && [[self tableName] length]); - } - - return [super validateMenuItem:menuItem]; -} - -#pragma mark - -#pragma mark Table list filter interaction - -/** - * Show the filter box if it's currently hidden. Use a delay to ensure - * action is executed on first load. - */ -- (void) showFilter -{ - if ([tableListFilterSplitView collapsibleSubviewIsCollapsed]) - [tableListFilterSplitView performSelectorOnMainThread:@selector(toggleCollapse:) withObject:nil waitUntilDone:NO]; -} - -/** - * Hide the filter box if it's currently shown. Use a delay to ensure - * action is executed on first load. - */ -- (void) hideFilter -{ - if (![tableListFilterSplitView collapsibleSubviewIsCollapsed]) - [tableListFilterSplitView performSelectorOnMainThread:@selector(toggleCollapse:) withObject:nil waitUntilDone:NO]; -} - -/** - * Clear the current content of the filter box - */ -- (void) clearFilter -{ - [listFilterField setStringValue:@""]; -} - -/** - * Set focus to table list filter search field - */ -- (void) makeTableListFilterHaveFocus -{ - if([tables count] > 20) { - [[tableDocumentInstance parentWindow] makeFirstResponder:listFilterField]; - } - else if([tables count] > 2) { - [[tableDocumentInstance parentWindow] makeFirstResponder:tablesListView]; - if([tablesListView numberOfSelectedRows] < 1) - [tablesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO]; - - } -} - -/** - * Update the filter search. - */ -- (IBAction) updateFilter:(id)sender -{ - - // Don't try and maintain selections of multiple rows through filtering - if ([tablesListView numberOfSelectedRows] > 1) { - [tablesListView deselectAll:self]; - if (selectedTableName) [selectedTableName release], selectedTableName = nil; - } - - if ([[listFilterField stringValue] length]) { - if (isTableListFiltered) { - [filteredTables release]; - [filteredTableTypes release]; - } - filteredTables = [[NSMutableArray alloc] init]; - filteredTableTypes = [[NSMutableArray alloc] init]; - - NSInteger i, lastTableType = NSNotFound, tableType; - NSRange substringRange; - for (i = 0; i < [tables count]; i++) { - tableType = [[tableTypes objectAtIndex:i] integerValue]; - if (tableType == SPTableTypeNone) continue; - substringRange = [[tables objectAtIndex:i] rangeOfString:[listFilterField stringValue] options:NSCaseInsensitiveSearch]; - if (substringRange.location == NSNotFound) continue; - - // Add a title if necessary - if ((tableType == SPTableTypeTable || tableType == SPTableTypeView) && lastTableType == NSNotFound) - { - if (tableListContainsViews) { - [filteredTables addObject:NSLocalizedString(@"TABLES & VIEWS",@"header for table & views list")]; - } else { - [filteredTables addObject:NSLocalizedString(@"TABLES",@"header for table list")]; - } - [filteredTableTypes addObject:[NSNumber numberWithInteger:SPTableTypeNone]]; - } else if ((tableType == SPTableTypeProc || tableType == SPTableTypeFunc) - && (lastTableType == NSNotFound || lastTableType == SPTableTypeTable || lastTableType == SPTableTypeView)) - { - [filteredTables addObject:NSLocalizedString(@"PROCS & FUNCS",@"header for procs & funcs list")]; - [filteredTableTypes addObject:[NSNumber numberWithInteger:SPTableTypeNone]]; - } - lastTableType = tableType; - - // Add the item - [filteredTables addObject:[tables objectAtIndex:i]]; - [filteredTableTypes addObject:[tableTypes objectAtIndex:i]]; - } - - // Add a "no matches" title if nothing matches the current filter settings - if (![filteredTables count]) { - [filteredTables addObject:NSLocalizedString(@"NO MATCHES",@"header for no matches in filtered list")]; - [filteredTableTypes addObject:[NSNumber numberWithInteger:SPTableTypeNone]]; - } - - // If the currently selected table isn't present in the filter list, add it as a special entry - if (selectedTableName && [filteredTables indexOfObject:selectedTableName] == NSNotFound) { - [filteredTables addObject:NSLocalizedString(@"CURRENT SELECTION",@"header for current selection in filtered list")]; - [filteredTableTypes addObject:[NSNumber numberWithInteger:SPTableTypeNone]]; - [filteredTables addObject:selectedTableName]; - [filteredTableTypes addObject:[NSNumber numberWithInteger:selectedTableType]]; - } - - isTableListFiltered = YES; - } else if (isTableListFiltered) { - isTableListFiltered = NO; - [filteredTables release]; - filteredTables = tables; - [filteredTableTypes release]; - filteredTableTypes = tableTypes; - } - - // Reselect correct row and reload the table view display - if ([tablesListView numberOfRows] < [filteredTables count]) [tablesListView noteNumberOfRowsChanged]; - if (selectedTableName) [tablesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:[filteredTables indexOfObject:selectedTableName]] byExtendingSelection:NO]; - [tablesListView reloadData]; -} - -/** - * Select the supplied row index; added for convenience to allow - * use with performSelector:withObject:afterDelay: for re-selection. - */ -- (void) selectTableAtIndex:(NSNumber *)row -{ - NSInteger rowIndex = [row integerValue]; - if (rowIndex == NSNotFound || rowIndex > [filteredTables count] || [[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeNone) - return; - - [tablesListView selectRowIndexes:[NSIndexSet indexSetWithIndex:rowIndex] byExtendingSelection:NO]; -} - -#pragma mark - -#pragma mark Task interaction - -/** - * Disable all table list interactive elements during an ongoing task. - */ -- (void) startDocumentTaskForTab:(NSNotification *)aNotification -{ - tableListIsSelectable = NO; - [toolbarAddButton setEnabled:NO]; - [toolbarActionsButton setEnabled:NO]; - [toolbarReloadButton setEnabled:NO]; -} - -/** - * Enable all table list interactive elements after an ongoing task. - */ -- (void) endDocumentTaskForTab:(NSNotification *)aNotification -{ - tableListIsSelectable = YES; - [toolbarAddButton setEnabled:YES]; - [toolbarActionsButton setEnabled:YES]; - [toolbarReloadButton setEnabled:YES]; -} - -/** - * Set the table list to selectable or not during the task process. - */ -- (void) setTableListSelectability:(BOOL)isSelectable -{ - tableListIsSelectable = isSelectable; -} - -#pragma mark - -#pragma mark SplitView Delegate Methods - -- (NSRect)splitView:(NSSplitView *)splitView effectiveRect:(NSRect)proposedEffectiveRect forDrawnRect:(NSRect)drawnRect ofDividerAtIndex:(NSInteger)dividerIndex -{ - return (splitView == tableListSplitView ? NSZeroRect : proposedEffectiveRect); -} - - -#pragma mark - -#pragma mark Other - -/** - * Standard init method. Performs various ivar initialisations. - */ -- (id)init -{ - if ((self = [super init])) { - tables = [[NSMutableArray alloc] init]; - filteredTables = tables; - tableTypes = [[NSMutableArray alloc] init]; - filteredTableTypes = tableTypes; - structureLoaded = NO; - contentLoaded = NO; - statusLoaded = NO; - triggersLoaded = NO; - isTableListFiltered = NO; - tableListIsSelectable = YES; - tableListContainsViews = NO; - selectedTableType = SPTableTypeNone; - selectedTableName = nil; - [tables addObject:NSLocalizedString(@"TABLES",@"header for table list")]; - } - - return self; -} - -/** - * Standard awakeFromNib method for interface loading. - */ -- (void)awakeFromNib -{ - - // Collapse the table information pane if preference to do so is set - if ([[[NSUserDefaults standardUserDefaults] objectForKey:SPTableInformationPanelCollapsed] boolValue] - && [tableListSplitView collapsibleSubview]) { - [tableInfoCollapseButton setNextState]; - [tableInfoCollapseButton setToolTip:NSLocalizedString(@"Show Table Information",@"Show Table Information")]; - [tableListSplitView setValue:[NSNumber numberWithFloat:[tableListSplitView collapsibleSubview].frame.size.height] forKey:@"uncollapsedSize"]; - [[tableListSplitView collapsibleSubview] setFrameSize:NSMakeSize([tableListSplitView collapsibleSubview].frame.size.width, 0)]; - [tableListSplitView setCollapsibleSubviewCollapsed:YES]; - } else { - [tableInfoCollapseButton setToolTip:NSLocalizedString(@"Hide Table Information",@"Hide Table Information")]; - } - - // Start the table filter list collapsed - if ([tableListFilterSplitView collapsibleSubview]) { - [tableListFilterSplitView setValue:[NSNumber numberWithFloat:[tableListFilterSplitView collapsibleSubview].frame.size.height] forKey:@"uncollapsedSize"]; - // Set search bar view to the height of 1 instead of 0 to ensure that the view will be visible - // after opening a next connection window which has more than 20 tables - [[tableListFilterSplitView collapsibleSubview] setFrameSize:NSMakeSize([tableListFilterSplitView collapsibleSubview].frame.size.width, 1)]; - [tableListFilterSplitView setCollapsibleSubviewCollapsed:YES]; - } - - // Add observers for document task activity - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(startDocumentTaskForTab:) - name:SPDocumentTaskStartNotification - object:tableDocumentInstance]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(endDocumentTaskForTab:) - name:SPDocumentTaskEndNotification - object:tableDocumentInstance]; -} - -/** - * Standard dealloc method. - */ -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [tables release]; - [tableTypes release]; - if (isTableListFiltered && filteredTables) [filteredTables release]; - if (isTableListFiltered && filteredTableTypes) [filteredTableTypes release]; - if (selectedTableName) [selectedTableName release]; - - [super dealloc]; -} - -@end - -@implementation TablesList (PrivateAPI) - -/** - * Removes the selected object (table, view, procedure, function, etc.) from the database and tableView. - */ -- (void)removeTable -{ - NSIndexSet *indexes = [tablesListView selectedRowIndexes]; - [tablesListView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; - - // get last index - NSUInteger currentIndex = [indexes lastIndex]; - - while (currentIndex != NSNotFound) - { - if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeView) { - [mySQLConnection queryString: [NSString stringWithFormat: @"DROP VIEW %@", - [[filteredTables objectAtIndex:currentIndex] backtickQuotedString] - ]]; - } else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeTable) { - [mySQLConnection queryString: [NSString stringWithFormat: @"DROP TABLE %@", - [[filteredTables objectAtIndex:currentIndex] backtickQuotedString] - ]]; - } else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeProc) { - [mySQLConnection queryString: [NSString stringWithFormat: @"DROP PROCEDURE %@", - [[filteredTables objectAtIndex:currentIndex] backtickQuotedString] - ]]; - } else if([[filteredTableTypes objectAtIndex:currentIndex] integerValue] == SPTableTypeFunc) { - [mySQLConnection queryString: [NSString stringWithFormat: @"DROP FUNCTION %@", - [[filteredTables objectAtIndex:currentIndex] backtickQuotedString] - ]]; - } - - // If no error is recorded, the table was successfully dropped - remove it from the list - if (![mySQLConnection queryErrored]) { - //dropped table with success - if (isTableListFiltered) { - NSInteger unfilteredIndex = [tables indexOfObject:[filteredTables objectAtIndex:currentIndex]]; - [tables removeObjectAtIndex:unfilteredIndex]; - [tableTypes removeObjectAtIndex:unfilteredIndex]; - } - [filteredTables removeObjectAtIndex:currentIndex]; - [filteredTableTypes removeObjectAtIndex:currentIndex]; - - // Get next index (beginning from the end) - currentIndex = [indexes indexLessThanIndex:currentIndex]; - - // Otherwise, display an alert - and if there's tables left, ask whether to proceed - } else { - - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - if ([indexes indexLessThanIndex:currentIndex] == NSNotFound) { - [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button")]; - } else { - [alert addButtonWithTitle:NSLocalizedString(@"Continue", @"continue button")]; - [alert addButtonWithTitle:NSLocalizedString(@"Stop", @"stop button")]; - } - [alert setMessageText:NSLocalizedString(@"Error", @"error")]; - [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Couldn't delete '%@'.\nMySQL said: %@", @"message of panel when an item cannot be deleted"), [tables objectAtIndex:currentIndex], [mySQLConnection getLastErrorMessage]]]; - [alert setAlertStyle:NSWarningAlertStyle]; - if ([indexes indexLessThanIndex:currentIndex] == NSNotFound) { - [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil]; - currentIndex = NSNotFound; - } else { - NSInteger choice = [alert runModal]; - if (choice == NSAlertFirstButtonReturn) { - currentIndex = [indexes indexLessThanIndex:currentIndex]; - } else { - currentIndex = NSNotFound; - } - } - } - } - - // Remove the isolated "current selection" item for filtered lists if appropriate - if (isTableListFiltered && [filteredTables count] > 1 - && [[filteredTableTypes objectAtIndex:[filteredTableTypes count]-1] integerValue] == SPTableTypeNone - && [[filteredTables objectAtIndex:[filteredTables count]-1] isEqualToString:NSLocalizedString(@"CURRENT SELECTION",@"header for current selection in filtered list")]) - { - [filteredTables removeLastObject]; - [filteredTableTypes removeLastObject]; - } - - [tablesListView reloadData]; - - // set window title - [tableDocumentInstance updateWindowTitle:self]; - - [tablesListView deselectAll:self]; - - // Query the structure of all databases in the background (mainly for completion) - [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", nil]]; - -} - -/** - * Trucates the selected table(s). - */ -- (void)truncateTable -{ - NSIndexSet *indexes = [tablesListView selectedRowIndexes]; - - // Get last index - NSUInteger currentIndex = [indexes lastIndex]; - - while (currentIndex != NSNotFound) - { - [mySQLConnection queryString:[NSString stringWithFormat: @"TRUNCATE TABLE %@", [[filteredTables objectAtIndex:currentIndex] backtickQuotedString]]]; - - // Couldn't truncate table - if ([mySQLConnection queryErrored]) { - NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Error truncating table", @"error truncating table message") - defaultButton:NSLocalizedString(@"OK", @"OK button") - alternateButton:nil - otherButton:nil - informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"An error occurred while trying to truncate the table '%@'.\n\nMySQL said: %@", @"error truncating table informative message"), - [filteredTables objectAtIndex:currentIndex], [mySQLConnection getLastErrorMessage]]]; - - [alert setAlertStyle:NSCriticalAlertStyle]; - // NSArray *buttons = [alert buttons]; - // // Change the alert's cancel button to have the key equivalent of return - // [[buttons objectAtIndex:0] setKeyEquivalent:@"t"]; - // [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; - // [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; - [alert beginSheetModalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"truncateTableError"]; - } - - // Get next index (beginning from the end) - currentIndex = [indexes indexLessThanIndex:currentIndex]; - } - - // Ensure the the table's content view is updated to show that it has been truncated - if ([tabView indexOfTabViewItem:[tabView selectedTabViewItem]] == SPTableViewContent) { - [tableContentInstance reloadTable:self]; - } else { - [self setContentRequiresReload:YES]; - } - - [tableDataInstance resetStatusData]; -} - -/** - * Adds a new table table to the database using the selected character set encoding and storage engine. - */ -- (void)addTable -{ - NSString *charSetStatement = @""; - NSString *engineStatement = @""; - - NSString *tableType = [tableTypeButton title]; - NSString *tableName = [tableNameField stringValue]; - - // If there is an encoding selected other than the default we must specify it in CREATE TABLE statement - if ([tableEncodingButton indexOfSelectedItem] > 0) { - charSetStatement = [NSString stringWithFormat:@"DEFAULT CHARACTER SET %@", [[tableDocumentInstance mysqlEncodingFromDisplayEncoding:[tableEncodingButton title]] backtickQuotedString]]; - } - - // If there is a type selected other than the default we must specify it in CREATE TABLE statement - if ([tableTypeButton indexOfSelectedItem] > 0) { - engineStatement = [NSString stringWithFormat:@"ENGINE = %@", [tableType backtickQuotedString]]; - } - - NSString *createStatement = [NSString stringWithFormat:@"CREATE TABLE %@ (%@) %@ %@", [tableName backtickQuotedString], ([tableType isEqualToString:@"CSV"]) ? @"id INT NOT NULL" : @"id INT", charSetStatement, engineStatement]; - - // Create the table - [mySQLConnection queryString:createStatement]; - - if (![mySQLConnection queryErrored]) { - - // Table creation was successful - insert the new item into the tables list and select it. - NSInteger addItemAtIndex = NSNotFound; - - for (NSInteger i = 0; i < [tables count]; i++) - { - NSInteger tableType = [[tableTypes objectAtIndex:i] integerValue]; - - if (tableType == SPTableTypeNone) continue; - if (tableType == SPTableTypeProc || tableType == SPTableTypeFunc) { - addItemAtIndex = (i - 1); - break; - } - - if ([tableName localizedCompare:[tables objectAtIndex:i]] == NSOrderedAscending) { - addItemAtIndex = i; - break; - } - } - - if (addItemAtIndex == NSNotFound) { - [tables addObject:tableName]; - [tableTypes addObject:[NSNumber numberWithInteger:SPTableTypeTable]]; - } - else { - [tables insertObject:tableName atIndex:addItemAtIndex]; - [tableTypes insertObject:[NSNumber numberWithInteger:SPTableTypeTable] atIndex:addItemAtIndex]; - } - - // Set the selected table name and type, and then use updateFilter and updateSelection to update the filter list and selection. - if (selectedTableName) [selectedTableName release]; - - selectedTableName = [[NSString alloc] initWithString:tableName]; - selectedTableType = SPTableTypeTable; - - [self updateFilter:self]; - - [tablesListView scrollRowToVisible:[tablesListView selectedRow]]; - - [self updateSelectionWithTaskString:[NSString stringWithFormat:NSLocalizedString(@"Loading %@...", @"Loading table task string"), selectedTableName]]; - - // Query the structure of all databases in the background (mainly for completion) - [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", nil]]; - - } - else { - // Error while creating new table - alertSheetOpened = YES; - - SPBeginAlertSheet(NSLocalizedString(@"Error adding new table", @"error adding new table message"), - NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, - @selector(sheetDidEnd:returnCode:contextInfo:), @"addRow", - [NSString stringWithFormat:NSLocalizedString(@"An error occurred while trying to add the new table '%@'.\n\nMySQL said: %@", @"error adding new table informative message"), tableName, [mySQLConnection getLastErrorMessage]]); - - [tablesListView reloadData]; - } - - // Clear table name - [tableNameField setStringValue:@""]; -} - -/** - * Copies the currently selected object (table, view, procedure, function, etc.). - */ -- (void)copyTable -{ - NSString *tableType = @""; - - if ([[copyTableNameField stringValue] isEqualToString:@""]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, NSLocalizedString(@"Table must have a name.", @"message of panel when no name is given for table")); - return; - } - - BOOL copyTableContent = ([copyTableContentSwitch state] == NSOnState); - - NSInteger tblType = [[filteredTableTypes objectAtIndex:[tablesListView selectedRow]] integerValue]; - - switch (tblType){ - case SPTableTypeTable: - tableType = NSLocalizedString(@"table",@"table"); - [copyTableContentSwitch setEnabled:YES]; - break; - case SPTableTypeView: - tableType = NSLocalizedString(@"view",@"view"); - [copyTableContentSwitch setEnabled:NO]; - break; - case SPTableTypeProc: - tableType = NSLocalizedString(@"procedure",@"procedure"); - [copyTableContentSwitch setEnabled:NO]; - break; - case SPTableTypeFunc: - tableType = NSLocalizedString(@"function",@"function"); - [copyTableContentSwitch setEnabled:NO]; - break; - } - - // Get table/view structure - MCPResult *queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE %@ %@", - [tableType uppercaseString], - [[filteredTables objectAtIndex:[tablesListView selectedRow]] backtickQuotedString] - ]]; - [queryResult setReturnDataAsStrings:YES]; - - if ( ![queryResult numOfRows] ) { - //error while getting table structure - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, - [NSString stringWithFormat:NSLocalizedString(@"Couldn't get create syntax.\nMySQL said: %@", @"message of panel when table information cannot be retrieved"), [mySQLConnection getLastErrorMessage]]); - - } else { - //insert new table name in create syntax and create new table - NSScanner *scanner; - NSString *scanString; - - if(tblType == SPTableTypeView){ - scanner = [[NSScanner alloc] initWithString:[[queryResult fetchRowAsDictionary] objectForKey:@"Create View"]]; - [scanner scanUpToString:@"AS" intoString:nil]; - [scanner scanUpToString:@"" intoString:&scanString]; - [scanner release]; - [mySQLConnection queryString:[NSString stringWithFormat:@"CREATE VIEW %@ %@", [[copyTableNameField stringValue] backtickQuotedString], scanString]]; - } - else if(tblType == SPTableTypeTable){ - scanner = [[NSScanner alloc] initWithString:[[queryResult fetchRowAsDictionary] objectForKey:@"Create Table"]]; - [scanner scanUpToString:@"(" intoString:nil]; - [scanner scanUpToString:@"" intoString:&scanString]; - [scanner release]; - - // If there are any InnoDB referencial constraints we need to strip out the names as they must be unique. - // MySQL will generate the new names based on the new table name. - scanString = [scanString stringByReplacingOccurrencesOfRegex:[NSString stringWithFormat:@"CONSTRAINT `[^`]+` "] withString:@""]; - - // If we're not copying the tables content as well then we need to strip out any AUTO_INCREMENT presets. - if (!copyTableContent) { - scanString = [scanString stringByReplacingOccurrencesOfRegex:[NSString stringWithFormat:@"AUTO_INCREMENT=[0-9]+ "] withString:@""]; - } - - [mySQLConnection queryString:[NSString stringWithFormat:@"CREATE TABLE %@ %@", [[copyTableNameField stringValue] backtickQuotedString], scanString]]; - } - else if(tblType == SPTableTypeFunc || tblType == SPTableTypeProc) - { - // get the create syntax - MCPResult *theResult; - if(selectedTableType == SPTableTypeProc) - theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE PROCEDURE %@", [selectedTableName backtickQuotedString]]]; - else if([self tableType] == SPTableTypeFunc) - theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE FUNCTION %@", [selectedTableName backtickQuotedString]]]; - else - return; - - // Check for errors, only displaying if the connection hasn't been terminated - if ([mySQLConnection queryErrored]) { - if ([mySQLConnection isConnected]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, - [NSString stringWithFormat:NSLocalizedString(@"An error occured while retrieving the create syntax for '%@'.\nMySQL said: %@", @"message of panel when create syntax cannot be retrieved"), selectedTableName, [mySQLConnection getLastErrorMessage]]); - } - return; - } - - [theResult setReturnDataAsStrings:YES]; - NSString *tableSyntax = [[theResult fetchRowAsArray] objectAtIndex:2]; - - // replace the old name by the new one and drop the old one - [mySQLConnection queryString:[tableSyntax stringByReplacingOccurrencesOfRegex:[NSString stringWithFormat:@"(?<=%@ )(`[^`]+?`)", [tableType uppercaseString]] withString:[[copyTableNameField stringValue] backtickQuotedString]]]; - - if ([mySQLConnection queryErrored]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, - [NSString stringWithFormat:NSLocalizedString(@"Couldn't duplicate '%@'.\nMySQL said: %@", @"message of panel when an item cannot be renamed"), [copyTableNameField stringValue], [mySQLConnection getLastErrorMessage]]); - } - - } - - if ([mySQLConnection queryErrored]) { - //error while creating new table - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, - [NSString stringWithFormat:NSLocalizedString(@"Couldn't create '%@'.\nMySQL said: %@", @"message of panel when table cannot be created"), [copyTableNameField stringValue], [mySQLConnection getLastErrorMessage]]); - } else { - - if (copyTableContent) { - //copy table content - [mySQLConnection queryString:[NSString stringWithFormat: - @"INSERT INTO %@ SELECT * FROM %@", - [[copyTableNameField stringValue] backtickQuotedString], - [selectedTableName backtickQuotedString] - ]]; - - if ([mySQLConnection queryErrored]) { - SPBeginAlertSheet( - NSLocalizedString(@"Warning", @"warning"), - NSLocalizedString(@"OK", @"OK button"), - nil, - nil, - [tableDocumentInstance parentWindow], - self, - nil, - nil, - NSLocalizedString(@"There have been errors while copying table content. Please control the new table.", @"message of panel when table content cannot be copied") - ); - } - } - - // Insert the new item into the tables list and select it. - NSInteger addItemAtIndex = NSNotFound; - for (NSInteger i = 0; i < [tables count]; i++) { - NSInteger theTableType = [[tableTypes objectAtIndex:i] integerValue]; - if (theTableType == SPTableTypeNone) continue; - if ((theTableType == SPTableTypeView || theTableType == SPTableTypeTable) - && (tblType == SPTableTypeProc || tblType == SPTableTypeFunc)) { - continue; - } - if ((theTableType == SPTableTypeProc || theTableType == SPTableTypeFunc) - && (tblType == SPTableTypeView || tblType == SPTableTypeTable)) { - addItemAtIndex = i - 1; - break; - } - if ([[copyTableNameField stringValue] localizedCompare:[tables objectAtIndex:i]] == NSOrderedAscending) { - addItemAtIndex = i; - break; - } - } - if (addItemAtIndex == NSNotFound) { - [tables addObject:[copyTableNameField stringValue]]; - [tableTypes addObject:[NSNumber numberWithInteger:tblType]]; - } else { - [tables insertObject:[copyTableNameField stringValue] atIndex:addItemAtIndex]; - [tableTypes insertObject:[NSNumber numberWithInteger:tblType] atIndex:addItemAtIndex]; - } - - // Set the selected table name and type, and use updateFilter to update the filter list and selection - if (selectedTableName) [selectedTableName release]; - selectedTableName = [[NSString alloc] initWithString:[copyTableNameField stringValue]]; - selectedTableType = tblType; - [self updateFilter:self]; - [tablesListView scrollRowToVisible:[tablesListView selectedRow]]; - [self updateSelectionWithTaskString:[NSString stringWithFormat:NSLocalizedString(@"Loading %@...", @"Loading table task string"), selectedTableName]]; - - // Query the structure of all databases in the background (mainly for completion) - [NSThread detachNewThreadSelector:@selector(queryDbStructureWithUserInfo:) toTarget:mySQLConnection withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], @"forceUpdate", nil]]; - - } - } -} - -/* - * Renames a table, view, procedure or function. Also handles only changes in case! - * This function ONLY changes the database. It does NOT refresh the views etc. - * CAREFUL: This function raises an exception if renaming fails, and does not show an error message. - */ -- (void)renameTableOfType:(SPTableType)tableType from:(NSString *)oldTableName to:(NSString *)newTableName -{ - // check if the name really changed - if ([oldTableName isEqualToString:newTableName]) return; - - // check if only the case changed - then we have to do two renames, see http://code.google.com/p/sequel-pro/issues/detail?id=484 - if ([[oldTableName lowercaseString] isEqualToString:[newTableName lowercaseString]]) - { - // first try finding an unused temporary name - // this code should be improved in case we find out that something uses table names like mytable-1, mytable-2, etc. - NSString* tempTableName; - int tempNumber; - for(tempNumber=2; tempNumber<100; tempNumber++) { - tempTableName = [NSString stringWithFormat:@"%@-%d",selectedTableName,tempNumber]; - if ([self isTableNameValid:tempTableName forType:tableType]) break; - } - if (tempNumber==100) { - // we couldn't find a temporary name - [NSException raise:@"No Tempname found" format:NSLocalizedString(@"An error occured while renaming '%@'. No temporary name could be found. Please try renaming to something else first.", @"rename table error - no temporary name found"), oldTableName]; - } - - [self renameTableOfType:tableType from:oldTableName to:tempTableName]; - [self renameTableOfType:tableType from:tempTableName to:newTableName]; - return; - } - - //check if we are trying to rename a TABLE or a VIEW - if (tableType == SPTableTypeView || tableType == SPTableTypeTable) { - // we can use the rename table statement - [mySQLConnection queryString:[NSString stringWithFormat:@"RENAME TABLE %@ TO %@", [oldTableName backtickQuotedString], [newTableName backtickQuotedString]]]; - // check for errors - if ([mySQLConnection queryErrored]) { - [NSException raise:@"MySQL Error" format:NSLocalizedString(@"An error occured while renaming '%@'.\n\nMySQL said: %@", @"rename table error informative message"), oldTableName, [mySQLConnection getLastErrorMessage]]; - } - return; - } - - //check if we are trying to rename a PROCEDURE or a FUNCTION - if (tableType == SPTableTypeProc || tableType == SPTableTypeFunc) { - // procedures and functions can only be renamed if one creates a new one and deletes the old one - - // first get the create syntax - NSString *stringTableType = @""; - - switch (tableType){ - case SPTableTypeProc: stringTableType = @"PROCEDURE"; break; - case SPTableTypeFunc: stringTableType = @"FUNCTION"; break; - } - - MCPResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE %@ %@", stringTableType, [oldTableName backtickQuotedString] ] ]; - if ([mySQLConnection queryErrored]) { - [NSException raise:@"MySQL Error" format:NSLocalizedString(@"An error occured while renaming. I couldn't retrieve the syntax for '%@'.\n\nMySQL said: %@", @"rename precedure/function error - can't retrieve syntax"), oldTableName, [mySQLConnection getLastErrorMessage]]; - } - [theResult setReturnDataAsStrings:YES]; - NSString *oldCreateSyntax = [[theResult fetchRowAsArray] objectAtIndex:2]; - - // replace the old name with the new name - NSRange rangeOfProcedureName = [oldCreateSyntax rangeOfString: [NSString stringWithFormat:@"%@ %@", stringTableType, [oldTableName backtickQuotedString] ] ]; - if (rangeOfProcedureName.length == 0) { - [NSException raise:@"Unknown Syntax" format:NSLocalizedString(@"An error occured while renaming. The CREATE syntax of '%@' could not be parsed.", @"rename error - invalid create syntax"), oldTableName]; - } - NSString *newCreateSyntax = [oldCreateSyntax stringByReplacingCharactersInRange: rangeOfProcedureName - withString: [NSString stringWithFormat:@"%@ %@", stringTableType, [newTableName backtickQuotedString] ] ]; - [mySQLConnection queryString: newCreateSyntax]; - if ([mySQLConnection queryErrored]) { - [NSException raise:@"MySQL Error" format:NSLocalizedString(@"An error occured while renaming. I couldn't recreate '%@'.\n\nMySQL said: %@", @"rename precedure/function error - can't recreate procedure"), oldTableName, [mySQLConnection getLastErrorMessage]]; - } - - [mySQLConnection queryString: [NSString stringWithFormat: @"DROP %@ %@", stringTableType, [oldTableName backtickQuotedString]]]; - if ([mySQLConnection queryErrored]) { - [NSException raise:@"MySQL Error" format:NSLocalizedString(@"An error occured while renaming. I couldn't delete '%@'.\n\nMySQL said: %@", @"rename precedure/function error - can't delete old procedure"), oldTableName, [mySQLConnection getLastErrorMessage]]; - } - return; - } - - [NSException raise:@"Object of unknown type" format:NSLocalizedString(@"An error occured while renaming. '%@' is of an unknown type.", @"rename error - don't know what type the renamed thing is"), oldTableName]; -} - - - -/* - * Check tableName for length and if the tableName doesn't match - * against current database table/view names (case-insensitive). - */ -- (BOOL)isTableNameValid:(NSString *)tableName forType:(SPTableType)tableType -{ - return [self isTableNameValid:tableName forType:tableType ignoringSelectedTable:NO]; -} - -- (BOOL)isTableNameValid:(NSString *)tableName forType:(SPTableType)tableType ignoringSelectedTable:(BOOL)ignoreSelectedTable -{ - BOOL isValid = YES; - - // delete trailing whitespaces since 'foo ' or ' ' are not valid table names - NSString *fieldStr = [tableName stringByMatching:@"(.*?)\\s*$" capture:1]; - NSString *lowercaseFieldStr = [fieldStr lowercaseString]; - - // If table name has trailing whitespaces return 'no valid' - if([fieldStr length] != [tableName length]) return NO; - - // empty table names are invalid - if([fieldStr length] == 0) return NO; - - - NSArray *similarTables; - switch (tableType) { - case SPTableTypeView: - case SPTableTypeTable: - similarTables = [self allTableAndViewNames]; - break; - case SPTableTypeProc: - similarTables = [self allProcedureNames]; - break; - case SPTableTypeFunc: - similarTables = [self allFunctionNames]; - break; - default: - // if some other table type is given, just return yes - // better a mysql error than not being able to change something at all - return YES; - } - - for(id table in similarTables) { - //compare case insensitive here - if([lowercaseFieldStr isEqualToString:[table lowercaseString]]) { - if (ignoreSelectedTable) { - // if table is the selectedTable, ignore it - // we must compare CASE SENSITIVE here! - if ([table isEqualToString:selectedTableName]) continue; - } - isValid = NO; - break; - } - } - return isValid; -} - -@end -- cgit v1.2.3