diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/SPHistoryController.h | 59 | ||||
-rw-r--r-- | Source/SPHistoryController.m | 256 | ||||
-rw-r--r-- | Source/TableDocument.h | 2 | ||||
-rw-r--r-- | Source/TableDocument.m | 18 | ||||
-rw-r--r-- | Source/TablesList.h | 2 | ||||
-rw-r--r-- | Source/TablesList.m | 2 |
6 files changed, 338 insertions, 1 deletions
diff --git a/Source/SPHistoryController.h b/Source/SPHistoryController.h new file mode 100644 index 00000000..110ebea3 --- /dev/null +++ b/Source/SPHistoryController.h @@ -0,0 +1,59 @@ +// +// $Id: SPFieldEditorController.h 802 2009-06-03 20:46:57Z bibiko $ +// +// SPHistoryController.h +// sequel-pro +// +// Created by Rowan Beentje on July 23, 2009 +// +// 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 TableDocument; + +enum sphistory_view_types +{ + SP_VIEW_STRUCTURE = 0, + SP_VIEW_CONTENT = 1, + SP_VIEW_CUSTOMQUERY = 2, + SP_VIEW_STATUS = 3, + SP_VIEW_RELATIONS = 4 +}; + +@interface SPHistoryController : NSObject { + IBOutlet TableDocument *theDocument; + IBOutlet NSSegmentedControl *historyControl; + + NSMutableArray *history; + unsigned int historyPosition; + BOOL restoringHistoryState; +} + +// Interface interaction +- (void) updateToolbarItem; +- (IBAction) historyControlClicked:(NSSegmentedControl *)theControl; +- (unsigned int) currentlySelectedView; + +// Adding or updating history entries +- (void) updateHistoryEntries; + +// Loading history entries +- (void) loadEntryAtPosition:(unsigned int)position; +- (void) abortEntryLoad; + +@end diff --git a/Source/SPHistoryController.m b/Source/SPHistoryController.m new file mode 100644 index 00000000..fc6e03b9 --- /dev/null +++ b/Source/SPHistoryController.m @@ -0,0 +1,256 @@ +// +// $Id: SPFieldEditorController.h 802 2009-06-03 20:46:57Z bibiko $ +// +// SPHistoryController.h +// sequel-pro +// +// Created by Rowan Beentje on July 23, 2009 +// +// 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 "TablesList.h" +#import "SPHistoryController.h" + + +@implementation SPHistoryController + +#pragma mark Setup and teardown + +/** + * Initialise by creating a blank history array + */ +- (id) init +{ + if (self = [super init]) { + history = [[NSMutableArray alloc] init]; + historyPosition = NSNotFound; + restoringHistoryState = NO; + } + return self; +} + +- (void) dealloc +{ + [history release]; + [super dealloc]; +} + +#pragma mark - +#pragma mark Interface interaction + +/** + * Updates the toolbar item to reflect the current history state and position + */ +- (void) updateToolbarItem +{ + BOOL backEnabled = NO; + BOOL forwardEnabled = NO; + + // Set the active state of the segments if appropriate + if ([history count] && historyPosition > 0) backEnabled = YES; + if ([history count] && historyPosition + 1 < [history count]) forwardEnabled = YES; + + [historyControl setEnabled:backEnabled forSegment:0]; + [historyControl setEnabled:forwardEnabled forSegment:1]; +} + +/** + * Trigger a navigation action in response to a click + */ +- (IBAction) historyControlClicked:(NSSegmentedControl *)theControl +{ + switch ([theControl selectedSegment]) { + + // Back button clicked: + case 0: + if (historyPosition == NSNotFound || !historyPosition) return; + [self loadEntryAtPosition:historyPosition - 1]; + break; + + // Forward button clicked: + case 1: + if (historyPosition == NSNotFound || historyPosition + 1 >= [history count]) return; + [self loadEntryAtPosition:historyPosition + 1]; + break; + } +} + +/** + * Retrieve the view that is currently selected from the database + */ +- (unsigned int) currentlySelectedView +{ + unsigned int theView = NSNotFound; + + NSString *viewName = [[[theDocument valueForKey:@"tableTabView"] selectedTabViewItem] identifier]; + if ([viewName isEqualToString:@"source"]) { + theView = SP_VIEW_STRUCTURE; + } else if ([viewName isEqualToString:@"content"]) { + theView = SP_VIEW_CONTENT; + } else if ([viewName isEqualToString:@"customQuery"]) { + theView = SP_VIEW_CUSTOMQUERY; + } else if ([viewName isEqualToString:@"status"]) { + theView = SP_VIEW_STATUS; + } else if ([viewName isEqualToString:@"relations"]) { + theView = SP_VIEW_RELATIONS; + } + + return theView; +} + +#pragma mark - +#pragma mark Adding or updating history entries + +/** + * Call to store or update a history item for the document state. Checks against + * the latest stored details; if they match, a new history item is not created. + * This should therefore be called without worry of duplicates. + */ +- (void) updateHistoryEntries +{ + + // Don't modify anything if we're in the process of restoring an old history state + if (restoringHistoryState) return; + + // Work out the current document details + NSString *theDatabase = [theDocument database]; + NSString *theTable = [theDocument table]; + unsigned int theView = [self currentlySelectedView]; + + // Check for a duplicate against the current entry + if (historyPosition != NSNotFound) { + NSDictionary *currentHistoryItem = [history objectAtIndex:historyPosition]; + if ([[currentHistoryItem objectForKey:@"database"] isEqualToString:theDatabase] + && [[currentHistoryItem objectForKey:@"table"] isEqualToString:theTable] + && [[currentHistoryItem objectForKey:@"view"] intValue] == theView) + { + return; + } + } + + // If there's any items after the current history position, remove them + if (historyPosition != NSNotFound && historyPosition < [history count] - 1) { + [history removeObjectsInRange:NSMakeRange(historyPosition + 1, [history count] - historyPosition - 1)]; + + // Special case: if the last history item is currently active, and has no table, + // but the new selection does - delete the last entry, in order to replace it. + // This improves history flow. + } else if (historyPosition != NSNotFound && historyPosition == [history count] - 1 + && [[[history objectAtIndex:historyPosition] objectForKey:@"database"] isEqualToString:theDatabase] + && ![[history objectAtIndex:historyPosition] objectForKey:@"table"]) + { + [history removeLastObject]; + } + + // Construct and add the new history entry + NSMutableDictionary *newEntry = [NSMutableDictionary dictionaryWithObjectsAndKeys: + theDatabase, @"database", + theTable, @"table", + [NSNumber numberWithInt:theView], @"view", + nil]; + [history addObject:newEntry]; + historyPosition = [history count] - 1; + [self updateToolbarItem]; +} + +#pragma mark - +#pragma mark Loading history entries + +/** + * Load a history entry and attempt to return the interface to that state. + */ +- (void) loadEntryAtPosition:(unsigned int)position +{ + + // Sanity check the input + if (position == NSNotFound || position < 0 || position >= [history count]) { + NSBeep(); + return; + } + + restoringHistoryState = YES; + + // Update the position and extract the history entry + historyPosition = position; + NSDictionary *historyEntry = [history objectAtIndex:historyPosition]; + + // Check and set the database + if (![[theDocument database] isEqualToString:[historyEntry objectForKey:@"database"]]) { + NSPopUpButton *chooseDatabaseButton = [theDocument valueForKey:@"chooseDatabaseButton"]; + [chooseDatabaseButton selectItemWithTitle:[historyEntry objectForKey:@"database"]]; + [theDocument chooseDatabase:self]; + if (![[theDocument database] isEqualToString:[historyEntry objectForKey:@"database"]]) { + return [self abortEntryLoad]; + } + } + + // Check and set the table + if ([historyEntry objectForKey:@"table"] && ![[theDocument table] isEqualToString:[historyEntry objectForKey:@"table"]]) { + TablesList *tablesListInstance = [theDocument valueForKey:@"tablesListInstance"]; + NSArray *tables = [tablesListInstance tables]; + if ([tables indexOfObject:[historyEntry objectForKey:@"table"]] == NSNotFound) { + return [self abortEntryLoad]; + } + [[tablesListInstance valueForKey:@"tablesListView"] selectRowIndexes:[NSIndexSet indexSetWithIndex:[tables indexOfObject:[historyEntry objectForKey:@"table"]]] byExtendingSelection:NO]; + if (![[theDocument table] isEqualToString:[historyEntry objectForKey:@"table"]]) { + return [self abortEntryLoad]; + } + } else if (![historyEntry objectForKey:@"table"] && [theDocument table]) { + TablesList *tablesListInstance = [theDocument valueForKey:@"tablesListInstance"]; + [[tablesListInstance valueForKey:@"tablesListView"] deselectAll:self]; + } + + // Check and set the view + if ([self currentlySelectedView] != [[historyEntry objectForKey:@"view"] intValue]) { + switch ([[historyEntry objectForKey:@"view"] intValue]) { + case SP_VIEW_STRUCTURE: + [theDocument viewStructure:self]; + break; + case SP_VIEW_CONTENT: + [theDocument viewContent:self]; + break; + case SP_VIEW_CUSTOMQUERY: + [theDocument viewQuery:self]; + break; + case SP_VIEW_STATUS: + [theDocument viewStatus:self]; + break; + case SP_VIEW_RELATIONS: + [theDocument viewRelations:self]; + break; + } + if ([self currentlySelectedView] != [[historyEntry objectForKey:@"view"] intValue]) { + return [self abortEntryLoad]; + } + } + + restoringHistoryState = NO; + [self updateToolbarItem]; +} + +/** + * Convenience method for aborting history load - could at some point + * clean up the history list, show an alert, etc + */ +- (void) abortEntryLoad +{ + NSBeep(); + restoringHistoryState = NO; +} + +@end diff --git a/Source/TableDocument.h b/Source/TableDocument.h index 5cd9dc6c..97da96f9 100644 --- a/Source/TableDocument.h +++ b/Source/TableDocument.h @@ -46,6 +46,7 @@ IBOutlet id tableDataInstance; IBOutlet id extendedTableInfoInstance; IBOutlet id databaseDataInstance; + IBOutlet id spHistoryControllerInstance; IBOutlet id spExportControllerInstance; IBOutlet id tableWindow; @@ -64,6 +65,7 @@ IBOutlet id databaseEncodingButton; IBOutlet id addDatabaseButton; IBOutlet id chooseDatabaseButton; + IBOutlet id historyControl; IBOutlet id variablesTableView; IBOutlet NSTabView *tableTabView; diff --git a/Source/TableDocument.m b/Source/TableDocument.m index 00c31a15..76e0337b 100644 --- a/Source/TableDocument.m +++ b/Source/TableDocument.m @@ -43,6 +43,7 @@ #import "MainController.h" #import "SPExtendedTableInfo.h" #import "SPConnectionController.h" +#import "SPHistoryController.h" #import "SPPreferenceController.h" #import "SPPrintAccessory.h" #import "QLPreviewPanel.h" @@ -168,6 +169,7 @@ if ([connectionController database] && ![[connectionController database] isEqualToString:@""]) { if (selectedDatabase) [selectedDatabase release], selectedDatabase = nil; selectedDatabase = [[NSString alloc] initWithString:[connectionController database]]; + [spHistoryControllerInstance updateHistoryEntries]; } // Update the database list @@ -416,6 +418,9 @@ [tablesListInstance setConnection:mySQLConnection]; [tableDumpInstance setConnection:mySQLConnection]; [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@", mySQLVersion, [self name], [self database]]]; + + // Add a history entry + [spHistoryControllerInstance updateHistoryEntries]; } /** @@ -1550,6 +1555,7 @@ [tableTabView selectTabViewItemAtIndex:0]; [mainToolbar setSelectedItemIdentifier:@"SwitchToTableStructureToolbarItemIdentifier"]; + [spHistoryControllerInstance updateHistoryEntries]; } - (IBAction)viewContent:(id)sender @@ -1563,6 +1569,7 @@ [tableTabView selectTabViewItemAtIndex:1]; [mainToolbar setSelectedItemIdentifier:@"SwitchToTableContentToolbarItemIdentifier"]; + [spHistoryControllerInstance updateHistoryEntries]; } - (IBAction)viewQuery:(id)sender @@ -1583,6 +1590,7 @@ [tableTabView selectTabViewItemAtIndex:2]; [mainToolbar setSelectedItemIdentifier:@"SwitchToRunQueryToolbarItemIdentifier"]; + [spHistoryControllerInstance updateHistoryEntries]; // Set the focus on the text field if no query has been run if (![[customQueryTextView string] length]) [tableWindow makeFirstResponder:customQueryTextView]; @@ -1606,6 +1614,7 @@ [tableTabView selectTabViewItemAtIndex:3]; [mainToolbar setSelectedItemIdentifier:@"SwitchToTableInfoToolbarItemIdentifier"]; + [spHistoryControllerInstance updateHistoryEntries]; } - (IBAction)viewRelations:(id)sender @@ -1626,6 +1635,7 @@ [tableTabView selectTabViewItemAtIndex:4]; [mainToolbar setSelectedItemIdentifier:@"SwitchToTableRelationsToolbarItemIdentifier"]; + [spHistoryControllerInstance updateHistoryEntries]; } @@ -1751,7 +1761,12 @@ chooseDatabaseToolbarItem = toolbarItem; [self updateChooseDatabaseToolbarItemWidth]; } - + + } else if ([itemIdentifier isEqualToString:@"HistoryNavigationToolbarItemIdentifier"]) { + [toolbarItem setLabel:NSLocalizedString(@"History", @"toolbar item for navigation history")]; + [toolbarItem setPaletteLabel:[toolbarItem label]]; + [toolbarItem setView:historyControl]; + } 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")]; @@ -1847,6 +1862,7 @@ { return [NSArray arrayWithObjects: @"DatabaseSelectToolbarItemIdentifier", + @"HistoryNavigationToolbarItemIdentifier", @"ToggleConsoleIdentifier", @"ClearConsoleIdentifier", @"FlushPrivilegesIdentifier", diff --git a/Source/TablesList.h b/Source/TablesList.h index 68578010..250f451f 100644 --- a/Source/TablesList.h +++ b/Source/TablesList.h @@ -25,6 +25,7 @@ #import <Cocoa/Cocoa.h> #import <MCPKit/MCPKit.h> +#import "SPHistoryController.h" enum sp_table_types { @@ -54,6 +55,7 @@ enum sp_table_types IBOutlet id tableDataInstance; IBOutlet id extendedTableInfoInstance; IBOutlet id databaseDataInstance; + IBOutlet SPHistoryController *spHistoryControllerInstance; IBOutlet id tableWindow; IBOutlet id copyTableSheet; diff --git a/Source/TablesList.m b/Source/TablesList.m index e5f4a35d..6d611306 100644 --- a/Source/TablesList.m +++ b/Source/TablesList.m @@ -1297,6 +1297,8 @@ [tableWindow setTitle:[NSString stringWithFormat:@"(MySQL %@) %@/%@", [tableDocumentInstance mySQLVersion], [tableDocumentInstance name], [tableDocumentInstance database]]]; } + + [spHistoryControllerInstance updateHistoryEntries]; } #pragma mark - |