From 9e72b900075cb73e32254754faea71882f1b8299 Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Thu, 30 Jul 2009 00:20:52 +0000 Subject: - Add an initial barebones version of the history controller, with a basic toolbar item to navigate backwards and forwards. TBC! --- Source/SPHistoryController.m | 256 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 Source/SPHistoryController.m (limited to 'Source/SPHistoryController.m') 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 + +#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 -- cgit v1.2.3