aboutsummaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorstuconnolly <stuart02@gmail.com>2009-11-12 23:44:09 +0000
committerstuconnolly <stuart02@gmail.com>2009-11-12 23:44:09 +0000
commita5af1bc73e296ff0b545310782a446259d896389 (patch)
tree82165af1afe69187bafd3e96c8b6e9bb41b4700b /Source
parent64010e52c3f4bcec585b7fb2494ccf38a481b5f6 (diff)
downloadsequelpro-a5af1bc73e296ff0b545310782a446259d896389.tar.gz
sequelpro-a5af1bc73e296ff0b545310782a446259d896389.tar.bz2
sequelpro-a5af1bc73e296ff0b545310782a446259d896389.zip
- New server processes panel, accessible via the 'Database' menu and alt+cmd+P. Includes the ability to kill queries and connections as well as live filtering support and the ability to save all processes or the current filtered set to a file. Implements issue #458.
- Reorganise 'Database' menu. - Give 'Flush Privileges' key equivalent of shift+cmd+F.
Diffstat (limited to 'Source')
-rw-r--r--Source/SPConstants.h6
-rw-r--r--Source/SPConstants.m4
-rw-r--r--Source/SPProcessListController.h56
-rw-r--r--Source/SPProcessListController.m498
-rw-r--r--Source/TableDocument.h4
-rw-r--r--Source/TableDocument.m36
6 files changed, 598 insertions, 6 deletions
diff --git a/Source/SPConstants.h b/Source/SPConstants.h
index aafabd73..e42b11b7 100644
--- a/Source/SPConstants.h
+++ b/Source/SPConstants.h
@@ -58,7 +58,7 @@
#define MAIN_TOOLBAR_TABLE_RELATIONS @"SwitchToTableRelationsToolbarItemIdentifier"
#define MAIN_TOOLBAR_USER_MANAGER @"SwitchToUserManagerToolbarItemIdentifier"
-// View Modes
+// View modes
typedef enum {
SPStructureViewMode = 1,
SPContentViewMode = 2,
@@ -67,6 +67,10 @@ typedef enum {
SPQueryEditorViewMode = 5
} SPViewMode;
+// Kill mode constants
+extern NSString *SPKillProcessQueryMode;
+extern NSString *SPKillProcessConnectionMode;
+
// Preference key constants
// General Prefpane
extern NSString *SPDefaultFavorite;
diff --git a/Source/SPConstants.m b/Source/SPConstants.m
index 21b9bfe7..188a6282 100644
--- a/Source/SPConstants.m
+++ b/Source/SPConstants.m
@@ -25,6 +25,10 @@
#import "SPConstants.h"
+// Kill mode constants
+NSString *SPKillProcessQueryMode = @"SPKillProcessQueryMode";
+NSString *SPKillProcessConnectionMode = @"SPKillProcessConnectionMode";
+
// Preference key constants
// General Prefpane
NSString *SPDefaultFavorite = @"DefaultFavorite";
diff --git a/Source/SPProcessListController.h b/Source/SPProcessListController.h
new file mode 100644
index 00000000..3e93f117
--- /dev/null
+++ b/Source/SPProcessListController.h
@@ -0,0 +1,56 @@
+//
+// $Id$
+//
+// SPProcessListController.h
+// sequel-pro
+//
+// Created by Stuart Connolly (stuconnolly.com) on November 12, 2009
+// Copyright (c) 2009 Stuart Connolly. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Cocoa/Cocoa.h>
+
+@class MCPConnection;
+
+@interface SPProcessListController : NSWindowController
+{
+ MCPConnection *connection;
+
+ NSMutableArray *processes, *processesFiltered;
+
+ IBOutlet NSTableView *processListTableView;
+ IBOutlet NSTextField *processesCountTextField;
+ IBOutlet NSSearchField *filterProcessesSearchField;
+ IBOutlet NSProgressIndicator *refreshProgressIndicator;
+ IBOutlet NSButton *closeProcessListButton;
+ IBOutlet NSButton *saveProcessesButton;
+ IBOutlet NSButton *refreshProcessesButton;
+}
+
+@property (readwrite, assign) MCPConnection *connection;
+
+- (IBAction)copy:(id)sender;
+- (IBAction)closeSheet:(id)sender;
+- (IBAction)refreshProcessList:(id)sender;
+- (IBAction)saveServerProcesses:(id)sender;
+- (IBAction)killProcessQuery:(id)sender;
+- (IBAction)killProcessConnection:(id)sender;
+
+- (void)displayProcessListSheetAttachedToWindow:(NSWindow *)window;
+
+@end
diff --git a/Source/SPProcessListController.m b/Source/SPProcessListController.m
new file mode 100644
index 00000000..c1f52d3c
--- /dev/null
+++ b/Source/SPProcessListController.m
@@ -0,0 +1,498 @@
+//
+// $Id$
+//
+// SPProcessListController.m
+// sequel-pro
+//
+// Created by Stuart Connolly (stuconnolly.com) on November 12, 2009
+// Copyright (c) 2009 Stuart Connolly. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <MCPKit/MCPKit.h>
+
+#import "SPProcessListController.h"
+#import "SPConstants.h"
+#import "SPArrayAdditions.h"
+
+@interface SPProcessListController (PrivateAPI)
+
+- (void)_getDatabaseProcessList;
+- (void)_killProcessQueryWithId:(NSUInteger)processId;
+- (void)_killProcessConnectionWithId:(NSUInteger)processId;
+- (void)_updateServerProcessesFilterForFilterString:(NSString *)filterString;
+
+@end
+
+@implementation SPProcessListController
+
+@synthesize connection;
+
+/**
+ * Initialisation
+ */
+- (id)init
+{
+ if ((self = [super initWithWindowNibName:@"DatabaseProcessList"])) {
+ processes = [[NSMutableArray alloc] init];
+ }
+
+ return self;
+}
+
+/**
+ * Interface initialisation
+ */
+- (void)awakeFromNib
+{
+ // Set the process table view's vertical gridlines if required
+ [processListTableView setGridStyleMask:([[NSUserDefaults standardUserDefaults] boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
+}
+
+#pragma mark -
+#pragma mark IBAction methods
+
+/**
+ * Copies the currently selected process(es) to the pasteboard.
+ */
+- (IBAction)copy:(id)sender
+{
+ NSResponder *firstResponder = [[self window] firstResponder];
+
+ if ((firstResponder == processListTableView) && ([processListTableView numberOfSelectedRows] > 0)) {
+
+ NSMutableString *string = [NSMutableString string];
+ NSIndexSet *rows = [processListTableView selectedRowIndexes];
+
+ NSUInteger i = [rows firstIndex];
+
+ while (i != NSNotFound)
+ {
+ if (i < [processesFiltered count]) {
+ NSDictionary *process = NSArrayObjectAtIndex(processesFiltered, i);
+
+ NSString *stringTmp = [NSString stringWithFormat:@"%@ %@ %@ %@ %@ %@ %@ %@",
+ [process objectForKey:@"Id"],
+ [process objectForKey:@"User"],
+ [process objectForKey:@"Host"],
+ [process objectForKey:@"db"],
+ [process objectForKey:@"Command"],
+ [process objectForKey:@"Time"],
+ [process objectForKey:@"State"],
+ [process objectForKey:@"Info"]];
+
+ [string appendString:stringTmp];
+ [string appendString:@"\n"];
+ }
+
+ i = [rows indexGreaterThanIndex:i];
+ }
+
+ NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
+
+ // Copy the string to the pasteboard
+ [pasteBoard declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil] owner:nil];
+ [pasteBoard setString:string forType:NSStringPboardType];
+ }
+}
+
+/**
+ * Close the process list sheet.
+ */
+- (IBAction)closeSheet:(id)sender
+{
+ [NSApp endSheet:[self window] returnCode:[sender tag]];
+ [[self window] orderOut:self];
+
+ // If the filtered array is allocated and it's not a reference to the processes array get rid of it
+ if ((processesFiltered) && (processesFiltered != processes)) {
+ [processesFiltered release], processesFiltered = nil;
+ }
+}
+
+/**
+ * Refreshes the process list.
+ */
+- (IBAction)refreshProcessList:(id)sender
+{
+ // Start progress Indicator
+ [refreshProgressIndicator startAnimation:self];
+ [refreshProgressIndicator setHidden:NO];
+
+ // Disable controls
+ [refreshProcessesButton setEnabled:NO];
+ [closeProcessListButton setEnabled:NO];
+ [saveProcessesButton setEnabled:NO];
+ [filterProcessesSearchField setEnabled:NO];
+
+ [self _getDatabaseProcessList];
+
+ // Reapply any filters is required
+ if ([[filterProcessesSearchField stringValue] length] > 0) {
+ [self _updateServerProcessesFilterForFilterString:[filterProcessesSearchField stringValue]];
+ }
+
+ [processListTableView reloadData];
+
+ // Enable controls
+ [filterProcessesSearchField setEnabled:YES];
+ [saveProcessesButton setEnabled:YES];
+ [closeProcessListButton setEnabled:YES];
+ [refreshProcessesButton setEnabled:YES];
+
+ // Stop progress Indicator
+ [refreshProgressIndicator stopAnimation:self];
+ [refreshProgressIndicator setHidden:YES];
+}
+
+/**
+ * Saves the process list to the selected file.
+ */
+- (IBAction)saveServerProcesses:(id)sender
+{
+ NSSavePanel *panel = [NSSavePanel savePanel];
+
+ [panel setExtensionHidden:NO];
+ [panel setAllowsOtherFileTypes:YES];
+ [panel setCanSelectHiddenExtension:YES];
+
+ [panel beginSheetForDirectory:nil file:@"ServerProcesses" modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:nil];
+}
+
+/**
+ * Kills the currently selected process' query.
+ */
+- (IBAction)killProcessQuery:(id)sender
+{
+ // No process selected. Interface validation should prevent this.
+ if ([processListTableView numberOfSelectedRows] != 1) return;
+
+ NSUInteger processId = [[[processes objectAtIndex:[processListTableView selectedRow]] valueForKey:@"Id"] integerValue];
+
+ NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Kill query?", @"kill query message")
+ defaultButton:NSLocalizedString(@"Kill", @"kill button")
+ alternateButton:NSLocalizedString(@"Cancel", @"cancel button")
+ otherButton:nil
+ informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to kill the current query executing on connection ID %d.\n\nPlease be aware that continuing to kill this query may result in data corruption. Please proceed with caution.", @"kill query informative message"), processId]];
+
+ NSArray *buttons = [alert buttons];
+
+ // Change the alert's cancel button to have the key equivalent of return
+ [[buttons objectAtIndex:0] setKeyEquivalent:@"k"];
+ [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask];
+ [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+
+ [alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:SPKillProcessQueryMode];
+}
+
+/**
+ * Kills the currently selected proceess' connection.
+ */
+- (IBAction)killProcessConnection:(id)sender
+{
+ // No process selected. Interface validation should prevent this.
+ if ([processListTableView numberOfSelectedRows] != 1) return;
+
+ NSUInteger processId = [[[processes objectAtIndex:[processListTableView selectedRow]] valueForKey:@"Id"] integerValue];
+
+ NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Kill connection?", @"kill connection message")
+ defaultButton:NSLocalizedString(@"Kill", @"kill button")
+ alternateButton:NSLocalizedString(@"Cancel", @"cancel button")
+ otherButton:nil
+ informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to kill connection ID %d.\n\nPlease be aware that continuing to kill this connection may result in data corruption. Please proceed with caution.", @"kill connection informative message"), processId]];
+
+ NSArray *buttons = [alert buttons];
+
+ // Change the alert's cancel button to have the key equivalent of return
+ [[buttons objectAtIndex:0] setKeyEquivalent:@"k"];
+ [[buttons objectAtIndex:0] setKeyEquivalentModifierMask:NSCommandKeyMask];
+ [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+
+ [alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:SPKillProcessConnectionMode];
+}
+
+#pragma mark -
+#pragma mark Other methods
+
+/**
+ * Displays the process list sheet attached to the supplied window.
+ */
+- (void)displayProcessListSheetAttachedToWindow:(NSWindow *)window
+{
+ // Weak reference
+ processesFiltered = processes;
+
+ // Get the current process list
+ [self _getDatabaseProcessList];
+
+ // Reload the tableview
+ [processListTableView reloadData];
+
+ // If the search field already has value from when the panel was previously open, apply the filter.
+ if ([[filterProcessesSearchField stringValue] length] > 0) {
+ [self _updateServerProcessesFilterForFilterString:[filterProcessesSearchField stringValue]];
+ }
+
+ // Open the sheet
+ [NSApp beginSheet:[self window] modalForWindow:window modalDelegate:self didEndSelector:nil contextInfo:nil];
+}
+
+/**
+ * Invoked when the kill alerts are dismissed. Decide what to do based on the user's decision.
+ */
+- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo
+{
+ if (returnCode == NSAlertDefaultReturn) {
+ NSUInteger processId = [[[processes objectAtIndex:[processListTableView selectedRow]] valueForKey:@"Id"] integerValue];
+
+ if ([contextInfo isEqualToString:SPKillProcessQueryMode]) {
+ [self _killProcessQueryWithId:processId];
+ }
+ else if ([contextInfo isEqualToString:SPKillProcessConnectionMode]) {
+ [self _killProcessConnectionWithId:processId];
+ }
+ }
+}
+
+/**
+ * Invoked when the save panel is dismissed.
+ */
+- (void)savePanelDidEnd:(NSSavePanel *)panel returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo
+{
+ if (returnCode == NSAlertDefaultReturn) {
+ if ([processesFiltered count] > 0) {
+ NSMutableString *processesString = [NSMutableString string];
+
+ for (NSDictionary *process in processesFiltered)
+ {
+ NSString *stringTmp = [NSString stringWithFormat:@"%@ %@ %@ %@ %@ %@ %@ %@",
+ [process objectForKey:@"Id"],
+ [process objectForKey:@"User"],
+ [process objectForKey:@"Host"],
+ [process objectForKey:@"db"],
+ [process objectForKey:@"Command"],
+ [process objectForKey:@"Time"],
+ [process objectForKey:@"State"],
+ [process objectForKey:@"Info"]];
+
+ [processesString appendString:stringTmp];
+ [processesString appendString:@"\n"];
+ }
+
+ [processesString writeToFile:[panel filename] atomically:YES encoding:NSUTF8StringEncoding error:NULL];
+ }
+ }
+}
+
+/**
+ * Menu item validation.
+ */
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+ SEL action = [menuItem action];
+
+ if (action == @selector(copy:)) {
+ return ([processListTableView numberOfSelectedRows] > 0);
+ }
+
+ if ((action == @selector(killProcessQuery:)) || (action == @selector(killProcessConnection:))) {
+ return ([processListTableView numberOfSelectedRows] == 1);
+ }
+
+ return YES;
+}
+
+/**
+ * This method is called as part of Key Value Observing which is used to watch for prefernce changes which effect the interface.
+ */
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ if ([keyPath isEqualToString:SPDisplayTableViewVerticalGridlines]) {
+ [processListTableView setGridStyleMask:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
+ }
+}
+
+#pragma mark -
+#pragma mark Tableview delegate methods
+
+/**
+ * Table view delegate method. Returns the number of rows in the table veiw.
+ */
+- (int)numberOfRowsInTableView:(NSTableView *)tableView
+{
+ return [processesFiltered count];
+}
+
+/**
+ * Table view delegate method. Returns the specific object for the request column and row.
+ */
+- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
+{
+ id object = [[processesFiltered objectAtIndex:row] valueForKey:[tableColumn identifier]];
+
+ return (![object isNSNull]) ? object : @"NULL";
+}
+
+#pragma mark -
+#pragma mark Text field delegate methods
+
+/**
+ * Apply the filter string to the current process list.
+ */
+- (void)controlTextDidChange:(NSNotification *)notification
+{
+ id object = [notification object];
+
+ if (object == filterProcessesSearchField) {
+ [self _updateServerProcessesFilterForFilterString:[object stringValue]];
+ }
+}
+
+#pragma mark -
+
+/**
+ * Dealloc
+ */
+- (void)dealloc
+{
+ [processes release], processes = nil;
+
+ [super dealloc];
+}
+
+@end
+
+@implementation SPProcessListController (PrivateAPI)
+
+/**
+ * Gets the current process list form the database;
+ */
+- (void)_getDatabaseProcessList
+{
+ NSUInteger i = 0;
+
+ // Get processes
+ MCPResult *processList = [connection queryString:@"SHOW PROCESSLIST"];
+
+ if ([processList numOfRows]) [processList dataSeek:0];
+
+ [processes removeAllObjects];
+
+ for (i = 0; i < [processList numOfRows]; i++)
+ {
+ [processes addObject:[processList fetchRowAsDictionary]];
+ }
+}
+
+/**
+ * Attempts to kill the query executing on the connection associate with the supplied ID.
+ */
+- (void)_killProcessQueryWithId:(NSUInteger)processId
+{
+ // Kill the query
+ [connection queryString:[NSString stringWithFormat:@"KILL QUERY %d", processId]];
+
+ // Check for errors
+ if (![[connection getLastErrorMessage] isEqualToString:@""]) {
+ NSBeginAlertSheet(NSLocalizedString(@"Unable to kill query", @"error killing query message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, nil,
+ [NSString stringWithFormat:NSLocalizedString(@"An error occured while attempting to kill the query associated with connection %d.\n\nMySQL said: %@", @"error killing query informative message"), processId, [connection getLastErrorMessage]]);
+ }
+
+ // Refresh the process list
+ [self refreshProcessList:self];
+}
+
+/**
+ * Attempts the kill the connection associated with the supplied ID.
+ */
+- (void)_killProcessConnectionWithId:(NSUInteger)processId
+{
+ // Kill the connection
+ [connection queryString:[NSString stringWithFormat:@"KILL CONNECTION %d", processId]];
+
+ // Check for errors
+ if (![[connection getLastErrorMessage] isEqualToString:@""]) {
+ NSBeginAlertSheet(NSLocalizedString(@"Unable to kill connection", @"error killing connection message"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, nil,
+ [NSString stringWithFormat:NSLocalizedString(@"An error occured while attempting to kill connection %d.\n\nMySQL said: %@", @"error killing query informative message"), processId, [connection getLastErrorMessage]]);
+ }
+
+ // Refresh the process list
+ [self refreshProcessList:self];
+}
+
+/**
+ * Filter the displayed server processes against the supplied filter string.
+ */
+- (void)_updateServerProcessesFilterForFilterString:(NSString *)filterString
+{
+ [saveProcessesButton setEnabled:NO];
+
+ filterString = [[filterString lowercaseString] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ // If the filtered array is allocated and its not a reference to the processes array,
+ // relase it to prevent memory leaks upon the next allocation.
+ if ((processesFiltered) && (processesFiltered != processes)) {
+ [processesFiltered release], processesFiltered = nil;
+ }
+
+ processesFiltered = [[NSMutableArray alloc] init];
+
+ if ([filterString length] == 0) {
+ [processesFiltered release];
+ processesFiltered = processes;
+
+ [saveProcessesButton setEnabled:YES];
+ [saveProcessesButton setTitle:@"Save As..."];
+ [processesCountTextField setStringValue:@""];
+
+ [processListTableView reloadData];
+
+ return;
+ }
+
+ // Perform filtering
+ for (NSDictionary *process in processes)
+ {
+ if (([[process objectForKey:@"Id"] rangeOfString:filterString options:NSCaseInsensitiveSearch].location != NSNotFound) ||
+ ([[process objectForKey:@"User"] rangeOfString:filterString options:NSCaseInsensitiveSearch].location != NSNotFound) ||
+ ([[process objectForKey:@"Host"] rangeOfString:filterString options:NSCaseInsensitiveSearch].location != NSNotFound) ||
+ ((![[process objectForKey:@"db"] isNSNull]) && ([[process objectForKey:@"db"] rangeOfString:filterString options:NSCaseInsensitiveSearch].location != NSNotFound)) ||
+ ([[process objectForKey:@"Command"] rangeOfString:filterString options:NSCaseInsensitiveSearch].location != NSNotFound) ||
+ ([[process objectForKey:@"Time"] rangeOfString:filterString options:NSCaseInsensitiveSearch].location != NSNotFound) ||
+ ((![[process objectForKey:@"State"] isNSNull]) && ([[process objectForKey:@"State"] rangeOfString:filterString options:NSCaseInsensitiveSearch].location != NSNotFound)) ||
+ ((![[process objectForKey:@"Info"] isNSNull]) && ([[process objectForKey:@"Info"] rangeOfString:filterString options:NSCaseInsensitiveSearch].location != NSNotFound)))
+ {
+ [processesFiltered addObject:process];
+ }
+ }
+
+ [processListTableView reloadData];
+
+ [processesCountTextField setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%d of %d", "filtered server processes count"), [processesFiltered count], [processes count]]];
+ [processesCountTextField setHidden:NO];
+
+ if ([processesFiltered count] == 0) return;
+
+ [saveProcessesButton setEnabled:YES];
+ [saveProcessesButton setTitle:@"Save View As..."];
+}
+
+@end
diff --git a/Source/TableDocument.h b/Source/TableDocument.h
index 843bb9c3..ab36ffb3 100644
--- a/Source/TableDocument.h
+++ b/Source/TableDocument.h
@@ -29,7 +29,7 @@
#import <MCPKit/MCPKit.h>
#import <WebKit/WebKit.h>
-@class SPConnectionController, SPUserManager;
+@class SPConnectionController, SPProcessListController, SPUserManager;
enum sp_current_query_mode
{
@@ -120,6 +120,7 @@ enum sp_current_query_mode
int passwordSheetReturnCode;
SPConnectionController *connectionController;
+ SPProcessListController *processListController;
MCPConnection *mySQLConnection;
@@ -183,6 +184,7 @@ enum sp_current_query_mode
- (IBAction)removeDatabase:(id)sender;
- (IBAction)showMySQLHelp:(id)sender;
- (IBAction)saveServerVariables:(id)sender;
+- (IBAction)showDatabaseProcessList:(id)sender;
- (IBAction)openCurrentConnectionInNewWindow:(id)sender;
- (NSArray *)allDatabaseNames;
diff --git a/Source/TableDocument.m b/Source/TableDocument.m
index b8071060..683d6237 100644
--- a/Source/TableDocument.m
+++ b/Source/TableDocument.m
@@ -52,6 +52,7 @@
#import "SPEncodingPopupAccessory.h"
#import "SPConstants.h"
#import "YRKSpinningProgressIndicator.h"
+#import "SPProcessListController.h"
// Printing
#import "MGTemplateEngine.h"
@@ -166,8 +167,12 @@
// Set the connection controller's delegate
[connectionController setDelegate:self];
+
+ // Set the server variables table view's vertical gridlines if required
+ [variablesTableView setGridStyleMask:([[NSUserDefaults standardUserDefaults] boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
// Register observers for when the DisplayTableViewVerticalGridlines preference changes
+ [prefs addObserver:self forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
[prefs addObserver:tableSourceInstance forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
[prefs addObserver:tableContentInstance forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
[prefs addObserver:customQueryInstance forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
@@ -1046,6 +1051,23 @@
}
/**
+ * Displays the database process list sheet.
+ */
+- (IBAction)showDatabaseProcessList:(id)sender
+{
+ if (!processListController) {
+ processListController = [[SPProcessListController alloc] init];
+
+ [processListController setConnection:mySQLConnection];
+
+ // Register to obeserve table view vertical grid line pref changes
+ [prefs addObserver:processListController forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
+ }
+
+ [processListController displayProcessListSheetAttachedToWindow:tableWindow];
+}
+
+/**
* Returns an array of all available database names
*/
- (NSArray *)allDatabaseNames
@@ -2015,7 +2037,7 @@
// If it was the server variables sheet that was closed release the relevant arrays if necessary
if ([sender window] == variablesSheet) {
- // If the filtered array is allocated and its not a reference to the variables array get rid of it
+ // If the filtered array is allocated and it's not a reference to the variables array get rid of it
if ((variablesFiltered) && (variablesFiltered != variables)) {
[variablesFiltered release], variablesFiltered = nil;
}
@@ -2168,6 +2190,9 @@
if ([keyPath isEqualToString:SPConsoleEnableLogging]) {
[mySQLConnection setDelegateQueryLogging:[[change objectForKey:NSKeyValueChangeNewKey] boolValue]];
}
+ else if ([keyPath isEqualToString:SPDisplayTableViewVerticalGridlines]) {
+ [variablesTableView setGridStyleMask:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
+ }
}
/*
@@ -3599,7 +3624,7 @@
}
#pragma mark -
-#pragma mark
+#pragma mark Connection controller delegate methods
/**
* Invoked by the connection controller when it starts the process of initiating a connection.
@@ -3620,7 +3645,7 @@
}
#pragma mark -
-#pragma mark Database name field delegate methods
+#pragma mark Text field delegate methods
/**
* When adding a database, enable the button only if the new name has a length.
@@ -3717,12 +3742,15 @@
#pragma mark -
+/**
+ * Dealloc
+ */
- (void)dealloc
{
-
[_encoding release];
[printWebView release];
if (connectionController) [connectionController release];
+ if (processListController) [processListController release];
if (mySQLConnection) [mySQLConnection release];
if (variables) [variables release];
if (selectedDatabase) [selectedDatabase release];