aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/TableDocument.m
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/TableDocument.m')
-rw-r--r--trunk/TableDocument.m1613
1 files changed, 1613 insertions, 0 deletions
diff --git a/trunk/TableDocument.m b/trunk/TableDocument.m
new file mode 100644
index 00000000..59aad2e0
--- /dev/null
+++ b/trunk/TableDocument.m
@@ -0,0 +1,1613 @@
+//
+// TableDocument.m
+// sequel-pro
+//
+// Created by lorenz textor (lorenz@textor.ch) on Wed May 01 2002.
+// Copyright (c) 2002-2003 Lorenz Textor. All rights reserved.
+//
+// Forked by Abhi Beckert (abhibeckert.com) 2008-04-04
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "TableDocument.h"
+#import "KeyChain.h"
+#import "TablesList.h"
+#import "TableSource.h"
+#import "TableContent.h"
+#import "CustomQuery.h"
+#import "TableDump.h"
+#import "TableStatus.h"
+#import "ImageAndTextCell.h"
+#import "SPGrowlController.h"
+
+NSString *TableDocumentFavoritesControllerSelectionIndexDidChange = @"TableDocumentFavoritesControllerSelectionIndexDidChange";
+NSString *TableDocumentFavoritesControllerFavoritesDidChange = @"TableDocumentFavoritesControllerFavoritesDidChange";
+
+@implementation TableDocument
+
+- (id)init
+{
+ if (![super init])
+ return nil;
+
+ _encoding = [@"utf8" retain];
+ chooseDatabaseButton = nil;
+ chooseDatabaseToolbarItem = nil;
+
+ return self;
+}
+
+- (void)awakeFromNib
+{
+ // register selection did change handler for favorites controller (used in connect sheet)
+ [favoritesController addObserver:self forKeyPath:@"selectionIndex" options:NSKeyValueChangeInsertion context:TableDocumentFavoritesControllerSelectionIndexDidChange];
+
+ // register value change handler for favourites, so we can save them to preferences
+ [self addObserver:self forKeyPath:@"favorites" options:0 context:TableDocumentFavoritesControllerFavoritesDidChange];
+
+ // register double click for the favorites view (double click favorite to connect)
+ [connectFavoritesTableView setTarget:self];
+
+ // find the Database -> Database Encoding menu (it's not in our nib, so we can't use interface builder)
+ selectEncodingMenu = [[[[[NSApp mainMenu] itemWithTag:1] submenu] itemWithTag:1] submenu];
+
+ // hide the tabs in the tab view (we only show them to allow switching tabs in interface builder)
+ [tableTabView setTabViewType:NSNoTabsNoBorder];
+}
+
+- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ if (context == TableDocumentFavoritesControllerSelectionIndexDidChange) {
+ [self chooseFavorite:self];
+ return;
+ }
+
+ if (context == TableDocumentFavoritesControllerFavoritesDidChange) {
+ [prefs setObject:[self favorites] forKey:@"favorites"];
+ return;
+ }
+
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+
+
+- (CMMCPConnection *)sharedConnection
+{
+ return mySQLConnection;
+}
+
+
+//start sheet
+
+/**
+ * tries to connect to a database server, shows connect sheet prompting user to
+ * enter details/select favorite and shoows alert sheets on failure.
+ */
+- (IBAction)connectToDB:(id)sender
+{
+
+ // load the details of the curretnly selected favorite into the text boxes in connect sheet
+ [self chooseFavorite:self];
+
+ // run the connect sheet (modal)
+ [NSApp beginSheet:connectSheet
+ modalForWindow:tableWindow
+ modalDelegate:self
+ didEndSelector:@selector(connectSheetDidEnd:returnCode:contextInfo:)
+ contextInfo:nil];
+}
+
+
+/*
+ invoked when user hits the connect-button of the connectSheet
+ stops modal session with code:
+ 1 when connected with success
+ 2 when no connection to host
+ 3 when no connection to db
+ 4 when hostField and socketField are empty
+ */
+- (IBAction)connect:(id)sender
+{
+ int code;
+
+ [connectProgressBar startAnimation:self];
+ [connectProgressStatusText setHidden:NO];
+ [connectProgressStatusText display];
+
+ [selectedDatabase autorelease];
+ selectedDatabase = nil;
+
+ code = 0;
+ if ( [[hostField stringValue] isEqualToString:@""] && [[socketField stringValue] isEqualToString:@""] ) {
+ code = 4;
+ } else {
+ if ( ![[socketField stringValue] isEqualToString:@""] ) {
+ //connect to socket
+ mySQLConnection = [[CMMCPConnection alloc] initToSocket:[socketField stringValue]
+ withLogin:[userField stringValue]
+ password:[passwordField stringValue]];
+ [hostField setStringValue:@"localhost"];
+ } else {
+ //connect to host
+ mySQLConnection = [[CMMCPConnection alloc] initToHost:[hostField stringValue]
+ withLogin:[userField stringValue]
+ password:[passwordField stringValue]
+ usingPort:[portField intValue]];
+ }
+ if ( ![mySQLConnection isConnected] )
+ code = 2;
+ if ( !code && ![[databaseField stringValue] isEqualToString:@""] ) {
+ if ([mySQLConnection selectDB:[databaseField stringValue]]) {
+ selectedDatabase = [[databaseField stringValue] retain];
+ } else {
+ code = 3;
+ }
+ }
+ if ( !code )
+ code = 1;
+ }
+
+ // close sheet
+ [connectSheet orderOut:nil];
+ [NSApp endSheet:connectSheet returnCode:code];
+ [connectProgressBar stopAnimation:self];
+ [connectProgressStatusText setHidden:YES];
+}
+
+-(void)connectSheetDidEnd:(NSWindow*)sheet returnCode:(int)code contextInfo:(void*)contextInfo
+{
+ [sheet orderOut:self];
+
+ CMMCPResult *theResult;
+ id version;
+
+ if ( code == 1) {
+ //connected with success
+ //register as delegate
+ [mySQLConnection setDelegate:self];
+ // set encoding
+ NSString *encodingName = [prefs objectForKey:@"encoding"];
+ if ( [encodingName isEqualToString:@"Autodetect"] ) {
+ [self detectEncoding];
+ } else {
+ [self setEncoding:[self mysqlEncodingFromDisplayEncoding:encodingName]];
+ }
+ //get mysql version
+ theResult = [mySQLConnection queryString:@"SHOW VARIABLES LIKE 'version'"];
+ version = [[theResult fetchRowAsArray] objectAtIndex:1];
+ if ( [version isKindOfClass:[NSData class]] ) {
+ // starting with MySQL 4.1.14 the mysql variables are returned as nsdata
+ mySQLVersion = [[NSString alloc] initWithData:version encoding:[mySQLConnection encoding]];
+ } else {
+ mySQLVersion = [[NSString stringWithString:version] retain];
+ }
+ [self setDatabases:self];
+ [tablesListInstance setConnection:mySQLConnection];
+ [tableSourceInstance setConnection:mySQLConnection];
+ [tableContentInstance setConnection:mySQLConnection];
+ [customQueryInstance setConnection:mySQLConnection];
+ [tableDumpInstance setConnection:mySQLConnection];
+ [tableStatusInstance setConnection:mySQLConnection];
+ [self setFileName:[NSString stringWithFormat:@"(MySQL %@) %@@%@ %@", mySQLVersion, [userField stringValue],
+ [hostField stringValue], [databaseField stringValue]]];
+ [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@", mySQLVersion, [userField stringValue],
+ [hostField stringValue], [databaseField stringValue]]];
+
+ // Connected Growl notification
+ [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Connected"
+ description:[NSString stringWithFormat:NSLocalizedString(@"Connected to %@",@"description for connected growl notification"), [tableWindow title]]
+ notificationName:@"Connected"];
+
+ } else if (code == 2) {
+ //can't connect to host
+ NSBeginAlertSheet(NSLocalizedString(@"Connection failed!", @"connection failed"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil,
+ @selector(sheetDidEnd:returnCode:contextInfo:), @"connect",
+ [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to host %@.\nBe sure that the address is correct and that you have the necessary privileges.\nMySQL said: %@", @"message of panel when connection to host failed"), [hostField stringValue], [mySQLConnection getLastErrorMessage]]);
+ } else if (code == 3) {
+ //can't connect to db
+ NSBeginAlertSheet(NSLocalizedString(@"Connection failed!", @"connection failed"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil,
+ @selector(sheetDidEnd:returnCode:contextInfo:), @"connect",
+ [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to database %@.\nBe sure that the database exists and that you have the necessary privileges.\nMySQL said: %@", @"message of panel when connection to db failed"), [databaseField stringValue], [mySQLConnection getLastErrorMessage]]);
+ } else if (code == 4) {
+ //no host is given
+ NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil,
+ @selector(sheetDidEnd:returnCode:contextInfo:), @"connect", NSLocalizedString(@"Please enter at least a host or socket.", @"message of panel when host/socket are missing"));
+ }
+
+}
+
+- (IBAction)cancelConnectSheet:(id)sender
+{
+ [NSApp endSheet:connectSheet];
+ [tableWindow close];
+}
+
+- (IBAction)closeSheet:(id)sender
+/*
+ invoked when user hits the cancel button of the connectSheet
+ stops modal session with code 0
+ reused when user hits the close button of the variablseSheet or of the createTableSyntaxSheet
+ */
+{
+ [NSApp stopModalWithCode:0];
+}
+
+/**
+ * sets fields for the chosen favorite.
+ */
+- (IBAction)chooseFavorite:(id)sender
+{
+ if (![self selectedFavorite])
+ return;
+
+ [hostField setStringValue:[self valueForKeyPath:@"selectedFavorite.host"]];
+ [socketField setStringValue:[self valueForKeyPath:@"selectedFavorite.socket"]];
+ [userField setStringValue:[self valueForKeyPath:@"selectedFavorite.user"]];
+ [portField setStringValue:[self valueForKeyPath:@"selectedFavorite.port"]];
+ [databaseField setStringValue:[self valueForKeyPath:@"selectedFavorite.database"]];
+ [passwordField setStringValue:[self selectedFavoritePassword]];
+
+ [selectedFavorite release];
+ selectedFavorite = [[favoritesButton titleOfSelectedItem] retain];
+}
+
+/**
+ * Remove the selected favourite. Instead of calling the remove: method of the Favorites NSArrayController
+ * directly in the XIB we do it here because we also need to remove the keychain password.
+ */
+- (IBAction)removeFavorite:(id)sender
+{
+ if (![self selectedFavorite]) {
+ return;
+ }
+
+ NSString *name = [self valueForKeyPath:@"selectedFavorite.name"];
+ NSString *user = [self valueForKeyPath:@"selectedFavorite.user"];
+ NSString *host = [self valueForKeyPath:@"selectedFavorite.host"];
+ NSString *database = [self valueForKeyPath:@"selectedFavorite.database"];
+
+ [keyChainInstance deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro : %@", name]
+ account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]];
+ [keyChainInstance deletePasswordForName:[NSString stringWithFormat:@"Sequel Pro SSHTunnel : %@", name]
+ account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]];
+
+ // Remove from favorites array controller
+ [favoritesController remove:[self selectedFavorite]];
+
+}
+
+/**
+ * Return the favorites array.
+ */
+- (NSMutableArray *)favorites
+{
+ // if no favorites, load from user defaults
+ if (!favorites) {
+ favorites = [[NSMutableArray alloc] initWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:@"favorites"]];
+ }
+
+ // if no favorites in user defaults, load empty ones
+ if (!favorites) {
+ favorites = [[NSMutableArray array] retain];
+ }
+
+ return favorites;
+}
+
+/**
+ * returns a KVC-compliant proxy to the currently selected favorite, or nil if nothing selected.
+ *
+ * see [NSObjectController selection]
+ */
+- (id)selectedFavorite
+{
+ if ([favoritesController selectionIndex] == NSNotFound)
+ return nil;
+
+ return [favoritesController selection];
+}
+
+/**
+ * fetches the password [self selectedFavorite] from the keychain, returns nil if no selection.
+ */
+- (NSString *)selectedFavoritePassword
+{
+ if (![self selectedFavorite])
+ return nil;
+
+ NSString *keychainName = [NSString stringWithFormat:@"Sequel Pro : %@", [self valueForKeyPath:@"selectedFavorite.name"]];
+ NSString *keychainAccount = [NSString stringWithFormat:@"%@@%@/%@",
+ [self valueForKeyPath:@"selectedFavorite.user"],
+ [self valueForKeyPath:@"selectedFavorite.host"],
+ [self valueForKeyPath:@"selectedFavorite.database"]];
+
+ return [keyChainInstance getPasswordForName:keychainName account:keychainAccount];
+}
+
+- (void)connectSheetAddToFavorites:(id)sender
+{
+ [self addToFavoritesHost:[hostField stringValue] socket:[socketField stringValue] user:[userField stringValue] password:[passwordField stringValue] port:[portField stringValue] database:[databaseField stringValue] useSSH:false sshHost:@"" sshUser:@"" sshPassword:@"" sshPort:@""];
+}
+
+/**
+ * add actual connection to favorites
+ */
+- (void)addToFavoritesHost:(NSString *)host socket:(NSString *)socket
+ user:(NSString *)user password:(NSString *)password
+ port:(NSString *)port database:(NSString *)database
+ useSSH:(BOOL)useSSH // no-longer in use
+ sshHost:(NSString *)sshHost // no-longer in use
+ sshUser:(NSString *)sshUser // no-longer in use
+ sshPassword:(NSString *)sshPassword // no-longer in use
+ sshPort:(NSString *)sshPort // no-longer in use
+{
+ NSString *favoriteName = [NSString stringWithFormat:@"%@@%@", user, host];
+ if (![database isEqualToString:@""])
+ favoriteName = [NSString stringWithFormat:@"%@ %@", database, favoriteName];
+
+ // test if host and socket are not nil
+ if ([host isEqualToString:@""] && [socket isEqualToString:@""]) {
+ NSRunAlertPanel(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"Please enter at least a host or socket.", @"message of panel when host/socket are missing"), NSLocalizedString(@"OK", @"OK button"), nil, nil);
+ return;
+ }
+
+ [self willChangeValueForKey:@"favorites"];
+
+ // write favorites and password
+ NSDictionary *newFavorite = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:favoriteName, host, socket, user, port, database, nil]
+ forKeys:[NSArray arrayWithObjects:@"name", @"host", @"socket", @"user", @"port", @"database", nil]];
+ [favorites addObject:newFavorite];
+
+ if (![password isEqualToString:@""]) {
+ [keyChainInstance addPassword:password
+ forName:[NSString stringWithFormat:@"Sequel Pro : %@", favoriteName]
+ account:[NSString stringWithFormat:@"%@@%@/%@", user, host, database]];
+ }
+
+ // select new favorite
+ selectedFavorite = [favoriteName retain];
+
+ [self didChangeValueForKey:@"favorites"];
+ [favoritesController setSelectedObjects:[NSArray arrayWithObject:newFavorite]];
+}
+
+/**
+ * alert sheets method
+ * invoked when alertSheet get closed
+ * if contextInfo == connect -> reopens the connectSheet
+ * if contextInfo == removedatabase -> tries to remove the selected database
+ */
+- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo
+{
+ [sheet orderOut:self];
+
+ if ([contextInfo isEqualToString:@"connect"]) {
+ [self connectToDB:nil];
+ return;
+ }
+
+ if ([contextInfo isEqualToString:@"removedatabase"]) {
+ if (returnCode != NSAlertDefaultReturn)
+ return;
+
+ [mySQLConnection queryString:[NSString stringWithFormat:@"DROP DATABASE `%@`", [self database]]];
+ if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
+ // error while deleting db
+ NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't remove database.\nMySQL said: %@", @"message of panel when removing db failed"), [mySQLConnection getLastErrorMessage]]);
+ return;
+ }
+
+ // db deleted with success
+ selectedDatabase = nil;
+ [self setDatabases:self];
+ [tablesListInstance setConnection:mySQLConnection];
+ [tableDumpInstance setConnection:mySQLConnection];
+ [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/", mySQLVersion, [userField stringValue], [hostField stringValue]]];
+ }
+}
+
+
+#pragma mark database methods
+
+/**
+ * sets up the database select toolbar item
+ */
+- (IBAction)setDatabases:(id)sender;
+{
+ if (!chooseDatabaseButton)
+ return;
+
+ [chooseDatabaseButton removeAllItems];
+ [chooseDatabaseButton addItemWithTitle:NSLocalizedString(@"Choose Database...", @"menu item for choose db")];
+ [[chooseDatabaseButton menu] addItem:[NSMenuItem separatorItem]];
+ [[chooseDatabaseButton menu] addItemWithTitle:NSLocalizedString(@"Add Database...", @"menu item to add db") action:@selector(addDatabase:) keyEquivalent:@""];
+ [[chooseDatabaseButton menu] addItem:[NSMenuItem separatorItem]];
+
+
+ MCPResult *queryResult = [mySQLConnection listDBs];
+ if ([queryResult numOfRows]) [queryResult dataSeek:0];
+ int i;
+ for ( i = 0 ; i < [queryResult numOfRows] ; i++ ) {
+ [chooseDatabaseButton addItemWithTitle:[[queryResult fetchRowAsArray] objectAtIndex:0]];
+ }
+ if ( ![self database] ) {
+ [chooseDatabaseButton selectItemAtIndex:0];
+ } else {
+ [chooseDatabaseButton selectItemWithTitle:[self database]];
+ }
+}
+
+/**
+ * selects the database choosen by the user
+ * errorsheet if connection failed
+ */
+- (IBAction)chooseDatabase:(id)sender
+{
+ if (![tablesListInstance selectionShouldChangeInTableView:nil]) {
+ [chooseDatabaseButton selectItemWithTitle:[self database]];
+ return;
+ }
+
+ if ( [chooseDatabaseButton indexOfSelectedItem] == 0 ) {
+ if ([self database]) {
+ [chooseDatabaseButton selectItemWithTitle:[self database]];
+ }
+ return;
+ }
+
+ // show error on connection failed
+ if ( ![mySQLConnection selectDB:[chooseDatabaseButton titleOfSelectedItem]] ) {
+ NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to database %@.\nBe sure that you have the necessary privileges.", @"message of panel when connection to db failed after selecting from popupbutton"), [chooseDatabaseButton titleOfSelectedItem]]);
+ [self setDatabases:self];
+ return;
+ }
+
+ //setConnection of TablesList and TablesDump to reload tables in db
+ [selectedDatabase release];
+ selectedDatabase = nil;
+ selectedDatabase = [[chooseDatabaseButton titleOfSelectedItem] retain];
+ [tablesListInstance setConnection:mySQLConnection];
+ [tableDumpInstance setConnection:mySQLConnection];
+ [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@", mySQLVersion, [userField stringValue], [hostField stringValue], [self database]]];
+}
+
+/**
+ * opens the add-db sheet and creates the new db
+ */
+- (IBAction)addDatabase:(id)sender
+{
+ int code = 0;
+
+ if (![tablesListInstance selectionShouldChangeInTableView:nil])
+ return;
+
+ [databaseNameField setStringValue:@""];
+ [NSApp beginSheet:databaseSheet
+ modalForWindow:tableWindow
+ modalDelegate:self
+ didEndSelector:nil
+ contextInfo:nil];
+ code = [NSApp runModalForWindow:databaseSheet];
+
+ [NSApp endSheet:databaseSheet];
+ [databaseSheet orderOut:nil];
+
+ if (!code)
+ return;
+
+ if ([[databaseNameField stringValue] isEqualToString:@""]) {
+ NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Database must have a name.", @"message of panel when no db name is given"));
+ return;
+ }
+
+ [mySQLConnection queryString:[NSString stringWithFormat:@"CREATE DATABASE `%@`", [databaseNameField stringValue]]];
+ if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
+ //error while creating db
+ NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't create database.\nMySQL said: %@", @"message of panel when creation of db failed"), [mySQLConnection getLastErrorMessage]]);
+ return;
+ }
+
+ if (![mySQLConnection selectDB:[databaseNameField stringValue]] ) { //error while selecting new db (is this possible?!)
+ NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Unable to connect to database %@.\nBe sure that you have the necessary privileges.", @"message of panel when connection to db failed after selecting from popupbutton"),
+ [databaseNameField stringValue]]);
+ [self setDatabases:self];
+ return;
+ }
+
+ //select new db
+ [selectedDatabase release];
+ selectedDatabase = nil;
+ selectedDatabase = [[databaseNameField stringValue] retain];
+ [self setDatabases:self];
+ [tablesListInstance setConnection:mySQLConnection];
+ [tableDumpInstance setConnection:mySQLConnection];
+ [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@@%@/%@", mySQLVersion, [userField stringValue], [hostField stringValue], selectedDatabase]];
+}
+
+/**
+ * closes the add-db sheet and stops modal session
+ */
+- (IBAction)closeDatabaseSheet:(id)sender
+{
+ [NSApp stopModalWithCode:[sender tag]];
+}
+
+/**
+ * opens sheet to ask user if he really wants to delete the db
+ */
+- (IBAction)removeDatabase:(id)sender
+{
+ if ([chooseDatabaseButton indexOfSelectedItem] == 0)
+ return;
+ if (![tablesListInstance selectionShouldChangeInTableView:nil])
+ return;
+
+ NSBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"Delete", @"delete button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, tableWindow, self, nil, @selector(sheetDidEnd:returnCode:contextInfo:), @"removedatabase", [NSString stringWithFormat:NSLocalizedString(@"Do you really want to delete the database %@?", @"message of panel asking for confirmation for deleting db"), [self database]]);
+}
+
+#pragma mark console methods
+
+//console methods
+/**
+ * shows or hides the console
+ */
+- (void)toggleConsole:(id)sender
+{
+ if ([self consoleIsOpened]) {
+ [consoleDrawer close];
+ } else {
+ [consoleTextView scrollRangeToVisible:[consoleTextView selectedRange]];
+ [consoleDrawer openOnEdge:NSMinYEdge];
+ }
+}
+
+/**
+ * clears the console
+ */
+- (void)clearConsole:(id)sender
+{
+ [consoleTextView setString:@""];
+}
+
+/**
+ * returns YES if the console is visible
+ */
+- (BOOL)consoleIsOpened
+{
+ return ([consoleDrawer state] == NSDrawerOpeningState || [consoleDrawer state] == NSDrawerOpenState);
+}
+
+/**
+ * shows a message in the console
+ */
+- (void)showMessageInConsole:(NSString *)message
+{
+ int begin, end;
+
+ [consoleTextView setSelectedRange:NSMakeRange([[consoleTextView string] length],0)];
+ begin = [[consoleTextView string] length];
+ [consoleTextView replaceCharactersInRange:NSMakeRange(begin,0) withString:message];
+ end = [[consoleTextView string] length];
+ [consoleTextView setTextColor:[NSColor blackColor] range:NSMakeRange(begin,end-begin)];
+
+ if ([self consoleIsOpened]) {
+ [consoleTextView displayIfNeeded];
+ [consoleTextView scrollRangeToVisible:[consoleTextView selectedRange]];
+ }
+}
+
+/**
+ * shows an error in the console (red)
+ */
+- (void)showErrorInConsole:(NSString *)error
+{
+ int begin, end;
+
+ [consoleTextView setSelectedRange:NSMakeRange([[consoleTextView string] length],0)];
+ begin = [[consoleTextView string] length];
+ [consoleTextView replaceCharactersInRange:NSMakeRange(begin,0) withString:error];
+ end = [[consoleTextView string] length];
+ [consoleTextView setTextColor:[NSColor redColor] range:NSMakeRange(begin,end-begin)];
+
+ if ([self consoleIsOpened]) {
+ [consoleTextView displayIfNeeded];
+ [consoleTextView scrollRangeToVisible:[consoleTextView selectedRange]];
+ }
+}
+
+#pragma mark Encoding Methods
+
+/**
+ * Set the encoding for the database connection
+ */
+- (void)setEncoding:(NSString *)mysqlEncoding
+{
+ // set encoding of connection and client
+ [mySQLConnection queryString:[NSString stringWithFormat:@"SET NAMES '%@'", mysqlEncoding]];
+ if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) {
+ [mySQLConnection setEncoding:[CMMCPConnection encodingForMySQLEncoding:[mysqlEncoding UTF8String]]];
+ [_encoding autorelease];
+ _encoding = [mysqlEncoding retain];
+ } else {
+ [self detectEncoding];
+ }
+
+ // update the selected menu item
+ [self updateEncodingMenuWithSelectedEncoding:[self encodingNameFromMySQLEncoding:mysqlEncoding]];
+
+ // reload stuff
+ [tableSourceInstance reloadTable:self];
+ [tableContentInstance reloadTable:self];
+ [tableStatusInstance reloadTable:self];
+}
+
+/**
+ * returns the current mysql encoding for this object
+ */
+- (NSString *)encoding
+{
+ return _encoding;
+}
+
+/**
+ * updates the currently selected item in the encoding menu
+ *
+ * @param NSString *encoding - the title of the menu item which will be selected
+ */
+- (void)updateEncodingMenuWithSelectedEncoding:(NSString *)encoding
+{
+ NSEnumerator *dbEncodingMenuEn = [[selectEncodingMenu itemArray] objectEnumerator];
+ id menuItem;
+ int correctStateForMenuItem;
+ while (menuItem = [dbEncodingMenuEn nextObject]) {
+ correctStateForMenuItem = [[menuItem title] isEqualToString:encoding] ? NSOnState : NSOffState;
+
+ if ([menuItem state] == correctStateForMenuItem) // don't re-apply state incase it causes performance issues
+ continue;
+
+ [menuItem setState:correctStateForMenuItem];
+ }
+}
+
+/**
+ * Returns the display name for a mysql encoding
+ */
+- (NSString *)encodingNameFromMySQLEncoding:(NSString *)mysqlEncoding
+{
+ NSDictionary *translationMap = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"UCS-2 Unicode (ucs2)", @"ucs2",
+ @"UTF-8 Unicode (utf8)", @"utf8",
+ @"US ASCII (ascii)", @"ascii",
+ @"ISO Latin 1 (latin1)", @"latin1",
+ @"Mac Roman (macroman)", @"macroman",
+ @"Windows Latin 2 (cp1250)", @"cp1250",
+ @"ISO Latin 2 (latin2)", @"latin2",
+ @"Windows Arabic (cp1256)", @"cp1256",
+ @"ISO Greek (greek)", @"greek",
+ @"ISO Hebrew (hebrew)", @"hebrew",
+ @"ISO Turkish (latin5)", @"latin5",
+ @"Windows Baltic (cp1257)", @"cp1257",
+ @"Windows Cyrillic (cp1251)", @"cp1251",
+ @"Big5 Traditional Chinese (big5)", @"big5",
+ @"Shift-JIS Japanese (sjis)", @"sjis",
+ @"EUC-JP Japanese (ujis)", @"ujis",
+ nil];
+ NSString *encodingName = [translationMap valueForKey:mysqlEncoding];
+
+ if (!encodingName)
+ return [NSString stringWithFormat:@"Unknown Encoding (%@)", mysqlEncoding, nil];
+
+ return encodingName;
+}
+
+/**
+ * Returns the mysql encoding for an encoding string that is displayed to the user
+ */
+- (NSString *)mysqlEncodingFromDisplayEncoding:(NSString *)encodingName
+{
+ NSDictionary *translationMap = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"ucs2", @"UCS-2 Unicode (ucs2)",
+ @"utf8", @"UTF-8 Unicode (utf8)",
+ @"ascii", @"US ASCII (ascii)",
+ @"latin1", @"ISO Latin 1 (latin1)",
+ @"macroman", @"Mac Roman (macroman)",
+ @"cp1250", @"Windows Latin 2 (cp1250)",
+ @"latin2", @"ISO Latin 2 (latin2)",
+ @"cp1256", @"Windows Arabic (cp1256)",
+ @"greek", @"ISO Greek (greek)",
+ @"hebrew", @"ISO Hebrew (hebrew)",
+ @"latin5", @"ISO Turkish (latin5)",
+ @"cp1257", @"Windows Baltic (cp1257)",
+ @"cp1251", @"Windows Cyrillic (cp1251)",
+ @"big5", @"Big5 Traditional Chinese (big5)",
+ @"sjis", @"Shift-JIS Japanese (sjis)",
+ @"ujis", @"EUC-JP Japanese (ujis)",
+ nil];
+ NSString *mysqlEncoding = [translationMap valueForKey:encodingName];
+
+ if (!mysqlEncoding)
+ return @"utf8";
+
+ return mysqlEncoding;
+}
+
+/**
+ * Autodetect the connection encoding and select the relevant encoding menu item in Database -> Database Encoding
+ */
+- (void)detectEncoding
+{
+ // mysql > 4.0
+ id mysqlEncoding = [[[mySQLConnection queryString:@"SHOW VARIABLES LIKE 'character_set_connection'"] fetchRowAsDictionary] objectForKey:@"Value"];
+ _supportsEncoding = (mysqlEncoding != nil);
+
+ if ( [mysqlEncoding isKindOfClass:[NSData class]] ) { // MySQL 4.1.14 returns the mysql variables as nsdata
+ mysqlEncoding = [mySQLConnection stringWithText:mysqlEncoding];
+ }
+ if ( !mysqlEncoding ) { // mysql 4.0 or older -> only default character set possible, cannot choose others using "set names xy"
+ mysqlEncoding = [[[mySQLConnection queryString:@"SHOW VARIABLES LIKE 'character_set'"] fetchRowAsDictionary] objectForKey:@"Value"];
+ }
+ if ( !mysqlEncoding ) { // older version? -> set encoding to mysql default encoding latin1
+ NSLog(@"error: no character encoding found, mysql version is %@", [self mySQLVersion]);
+ mysqlEncoding = @"latin1";
+ }
+ [mySQLConnection setEncoding:[CMMCPConnection encodingForMySQLEncoding:[mysqlEncoding cString]]];
+
+ // save the encoding
+ [_encoding autorelease];
+ _encoding = [mysqlEncoding retain];
+
+ // update the selected menu item
+ [self updateEncodingMenuWithSelectedEncoding:[self encodingNameFromMySQLEncoding:mysqlEncoding]];
+}
+
+/**
+ * when sent by an NSMenuItem, will set the encoding based on the title of the menu item
+ */
+- (IBAction)chooseEncoding:(id)sender
+{
+ [self setEncoding:[self mysqlEncodingFromDisplayEncoding:[(NSMenuItem *)sender title]]];
+}
+
+/**
+ * return YES if MySQL server supports choosing connection and table encodings (MySQL 4.1 and newer)
+ */
+- (BOOL)supportsEncoding
+{
+ return _supportsEncoding;
+}
+
+
+#pragma mark Table Methods
+
+- (IBAction)showCreateTableSyntax:(id)sender
+{
+ //Create the query and get results
+ NSString *query = [NSString stringWithFormat:@"SHOW CREATE TABLE `%@`", [self table]];
+ CMMCPResult *theResult = [mySQLConnection queryString:query];
+
+ // Check for errors
+ if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
+ NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while creating table syntax.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil);
+ return;
+ }
+
+ id tableSyntax = [[theResult fetchRowAsArray] objectAtIndex:1];
+
+ if ([tableSyntax isKindOfClass:[NSData class]])
+ tableSyntax = [[NSString alloc] initWithData:tableSyntax encoding:[mySQLConnection encoding]];
+
+ [syntaxViewContent setString:tableSyntax];
+ [createTableSyntaxWindow makeKeyAndOrderFront:self];
+}
+
+- (IBAction)copyCreateTableSyntax:(id)sender
+{
+ // Create the query and get results
+ NSString *query = [NSString stringWithFormat:@"SHOW CREATE TABLE `%@`", [self table]];
+ CMMCPResult *theResult = [mySQLConnection queryString:query];
+
+ // Check for errors
+ if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
+ NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while creating table syntax.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil);
+ return;
+ }
+
+ id tableSyntax = [[theResult fetchRowAsArray] objectAtIndex:1];
+
+ if ([tableSyntax isKindOfClass:[NSData class]])
+ tableSyntax = [[NSString alloc] initWithData:tableSyntax encoding:[mySQLConnection encoding]];
+
+ // copy to the clipboard
+ NSPasteboard *pb = [NSPasteboard generalPasteboard];
+ [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
+ [pb setString:tableSyntax forType:NSStringPboardType];
+
+ // Table syntax copied Growl notification
+ [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Table Syntax Copied"
+ description:[NSString stringWithFormat:NSLocalizedString(@"Syntax for %@ table copied",@"description for table syntax copied growl notification"), [self table]]
+ notificationName:@"Table Syntax Copied"];
+}
+
+- (IBAction)checkTable:(id)sender
+{
+ NSString *query;
+ CMMCPResult *theResult;
+ NSDictionary *theRow;
+
+ //Create the query and get results
+ query = [NSString stringWithFormat:@"CHECK TABLE `%@`", [self table]];
+ theResult = [mySQLConnection queryString:query];
+
+ // Check for errors
+ if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
+ NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while checking table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil);
+ return;
+ }
+
+ // Process result
+ theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject];
+ NSRunInformationalAlertPanel([NSString stringWithFormat:@"Check '%@' table", [self table]], [NSString stringWithFormat:@"Check: %@", [theRow objectForKey:@"Msg_text"]], @"OK", nil, nil);
+}
+
+- (IBAction)analyzeTable:(id)sender
+{
+ NSString *query;
+ CMMCPResult *theResult;
+ NSDictionary *theRow;
+
+ //Create the query and get results
+ query = [NSString stringWithFormat:@"ANALYZE TABLE `%@`", [self table]];
+ theResult = [mySQLConnection queryString:query];
+
+ // Check for errors
+ if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
+ NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while analyzing table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil);
+ return;
+ }
+
+ // Process result
+ theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject];
+ NSRunInformationalAlertPanel([NSString stringWithFormat:@"Analyze '%@' table", [self table]], [NSString stringWithFormat:@"Analyze: %@", [theRow objectForKey:@"Msg_text"]], @"OK", nil, nil);
+}
+
+- (IBAction)optimizeTable:(id)sender
+{
+ NSString *query;
+ CMMCPResult *theResult;
+ NSDictionary *theRow;
+
+ //Create the query and get results
+ query = [NSString stringWithFormat:@"OPTIMIZE TABLE `%@`", [self table]];
+ theResult = [mySQLConnection queryString:query];
+
+ // Check for errors
+ if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) {
+ NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while optimizing table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil);
+ }
+
+ // Process result
+ theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject];
+ NSRunInformationalAlertPanel([NSString stringWithFormat:@"Optimize '%@' table", [self table]], [NSString stringWithFormat:@"Optimize: %@", [theRow objectForKey:@"Msg_text"]], @"OK", nil, nil);
+}
+
+- (IBAction)repairTable:(id)sender
+{
+ NSString *query;
+ CMMCPResult *theResult;
+ NSDictionary *theRow;
+
+ //Create the query and get results
+ query = [NSString stringWithFormat:@"REPAIR TABLE `%@`", [self table]];
+ theResult = [mySQLConnection queryString:query];
+
+ // Check for errors
+ if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
+ NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while repairing table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil);
+ }
+
+ // Process result
+ theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject];
+ NSRunInformationalAlertPanel([NSString stringWithFormat:@"Repair '%@' table", [self table]], [NSString stringWithFormat:@"Repair: %@", [theRow objectForKey:@"Msg_text"]], @"OK", nil, nil);
+}
+
+- (IBAction)flushTable:(id)sender
+{
+ NSString *query;
+ CMMCPResult *theResult;
+
+ //Create the query and get results
+ query = [NSString stringWithFormat:@"FLUSH TABLE `%@`", [self table]];
+ theResult = [mySQLConnection queryString:query];
+
+ // Check for errors
+ if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
+ NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while flushing table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil);
+ return;
+ }
+
+ // Process result
+ NSRunInformationalAlertPanel([NSString stringWithFormat:@"Flush '%@' table", [self table]], @"Flushed", @"OK", nil, nil);
+}
+
+- (IBAction)checksumTable:(id)sender
+{
+ NSString *query;
+ CMMCPResult *theResult;
+ NSDictionary *theRow;
+
+ //Create the query and get results
+ query = [NSString stringWithFormat:@"CHECKSUM TABLE `%@`", [self table]];
+ theResult = [mySQLConnection queryString:query];
+
+ // Check for errors
+ if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""]) {
+ NSRunAlertPanel(@"Error", [NSString stringWithFormat:@"An error occured while performming checksum on table.\n\n: %@",[mySQLConnection getLastErrorMessage]], @"OK", nil, nil);
+ }
+
+ // Process result
+ theRow = [[theResult fetch2DResultAsType:MCPTypeDictionary] lastObject];
+ NSRunInformationalAlertPanel([NSString stringWithFormat:@"Checksum '%@' table", [self table]], [NSString stringWithFormat:@"Checksum: %@", [theRow objectForKey:@"Checksum"]], @"OK", nil, nil);
+}
+
+#pragma mark Other Methods
+/**
+ * returns the host
+ */
+- (NSString *)host
+{
+ return [hostField stringValue];
+}
+
+/**
+ * passes query to tablesListInstance
+ */
+- (void)doPerformQueryService:(NSString *)query
+{
+ [tableWindow makeKeyAndOrderFront:self];
+ [tablesListInstance doPerformQueryService:query];
+}
+
+/**
+ * flushes the mysql privileges
+ */
+- (void)flushPrivileges:(id)sender
+{
+ [mySQLConnection queryString:@"FLUSH PRIVILEGES"];
+
+ if ( [[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) {
+ //flushed privileges without errors
+ NSBeginAlertSheet(NSLocalizedString(@"Flushed Privileges", @"title of panel when successfully flushed privs"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Succesfully flushed privileges.", @"message of panel when successfully flushed privs"));
+ } else {
+ //error while flushing privileges
+ NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, [NSString stringWithFormat:NSLocalizedString(@"Couldn't flush privileges.\nMySQL said: %@", @"message of panel when flushing privs failed"),
+ [mySQLConnection getLastErrorMessage]]);
+ }
+}
+
+- (void)showVariables:(id)sender
+/*
+ shows the mysql variables
+ */
+{
+ CMMCPResult *theResult;
+ NSMutableArray *tempResult = [NSMutableArray array];
+ int i;
+
+ if ( variables ) {
+ [variables release];
+ variables = nil;
+ }
+ //get variables
+ theResult = [mySQLConnection queryString:@"SHOW VARIABLES"];
+ if ([theResult numOfRows]) [theResult dataSeek:0];
+ for ( i = 0 ; i < [theResult numOfRows] ; i++ ) {
+ [tempResult addObject:[theResult fetchRowAsDictionary]];
+ }
+ variables = [[NSArray arrayWithArray:tempResult] retain];
+ [variablesTableView reloadData];
+ //show variables sheet
+ [NSApp beginSheet:variablesSheet
+ modalForWindow:tableWindow modalDelegate:self
+ didEndSelector:nil contextInfo:nil];
+ [NSApp runModalForWindow:variablesSheet];
+
+ [NSApp endSheet:variablesSheet];
+ [variablesSheet orderOut:nil];
+}
+
+- (void)closeConnection
+{
+ [mySQLConnection disconnect];
+
+ // Disconnected Growl notification
+ [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Disconnected"
+ description:[NSString stringWithFormat:NSLocalizedString(@"Disconnected from %@",@"description for disconnected growl notification"), [tableWindow title]]
+ notificationName:@"Disconnected"];
+}
+
+
+//getter methods
+- (NSString *)database
+/*
+ returns the currently selected database
+ */
+{
+ return selectedDatabase;
+}
+
+- (NSString *)table
+/*
+ returns the currently selected table (passing the request to TablesList)
+ */
+{
+ return (NSString *)[tablesListInstance table];
+}
+
+- (NSString *)mySQLVersion
+/*
+ returns the mysql version
+ */
+{
+ return mySQLVersion;
+}
+
+- (NSString *)user
+/*
+ returns the mysql version
+ */
+{
+ return [userField stringValue];
+}
+
+
+//notification center methods
+- (void)willPerformQuery:(NSNotification *)notification
+/*
+ invoked before a query is performed
+ */
+{
+ // Only start the progress indicator is this document window is key.
+ // Because we are starting the progress indicator based on the notification
+ // of a query being started, we have to prevent other windows from
+ // starting theirs. The same is also true for the below hasPerformedQuery:
+ // method.
+ //
+ // This code should be removed. Updating user interface elements based on
+ // notifications is bad practice as notifications are global to the application.
+ if ([tableWindow isKeyWindow]) {
+ [queryProgressBar startAnimation:self];
+ }
+}
+
+- (void)hasPerformedQuery:(NSNotification *)notification
+/*
+ invoked after a query has been performed
+ */
+{
+ if ([tableWindow isKeyWindow]) {
+ [queryProgressBar stopAnimation:self];
+ }
+}
+
+- (void)applicationWillTerminate:(NSNotification *)notification
+/*
+ invoked when the application will terminate
+ */
+{
+ [tablesListInstance selectionShouldChangeInTableView:nil];
+}
+
+- (void)tunnelStatusChanged:(NSNotification *)notification
+/*
+ the status of the tunnel has changed
+ */
+{
+}
+
+//menu methods
+- (IBAction)import:(id)sender
+/*
+ passes the request to the tableDump object
+ */
+{
+ [tableDumpInstance importFile];
+}
+
+- (IBAction)export:(id)sender
+/*
+ passes the request to the tableDump object
+ */
+{
+ [tableDumpInstance exportFile:[sender tag]];
+}
+
+- (IBAction)exportTable:(id)sender
+{
+ return [self export:sender];
+}
+
+- (IBAction)exportMultipleTables:(id)sender
+{
+ return [self export:sender];
+}
+
+/**
+ * Menu validation
+ */
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+ if ([menuItem action] == @selector(import:)) {
+ return ([self database] != nil);
+ }
+
+ if ([menuItem action] == @selector(importCSV:)) {
+ return ([self database] != nil && [self table] != nil);
+ }
+
+ if ([menuItem action] == @selector(export:)) {
+ return ([self database] != nil);
+ }
+
+ if ([menuItem action] == @selector(exportTable:)) {
+ return ([self database] != nil && [self table] != nil);
+ }
+
+ if ([menuItem action] == @selector(exportMultipleTables:)) {
+ return ([self database] != nil);
+ }
+
+ if ([menuItem action] == @selector(chooseEncoding:)) {
+ return [self supportsEncoding];
+ }
+
+ // table menu items
+ if ([menuItem action] == @selector(showCreateTableSyntax:) ||
+ [menuItem action] == @selector(copyCreateTableSyntax:) ||
+ [menuItem action] == @selector(checkTable:) ||
+ [menuItem action] == @selector(analyzeTable:) ||
+ [menuItem action] == @selector(optimizeTable:) ||
+ [menuItem action] == @selector(repairTable:) ||
+ [menuItem action] == @selector(flushTable:) ||
+ [menuItem action] == @selector(checksumTable:))
+ {
+ return ([self table] != nil && [[self table] isNotEqualTo:@""]);
+ }
+ return [super validateMenuItem:menuItem];
+}
+
+- (IBAction)viewStructure:(id)sender
+{
+ [tableTabView selectTabViewItemAtIndex:0];
+ [mainToolbar setSelectedItemIdentifier:@"SwitchToTableStructureToolbarItemIdentifier"];
+}
+
+- (IBAction)viewContent:(id)sender
+{
+ [tableTabView selectTabViewItemAtIndex:1];
+ [mainToolbar setSelectedItemIdentifier:@"SwitchToTableContentToolbarItemIdentifier"];
+}
+
+- (IBAction)viewQuery:(id)sender
+{
+ [tableTabView selectTabViewItemAtIndex:2];
+ [mainToolbar setSelectedItemIdentifier:@"SwitchToRunQueryToolbarItemIdentifier"];
+}
+
+- (IBAction)viewStatus:(id)sender
+{
+ [tableTabView selectTabViewItemAtIndex:3];
+ [mainToolbar setSelectedItemIdentifier:@"SwitchToTableStatusToolbarItemIdentifier"];
+}
+
+
+#pragma mark Toolbar Methods
+
+/**
+ * set up the standard toolbar
+ */
+- (void)setupToolbar
+{
+ // create a new toolbar instance, and attach it to our document window
+ mainToolbar = [[[NSToolbar alloc] initWithIdentifier:@"TableWindowToolbar"] autorelease];
+
+ // set up toolbar properties
+ [mainToolbar setAllowsUserCustomization: YES];
+ [mainToolbar setAutosavesConfiguration: YES];
+ [mainToolbar setDisplayMode:NSToolbarDisplayModeIconAndLabel];
+
+ // set ourself as the delegate
+ [mainToolbar setDelegate:self];
+
+ // attach the toolbar to the document window
+ [tableWindow setToolbar:mainToolbar];
+
+ // select the structure toolbar item
+ [self viewStructure:self];
+
+ // update the toolbar item size
+ [self updateChooseDatabaseToolbarItemWidth];
+}
+
+/**
+ * toolbar delegate method
+ */
+- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)willBeInsertedIntoToolbar
+{
+ NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier] autorelease];
+
+ if ([itemIdentifier isEqualToString:@"DatabaseSelectToolbarItemIdentifier"]) {
+ [toolbarItem setLabel:NSLocalizedString(@"Select Database", @"toolbar item for selecting a db")];
+ [toolbarItem setPaletteLabel:[toolbarItem label]];
+ [toolbarItem setView:chooseDatabaseButton];
+ [toolbarItem setMinSize:NSMakeSize(200,26)];
+ [toolbarItem setMaxSize:NSMakeSize(200,32)];
+ [chooseDatabaseButton setTarget:self];
+ [chooseDatabaseButton setAction:@selector(chooseDatabase:)];
+
+ if (willBeInsertedIntoToolbar) {
+ chooseDatabaseToolbarItem = toolbarItem;
+ [self updateChooseDatabaseToolbarItemWidth];
+ }
+
+ } else if ([itemIdentifier isEqualToString:@"ToggleConsoleIdentifier"]) {
+ //set the text label to be displayed in the toolbar and customization palette
+ [toolbarItem setPaletteLabel:NSLocalizedString(@"Show/Hide Console", @"toolbar item for show/hide console")];
+ //set up tooltip and image
+ [toolbarItem setToolTip:NSLocalizedString(@"Show or hide the console which shows all MySQL commands performed by Sequel Pro", @"tooltip for toolbar item for show/hide console")];
+ if ( [self consoleIsOpened] ) {
+ [toolbarItem setLabel:NSLocalizedString(@"Hide Console", @"toolbar item for hide console")];
+ [toolbarItem setImage:[NSImage imageNamed:@"hideconsole"]];
+ } else {
+ [toolbarItem setLabel:NSLocalizedString(@"Show Console", @"toolbar item for showconsole")];
+ [toolbarItem setImage:[NSImage imageNamed:@"showconsole"]];
+ }
+ //set up the target action
+ [toolbarItem setTarget:self];
+ [toolbarItem setAction:@selector(toggleConsole:)];
+
+ } else if ([itemIdentifier isEqualToString:@"ClearConsoleIdentifier"]) {
+ //set the text label to be displayed in the toolbar and customization palette
+ [toolbarItem setLabel:NSLocalizedString(@"Clear Console", @"toolbar item for clear console")];
+ [toolbarItem setPaletteLabel:NSLocalizedString(@"Clear Console", @"toolbar item for clear console")];
+ //set up tooltip and image
+ [toolbarItem setToolTip:NSLocalizedString(@"Clear the console which shows all MySQL commands performed by Sequel Pro", @"tooltip for toolbar item for clear console")];
+ [toolbarItem setImage:[NSImage imageNamed:@"clearconsole"]];
+ //set up the target action
+ [toolbarItem setTarget:self];
+ [toolbarItem setAction:@selector(clearConsole:)];
+
+ } else if ([itemIdentifier isEqualToString:@"SwitchToTableStructureToolbarItemIdentifier"]) {
+ [toolbarItem setLabel:NSLocalizedString(@"Table", @"toolbar item label for switching to the Table Structure tab")];
+ [toolbarItem setPaletteLabel:NSLocalizedString(@"Table Structure", @"toolbar item label for switching to the Table Structure tab")];
+ //set up tooltip and image
+ [toolbarItem setToolTip:NSLocalizedString(@"Switch to the Table Structure tab", @"tooltip for toolbar item for switching to the Table Structure tab")];
+ [toolbarItem setImage:[NSImage imageNamed:@"toolbar-switch-to-structure"]];
+ //set up the target action
+ [toolbarItem setTarget:self];
+ [toolbarItem setAction:@selector(viewStructure:)];
+
+ } else if ([itemIdentifier isEqualToString:@"SwitchToTableContentToolbarItemIdentifier"]) {
+ [toolbarItem setLabel:NSLocalizedString(@"Browse", @"toolbar item label for switching to the Table Content tab")];
+ [toolbarItem setPaletteLabel:NSLocalizedString(@"Table Content", @"toolbar item label for switching to the Table Content tab")];
+ //set up tooltip and image
+ [toolbarItem setToolTip:NSLocalizedString(@"Switch to the Table Content tab", @"tooltip for toolbar item for switching to the Table Content tab")];
+ [toolbarItem setImage:[NSImage imageNamed:@"toolbar-switch-to-browse"]];
+ //set up the target action
+ [toolbarItem setTarget:self];
+ [toolbarItem setAction:@selector(viewContent:)];
+
+ } else if ([itemIdentifier isEqualToString:@"SwitchToRunQueryToolbarItemIdentifier"]) {
+ [toolbarItem setLabel:NSLocalizedString(@"SQL", @"toolbar item label for switching to the Run Query tab")];
+ [toolbarItem setPaletteLabel:NSLocalizedString(@"Run Query", @"toolbar item label for switching to the Run Query tab")];
+ //set up tooltip and image
+ [toolbarItem setToolTip:NSLocalizedString(@"Switch to the Run Query tab", @"tooltip for toolbar item for switching to the Run Query tab")];
+ [toolbarItem setImage:[NSImage imageNamed:@"toolbar-switch-to-sql"]];
+ //set up the target action
+ [toolbarItem setTarget:self];
+ [toolbarItem setAction:@selector(viewQuery:)];
+
+ } else if ([itemIdentifier isEqualToString:@"SwitchToTableStatusToolbarItemIdentifier"]) {
+ [toolbarItem setLabel:NSLocalizedString(@"Table Status", @"toolbar item label for switching to the Table Status tab")];
+ [toolbarItem setPaletteLabel:NSLocalizedString(@"Table Status", @"toolbar item label for switching to the Table Status tab")];
+ //set up tooltip and image
+ [toolbarItem setToolTip:NSLocalizedString(@"Switch to the Table Status tab", @"tooltip for toolbar item for switching to the Table Status tab")];
+ [toolbarItem setImage:[NSImage imageNamed:@"toolbar-switch-to-table-info"]];
+ //set up the target action
+ [toolbarItem setTarget:self];
+ [toolbarItem setAction:@selector(viewStatus:)];
+
+ } else {
+ //itemIdentifier refered to a toolbar item that is not provided or supported by us or cocoa
+ toolbarItem = nil;
+ }
+
+ return toolbarItem;
+}
+
+/**
+ * toolbar delegate method
+ */
+- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
+{
+ return [NSArray arrayWithObjects:
+ @"DatabaseSelectToolbarItemIdentifier",
+ @"ToggleConsoleIdentifier",
+ @"ClearConsoleIdentifier",
+ @"FlushPrivilegesIdentifier",
+ @"SwitchToTableStructureToolbarItemIdentifier",
+ @"SwitchToTableContentToolbarItemIdentifier",
+ @"SwitchToRunQueryToolbarItemIdentifier",
+ @"SwitchToTableStatusToolbarItemIdentifier",
+ NSToolbarCustomizeToolbarItemIdentifier,
+ NSToolbarFlexibleSpaceItemIdentifier,
+ NSToolbarSpaceItemIdentifier,
+ NSToolbarSeparatorItemIdentifier,
+ nil];
+}
+
+/**
+ * toolbar delegate method
+ */
+- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
+{
+ return [NSArray arrayWithObjects:
+ @"DatabaseSelectToolbarItemIdentifier",
+ NSToolbarFlexibleSpaceItemIdentifier,
+ @"SwitchToTableStructureToolbarItemIdentifier",
+ @"SwitchToTableContentToolbarItemIdentifier",
+ @"SwitchToRunQueryToolbarItemIdentifier",
+ NSToolbarFlexibleSpaceItemIdentifier,
+ nil];
+}
+
+- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
+{
+ return [NSArray arrayWithObjects:
+ @"SwitchToTableStructureToolbarItemIdentifier",
+ @"SwitchToTableContentToolbarItemIdentifier",
+ @"SwitchToRunQueryToolbarItemIdentifier",
+ @"SwitchToTableStatusToolbarItemIdentifier",
+ nil];
+
+}
+
+/**
+ * validates the toolbar items
+ */
+- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem;
+{
+ if ( [[toolbarItem itemIdentifier] isEqualToString:@"ToggleConsoleIdentifier"] ) {
+ if ( [self consoleIsOpened] ) {
+ [toolbarItem setLabel:@"Hide Console"];
+ [toolbarItem setImage:[NSImage imageNamed:@"hideconsole"]];
+ } else {
+ [toolbarItem setLabel:@"Show Console"];
+ [toolbarItem setImage:[NSImage imageNamed:@"showconsole"]];
+ }
+ }
+
+ return YES;
+}
+
+
+//NSDocument methods
+- (NSString *)windowNibName
+/*
+ returns the name of the nib file
+ */
+{
+ return @"DBView";
+}
+
+- (void)windowControllerDidLoadNib:(NSWindowController *) aController
+/*
+ code that need to be executed once the windowController has loaded the document's window
+ sets upt the interface (small fonts)
+ */
+{
+ [aController setShouldCascadeWindows:YES];
+ [super windowControllerDidLoadNib:aController];
+
+ NSEnumerator *theCols = [[variablesTableView tableColumns] objectEnumerator];
+ NSTableColumn *theCol;
+
+ // [tableWindow makeKeyAndOrderFront:self];
+
+ prefs = [[NSUserDefaults standardUserDefaults] retain];
+ selectedFavorite = [[NSString alloc] initWithString:NSLocalizedString(@"Custom", @"menu item for custom connection")];
+
+ //register for notifications
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willPerformQuery:)
+ name:@"SMySQLQueryWillBePerformed" object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hasPerformedQuery:)
+ name:@"SMySQLQueryHasBeenPerformed" object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:)
+ name:@"NSApplicationWillTerminateNotification" object:nil];
+
+ //set up interface
+ if ( [prefs boolForKey:@"useMonospacedFonts"] ) {
+ [consoleTextView setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]];
+ [syntaxViewContent setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]];
+
+ while ( (theCol = [theCols nextObject]) ) {
+ [[theCol dataCell] setFont:[NSFont fontWithName:@"Monaco" size:10]];
+ }
+ } else {
+ [consoleTextView setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
+ [syntaxViewContent setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
+ while ( (theCol = [theCols nextObject]) ) {
+ [[theCol dataCell] setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
+ }
+ }
+ [consoleDrawer setContentSize:NSMakeSize(110,110)];
+
+ //set up toolbar
+ [self setupToolbar];
+ // [self connectToDB:nil];
+ [self performSelector:@selector(connectToDB:) withObject:tableWindow afterDelay:0.0f];
+}
+
+- (void)windowWillClose:(NSNotification *)aNotification
+{
+ if ([mySQLConnection isConnected]) [self closeConnection];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+
+//NSWindow delegate methods
+- (BOOL)windowShouldClose:(id)sender
+/*
+ invoked when the document window should close
+ */
+{
+ if ( ![tablesListInstance selectionShouldChangeInTableView:nil] ) {
+ return NO;
+ } else {
+ return YES;
+ }
+
+}
+
+
+//SMySQL delegate methods
+- (void)willQueryString:(NSString *)query
+/*
+invoked when framework will perform a query
+*/
+{
+ NSString *currentTime = [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:nil];
+
+ [self showMessageInConsole:[NSString stringWithFormat:@"/* MySQL %@ */ %@;\n", currentTime, query]];
+}
+
+- (void)queryGaveError:(NSString *)error
+/*
+invoked when query gave an error
+*/
+{
+ NSString *currentTime = [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:nil];
+
+ [self showErrorInConsole:[NSString stringWithFormat:@"/* ERROR %@ */ %@;\n", currentTime, error]];
+}
+
+#pragma mark SplitView delegate methods
+
+/**
+ * tells the splitView that it can collapse views
+ */
+- (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview
+{
+ return YES;
+}
+
+/**
+ * defines max position of splitView
+ */
+- (float)splitView:(NSSplitView *)sender constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(int)offset
+{
+ return proposedMax - 600;
+}
+
+/**
+ * defines min position of splitView
+ */
+- (float)splitView:(NSSplitView *)sender constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)offset
+{
+ return proposedMin + 160;
+}
+
+- (void)splitViewDidResizeSubviews:(NSNotification *)notification
+{
+ [self updateChooseDatabaseToolbarItemWidth];
+}
+
+- (NSRect)splitView:(NSSplitView *)splitView additionalEffectiveRectOfDividerAtIndex:(int)dividerIndex
+{
+ if (sidebarGrabber != nil) {
+ return [sidebarGrabber convertRect:[sidebarGrabber bounds] toView:splitView];
+ } else {
+ return NSZeroRect;
+ }
+}
+
+- (void)updateChooseDatabaseToolbarItemWidth
+{
+ // make sure the toolbar item is actually in the toolbar
+ if (!chooseDatabaseToolbarItem)
+ return;
+
+ // grab the width of the left pane
+ float leftPaneWidth = [dbTablesTableView frame].size.width;
+
+ // subtract some pixels to allow for misc stuff
+ leftPaneWidth -= 12;
+
+ // make sure it's not too small or to big
+ if (leftPaneWidth < 130)
+ leftPaneWidth = 130;
+ if (leftPaneWidth > 360)
+ leftPaneWidth = 360;
+
+ // apply the size
+ [chooseDatabaseToolbarItem setMinSize:NSMakeSize(leftPaneWidth, 26)];
+ [chooseDatabaseToolbarItem setMaxSize:NSMakeSize(leftPaneWidth, 32)];
+}
+
+
+//tableView datasource methods
+- (int)numberOfRowsInTableView:(NSTableView *)aTableView
+{
+ return [variables count];
+}
+
+- (id)tableView:(NSTableView *)aTableView
+objectValueForTableColumn:(NSTableColumn *)aTableColumn
+ row:(int)rowIndex
+{
+ id theValue;
+
+ theValue = [[variables objectAtIndex:rowIndex] objectForKey:[aTableColumn identifier]];
+
+ if ( [theValue isKindOfClass:[NSData class]] ) {
+ theValue = [[NSString alloc] initWithData:theValue encoding:[mySQLConnection encoding]];
+ }
+
+ return theValue;
+}
+
+- (IBAction)terminate:(id)sender
+{
+ [[NSApp orderedDocuments] makeObjectsPerformSelector:@selector(cancelConnectSheet:) withObject:nil];
+ [NSApp terminate:sender];
+}
+
+- (void)dealloc
+{
+ [chooseDatabaseButton release];
+ [mySQLConnection release];
+ [favorites release];
+ [variables release];
+ [selectedDatabase release];
+ [selectedFavorite release];
+ [mySQLVersion release];
+ [prefs release];
+
+ [super dealloc];
+}
+
+@end