aboutsummaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBibiko <bibiko@eva.mpg.de>2009-09-29 09:07:20 +0000
committerBibiko <bibiko@eva.mpg.de>2009-09-29 09:07:20 +0000
commitf6184afd58588a6687fd2312d9db42a83be0e286 (patch)
tree9c0a335c978e00c4267d4c19585bd06dd8a10f8f /Source
parent03783658ec16339b87d9005838d6ac8263871853 (diff)
downloadsequelpro-f6184afd58588a6687fd2312d9db42a83be0e286.tar.gz
sequelpro-f6184afd58588a6687fd2312d9db42a83be0e286.tar.bz2
sequelpro-f6184afd58588a6687fd2312d9db42a83be0e286.zip
• preparation for GUI support to manage user-defined content filters
Diffstat (limited to 'Source')
-rw-r--r--Source/SPContentFilterManager.h69
-rw-r--r--Source/SPContentFilterManager.m832
2 files changed, 901 insertions, 0 deletions
diff --git a/Source/SPContentFilterManager.h b/Source/SPContentFilterManager.h
new file mode 100644
index 00000000..c0b22cd9
--- /dev/null
+++ b/Source/SPContentFilterManager.h
@@ -0,0 +1,69 @@
+//
+// $Id$
+//
+// SPContentFilterManager.h
+// sequel-pro
+//
+// Created by Stuart Connolly (stuconnolly.com) on Aug 23, 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>
+
+@interface NSObject (SPContentFilterManagerDelegate)
+
+- (void)queryFavoritesHaveBeenUpdated:(id)manager;
+
+@end
+
+@interface SPContentFilterManager : NSWindowController
+{
+ NSUserDefaults *prefs;
+
+ NSURL *delegatesFileURL;
+ IBOutlet NSPopUpButton *encodingPopUp;
+ IBOutlet NSTableView *favoritesTableView;
+ IBOutlet NSTextField *favoriteNameTextField;
+ IBOutlet NSTextView *favoriteQueryTextView;
+ IBOutlet id removeButton;
+
+ IBOutlet NSArrayController *favoritesArrayController;
+
+ NSMutableArray *favorites;
+
+ BOOL isTableCellEditing;
+}
+
+- (id)initWithDelegate:(id)managerDelegate;
+
+// Accessors
+- (NSMutableArray *)queryFavoritesForFileURL:(NSURL *)fileURL;
+- (id)customQueryInstance;
+
+// IBAction methods
+- (IBAction)addQueryFavorite:(id)sender;
+- (IBAction)removeQueryFavorite:(id)sender;
+- (IBAction)removeAllQueryFavorites:(id)sender;
+- (IBAction)duplicateQueryFavorite:(id)sender;
+- (IBAction)saveFavoriteToFile:(id)sender;
+- (IBAction)exportFavorites:(id)sender;
+- (IBAction)importFavoritesByAdding:(id)sender;
+- (IBAction)importFavoritesByReplacing:(id)sender;
+- (IBAction)closeQueryManagerSheet:(id)sender;
+
+@end
diff --git a/Source/SPContentFilterManager.m b/Source/SPContentFilterManager.m
new file mode 100644
index 00000000..46726bb8
--- /dev/null
+++ b/Source/SPContentFilterManager.m
@@ -0,0 +1,832 @@
+//
+// $Id$
+//
+// SPContentFilterManager.m
+// sequel-pro
+//
+// Created by Stuart Connolly (stuconnolly.com) on Aug 23, 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 "SPContentFilterManager.h"
+#import "ImageAndTextCell.h"
+#import "SPEncodingPopupAccessory.h"
+#import "SPQueryController.h"
+
+#define DEFAULT_QUERY_FAVORITE_FILE_EXTENSION @"sql"
+#define DEFAULT_SEQUELPRO_FILE_EXTENSION @"spf"
+
+#define SP_MULTIPLE_SELECTION_PLACEHOLDER_STRING NSLocalizedString(@"[multiple selection]", @"[multiple selection]")
+#define SP_NO_SELECTION_PLACEHOLDER_STRING NSLocalizedString(@"[no selection]", @"[no selection]")
+
+#define QUERY_FAVORITES_PB_DRAG_TYPE @"SequelProQueryFavoritesPasteboard"
+
+@interface SPContentFilterManager (Private)
+- (void)_initWithNoSelection;
+@end
+
+@implementation SPContentFilterManager
+
+/**
+ * Initialize the manager with the supplied delegate
+ */
+- (id)initWithDelegate:(id)managerDelegate
+{
+ if ((self = [super initWithWindowNibName:@"QueryFavoriteManager"])) {
+
+ prefs = [NSUserDefaults standardUserDefaults];
+
+ favorites = [[NSMutableArray alloc] init];
+
+ if(managerDelegate == nil) {
+ NSBeep();
+ NSLog(@"Query Favorite Manger was called without a delegate.");
+ return nil;
+ }
+ delegatesFileURL = [[managerDelegate valueForKeyPath:@"tableDocumentInstance"] fileURL];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [favorites release];
+ [super dealloc];
+}
+
+/**
+ * Upon awakening bind the query text view's background colour.
+ */
+- (void)awakeFromNib
+{
+
+ [favoriteQueryTextView setAllowsDocumentBackgroundColorChange:YES];
+
+ NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary];
+
+ [bindingOptions setObject:NSUnarchiveFromDataTransformerName forKey:@"NSValueTransformerName"];
+
+ [favoriteQueryTextView bind:@"backgroundColor"
+ toObject:[NSUserDefaultsController sharedUserDefaultsController]
+ withKeyPath:@"values.CustomQueryEditorBackgroundColor"
+ options:bindingOptions];
+
+
+ [favorites addObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ @"Global", @"name",
+ @"", @"headerOfFileURL",
+ @"", @"query",
+ nil]];
+
+ // Build data source for global queryFavorites (as mutable copy! otherwise each
+ // change will be stored in the prefs at once)
+ if([prefs objectForKey:@"queryFavorites"]) {
+ for(id fav in [prefs objectForKey:@"queryFavorites"])
+ [favorites addObject:[[fav mutableCopy] autorelease]];
+ }
+
+ [favorites addObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ [[[delegatesFileURL absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] lastPathComponent], @"name",
+ [delegatesFileURL absoluteString], @"headerOfFileURL",
+ @"", @"query",
+ nil]];
+
+ if([[SPQueryController sharedQueryController] favoritesForFileURL:delegatesFileURL]) {
+ for(id fav in [[SPQueryController sharedQueryController] favoritesForFileURL:delegatesFileURL])
+ [favorites addObject:[[fav mutableCopy] autorelease]];
+ }
+
+
+ // Select the first query if any
+ NSUInteger i = 0;
+ for(i=0; i < [favorites count]; i++ )
+ if(![[favorites objectAtIndex:i] objectForKey:@"headerOfFileURL"])
+ break;
+
+ [[self window] makeFirstResponder:favoritesTableView];
+ [self _initWithNoSelection];
+
+ // Register drag types
+ [favoritesTableView registerForDraggedTypes:[NSArray arrayWithObject:QUERY_FAVORITES_PB_DRAG_TYPE]];
+
+ [favoritesArrayController setContent:favorites];
+ [favoritesTableView reloadData];
+
+ // Set Remove button state
+ [removeButton setEnabled:([favoritesTableView numberOfSelectedRows] > 0)];
+
+}
+
+#pragma mark -
+#pragma mark Accessor methods
+
+/**
+ * Returns the query favorites array for fileURL.
+ * fileURL == nil → global favorites
+ */
+- (NSMutableArray *)queryFavoritesForFileURL:(NSURL *)fileURL
+{
+ NSMutableArray *favs = [NSMutableArray array];
+ NSString *fileURLstring;
+
+ if(fileURL == nil)
+ fileURLstring = @"";
+ else
+ fileURLstring = [fileURL absoluteString];
+
+ NSUInteger i = 0;
+
+ // Look for the header specified by fileURL
+ while(i<[favorites count]) {
+ if ([[favorites objectAtIndex:i] objectForKey:@"headerOfFileURL"]
+ && [[[favorites objectAtIndex:i] objectForKey:@"headerOfFileURL"] isEqualToString:fileURLstring]) {
+ i++;
+ break;
+ }
+ i++;
+ }
+
+ // Take all favorites until the next header or end of favorites
+ for(i; i<[favorites count]; i++) {
+
+ if(![[favorites objectAtIndex:i] objectForKey:@"headerOfFileURL"])
+ [favs addObject:[favorites objectAtIndex:i]];
+ else
+ break;
+
+ }
+
+ return favs;
+}
+
+/**
+ * This method is only implemented to be compatible with CMTextView.
+ */
+- (id)customQueryInstance
+{
+ return [[[NSApp mainWindow] delegate] valueForKey:@"customQueryInstance"];
+}
+
+#pragma mark -
+#pragma mark IBAction methods
+
+/**
+ * Adds/Inserts a query favorite
+ */
+- (IBAction)addQueryFavorite:(id)sender
+{
+
+ NSMutableDictionary *favorite;
+ NSUInteger insertIndex;
+
+ // Duplicate a selected favorite if sender == self
+ if(sender == self)
+ favorite = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[[favoriteNameTextField stringValue] stringByAppendingFormat:@" Copy"], [favoriteQueryTextView string], nil] forKeys:[NSArray arrayWithObjects:@"name", @"query", nil]];
+ // Add a new favorite
+ else
+ favorite = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"New Favorite", @"", nil] forKeys:[NSArray arrayWithObjects:@"name", @"query", nil]];
+
+ if([favoritesTableView numberOfSelectedRows] > 0) {
+ insertIndex = [[favoritesTableView selectedRowIndexes] lastIndex]+1;
+ [favorites insertObject:favorite atIndex:insertIndex];
+ } else {
+ [favorites addObject:favorite];
+ insertIndex = [favorites count] - 1;
+ }
+
+ [favoritesArrayController rearrangeObjects];
+ [favoritesTableView reloadData];
+
+ [favoritesTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:insertIndex] byExtendingSelection:NO];
+
+ [favoritesTableView scrollRowToVisible:[favoritesTableView selectedRow]];
+
+ [removeButton setEnabled:([favoritesTableView numberOfSelectedRows] > 0)];
+ [[self window] makeFirstResponder:favoriteNameTextField];
+
+}
+
+/**
+ * Duplicates a query favorite
+ */
+- (IBAction)duplicateQueryFavorite:(id)sender
+{
+ if ([favoritesTableView numberOfSelectedRows] == 1)
+ [self addQueryFavorite:self];
+ else
+ NSBeep();
+}
+
+/**
+ * Removes a query favorite
+ */
+- (IBAction)removeQueryFavorite:(id)sender
+{
+ NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Remove selected query favorites?", @"remove selected query favorites message")
+ defaultButton:NSLocalizedString(@"Cancel", @"cancel button")
+ alternateButton:NSLocalizedString(@"Remove", @"remove button")
+ otherButton:nil
+ informativeTextWithFormat:NSLocalizedString(@"Are you sure you want to remove all selected query favorites? This action cannot be undone.", @"remove all selected query favorites informative message")];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+
+ NSArray *buttons = [alert buttons];
+
+ // Change the alert's cancel button to have the key equivalent of return
+ [[buttons objectAtIndex:0] setKeyEquivalent:@"\r"];
+ [[buttons objectAtIndex:1] setKeyEquivalent:@""];
+
+ [alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removeSelectedFavorites"];
+}
+
+/**
+ * Removes all query favorites
+ */
+- (IBAction)removeAllQueryFavorites:(id)sender
+{
+ NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Remove all query favorites?", @"remove all query favorites message")
+ defaultButton:NSLocalizedString(@"Cancel", @"cancel button")
+ alternateButton:NSLocalizedString(@"Remove All", @"remove all button")
+ otherButton:nil
+ informativeTextWithFormat:NSLocalizedString(@"Are you sure you want to remove all of your saved query favorites? This action cannot be undone.", @"remove all query favorites informative message")];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+
+ NSArray *buttons = [alert buttons];
+
+ // Change the alert's cancel button to have the key equivalent of return
+ [[buttons objectAtIndex:0] setKeyEquivalent:@"\r"];
+ [[buttons objectAtIndex:1] setKeyEquivalent:@""];
+
+ [alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removeAllFavorites"];
+}
+
+/**
+ * Saves the currently selected query favorite to a user specified file.
+ */
+- (IBAction)saveFavoriteToFile:(id)sender
+{
+ NSSavePanel *panel = [NSSavePanel savePanel];
+
+ [panel setRequiredFileType:DEFAULT_QUERY_FAVORITE_FILE_EXTENSION];
+
+ [panel setExtensionHidden:NO];
+ [panel setAllowsOtherFileTypes:YES];
+ [panel setCanSelectHiddenExtension:YES];
+ [panel setCanCreateDirectories:YES];
+
+ [panel setAccessoryView:[SPEncodingPopupAccessory encodingAccessory:[prefs integerForKey:@"lastSqlFileEncoding"] includeDefaultEntry:NO encodingPopUp:&encodingPopUp]];
+
+ [encodingPopUp setEnabled:YES];
+
+ [panel beginSheetForDirectory:nil file:[favoriteNameTextField stringValue] modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:@"saveQuery"];
+}
+
+- (IBAction)exportFavorites:(id)sender
+{
+ NSSavePanel *panel = [NSSavePanel savePanel];
+
+ [panel setRequiredFileType:DEFAULT_SEQUELPRO_FILE_EXTENSION];
+
+ [panel setExtensionHidden:NO];
+ [panel setAllowsOtherFileTypes:NO];
+ [panel setCanSelectHiddenExtension:YES];
+ [panel setCanCreateDirectories:YES];
+
+ [panel beginSheetForDirectory:nil file:nil modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:@"exportFavorites"];
+}
+
+- (IBAction)importFavoritesByAdding:(id)sender
+{
+ NSOpenPanel *panel = [NSOpenPanel openPanel];
+ [panel setCanSelectHiddenExtension:YES];
+ [panel setDelegate:self];
+ [panel setCanChooseDirectories:NO];
+ [panel setAllowsMultipleSelection:NO];
+ [panel setResolvesAliases:YES];
+
+ [panel beginSheetForDirectory:nil
+ file:@""
+ types:[NSArray arrayWithObjects:@"spf", @"sql", nil]
+ modalForWindow:[self window]
+ modalDelegate:self
+ didEndSelector:@selector(importPanelDidEnd:returnCode:contextInfo:)
+ contextInfo:NULL];
+}
+
+- (IBAction)importFavoritesByReplacing:(id)sender
+{
+
+}
+
+/**
+ * Closes the query favorite manager
+ */
+- (IBAction)closeQueryManagerSheet:(id)sender
+{
+
+ // First check for ESC if pressed while inline editing
+ if(![sender tag] && isTableCellEditing) {
+ [favoritesTableView abortEditing];
+ isTableCellEditing = NO;
+ return;
+ }
+
+ [NSApp endSheet:[self window] returnCode:0];
+ [[self window] orderOut:self];
+
+ // "Apply Changes" button was pressed
+ if([sender tag]) {
+
+ // Ensure that last changes will be written back
+ // if only one favorite is selected; otherwise unstable state
+ if ([favoritesTableView numberOfSelectedRows] == 1) {
+ [[self window] makeFirstResponder:favoritesTableView];
+ }
+
+ // Update current document's query favorites in the SPQueryController
+ [[SPQueryController sharedQueryController] replaceFavoritesByArray:
+ [self queryFavoritesForFileURL:delegatesFileURL] forFileURL:delegatesFileURL];
+
+ // Update global preferences' list
+ [prefs setObject:[self queryFavoritesForFileURL:nil] forKey:@"queryFavorites"];
+
+ // Inform all opened documents to update the query favorites list
+ for(id doc in [[NSDocumentController sharedDocumentController] documents])
+ if([[doc valueForKeyPath:@"customQueryInstance"] respondsToSelector:@selector(queryFavoritesHaveBeenUpdated:)])
+ [[doc valueForKeyPath:@"customQueryInstance"] queryFavoritesHaveBeenUpdated:self];
+
+
+ }
+
+}
+
+#pragma mark -
+#pragma mark SplitView delegate methods
+
+/**
+ * Return the maximum possible size of the splitview.
+ */
+- (float)splitView:(NSSplitView *)sender constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(int)offset
+{
+ return (proposedMax - 220);
+}
+
+/**
+ * Return the minimum possible size of the splitview.
+ */
+- (float)splitView:(NSSplitView *)sender constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)offset
+{
+ return (proposedMin + 120);
+}
+
+#pragma mark -
+#pragma mark TableView datasource methods
+
+/**
+ * Returns the number of query favorites.
+ */
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
+{
+ return [favorites count];
+}
+
+/**
+ * Returns the value for the requested table column and row index.
+ */
+- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
+{
+ if(![[favorites objectAtIndex:rowIndex] objectForKey:[aTableColumn identifier]]) return @"";
+
+ return [[favorites objectAtIndex:rowIndex] objectForKey:[aTableColumn identifier]];
+}
+
+/*
+ * Save favorite names if inline edited (suppress empty names)
+ */
+- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
+{
+
+ if([[aTableColumn identifier] isEqualToString:@"name"] && [anObject length]) {
+ [[favorites objectAtIndex:rowIndex] setObject:[anObject description] forKey:@"name"];
+ // [[favorites objectAtIndex:rowIndex] setObject:[favoriteQueryTextView string] forKey:@"query"];
+ [favoriteNameTextField setStringValue:[anObject description]];
+ }
+
+ [favoritesTableView reloadData];
+}
+
+/*
+ * Before selecting an other favorite save pending query string changes
+ * and make sure that no group table item can be selected
+ */
+- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
+{
+ return ([[favorites objectAtIndex:rowIndex] objectForKey:@"headerOfFileURL"]) ? NO : YES;
+}
+
+/*
+ * Set indention levels for headers and favorites
+ * (maybe in the future use an image for headers for expanding and collapsing)
+ */
+- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
+{
+ if([[favorites objectAtIndex:rowIndex] objectForKey:@"headerOfFileURL"] && [[aTableColumn identifier] isEqualToString:@"name"]) {
+ // if([[[favoriteProperties objectAtIndex:rowIndex] objectForKey:@"isGroup"] isEqualToString:@"1"])
+ // [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"NSRightFacingTriangleTemplate"]];
+ // else
+ // [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"NSLeftFacingTriangleTemplate"]];
+ [(ImageAndTextCell*)aCell setIndentationLevel:0];
+ }
+ else if(![[favorites objectAtIndex:rowIndex] objectForKey:@"headerOfFileURL"] && [[aTableColumn identifier] isEqualToString:@"name"]) {
+ // [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"dummy-small"]];
+ [(ImageAndTextCell*)aCell setIndentationLevel:1];
+ }
+}
+
+/*
+ * A row of an header return is slighlty larger
+ */
+- (CGFloat)tableView:(NSTableView *)aTableView heightOfRow:(NSInteger)rowIndex
+{
+ return ([[favorites objectAtIndex:rowIndex] objectForKey:@"headerOfFileURL"]) ? 20 : 18;
+}
+
+/*
+ * Only favorite name can be edited inline
+ */
+- (BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
+{
+ if([[favorites objectAtIndex:rowIndex] objectForKey:@"headerOfFileURL"]) {
+ return NO;
+ } else {
+ isTableCellEditing = YES;
+ return YES;
+ }
+}
+
+/*
+ * Sorting by clicking at a column header inside groups
+ */
+- (void)tableView:(NSTableView*)tableView didClickTableColumn:(NSTableColumn *)tableColumn
+{
+ // TODO: Not yet implemented
+ return;
+}
+
+/*
+ * favoriteProperties holds the data if a table row is a group header or not
+ */
+- (BOOL)tableView:(NSTableView *)aTableView isGroupRow:(NSInteger)rowIndex
+{
+ return ([[favorites objectAtIndex:rowIndex] objectForKey:@"headerOfFileURL"]) ? YES : NO;
+}
+/*
+ * Detect if inline editing was done - then ESC to close the sheet will be activate
+ */
+- (void)controlTextDidEndEditing:(NSNotification *)aNotification
+{
+ isTableCellEditing = NO;
+}
+
+/*
+ * Changes in the name text field will be saved in data source directly
+ * to update the table view accordingly
+ */
+- (void)controlTextDidChange:(NSNotification *)notification
+{
+
+ // Do nothing if no favorite is selected
+ if([favoritesTableView numberOfSelectedRows] < 1) return;
+
+ id object = [notification object];
+
+ if(object == favoriteNameTextField) {
+ [[favorites objectAtIndex:[favoritesTableView selectedRow]] setObject:[favoriteNameTextField stringValue] forKey:@"name"];
+ [favoritesTableView reloadData];
+ }
+
+}
+
+#pragma mark -
+#pragma mark Menu validation
+
+/**
+ * Menu item validation.
+ */
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+
+ // Disable all if only GLOBAL is in the table
+ if([favorites count] < 2) return NO;
+
+ SEL action = [menuItem action];
+
+ if ( (action == @selector(duplicateQueryFavorite:)) ||
+ (action == @selector(saveFavoriteToFile:)))
+ {
+ return ([favoritesTableView numberOfSelectedRows] == 1);
+ }
+ else if ( (action == @selector(removeQueryFavorite:)) ||
+ ( action == @selector(exportFavorites:)))
+ {
+ return ([favoritesTableView numberOfSelectedRows] > 0);
+ }
+ else if (action == @selector(removeAllQueryFavorites:)) {
+ return ([favorites count] > 0);
+ }
+
+ return YES;
+}
+
+#pragma mark -
+#pragma mark TableView drag & drop delegate methods
+
+/**
+ * Return whether or not the supplied rows can be written.
+ */
+- (BOOL)tableView:(NSTableView *)tableView writeRows:(NSArray *)rows toPasteboard:(NSPasteboard *)pboard
+{
+
+ // Up to now only one row can be dragged
+ // if ([rows count] == 1) {
+
+ NSArray *pboardTypes = [NSArray arrayWithObject:QUERY_FAVORITES_PB_DRAG_TYPE];
+ NSInteger originalRow = [[rows objectAtIndex:0] intValue];
+
+ if(originalRow < 1) return NO;
+
+ // Do not drag headers
+ if([[favorites objectAtIndex:originalRow] objectForKey:@"headerOfFileURL"]) return NO;
+
+ [pboard declareTypes:pboardTypes owner:nil];
+
+ NSMutableData *indexdata = [[[NSMutableData alloc] init] autorelease];
+ NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:indexdata] autorelease];
+ [archiver encodeObject:rows forKey:@"indexdata"];
+ [archiver finishEncoding];
+ [pboard setData:indexdata forType:QUERY_FAVORITES_PB_DRAG_TYPE];
+
+ return YES;
+
+ // }
+
+ // return NO;
+}
+
+/**
+ * Validate the proposed drop of the supplied rows.
+ */
+- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation
+{
+ NSArray *pboardTypes = [[info draggingPasteboard] types];
+
+ if (([pboardTypes count] > 1) && (row != -1)) {
+ if (([pboardTypes containsObject:QUERY_FAVORITES_PB_DRAG_TYPE]) && (operation == NSTableViewDropAbove)) {
+ if (row > 0) {
+ return NSDragOperationMove;
+ }
+ }
+ }
+
+ return NSDragOperationNone;
+}
+
+/**
+ * Return whether or not to accept the drop of the supplied rows.
+ */
+
+- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation
+{
+
+ if(row < 1) return NO;
+
+ NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:[[info draggingPasteboard] dataForType:QUERY_FAVORITES_PB_DRAG_TYPE]] autorelease];
+ NSArray *draggedRows = [NSArray arrayWithArray:(NSArray *)[unarchiver decodeObjectForKey:@"indexdata"]];
+ [unarchiver finishDecoding];
+
+ NSInteger destinationRow = row;
+ NSInteger offset = 0;
+
+ NSUInteger i;
+
+ for(i=0; i<[draggedRows count]; i++) {
+
+ NSInteger originalRow = [[draggedRows objectAtIndex:i] intValue];
+
+ if(originalRow < destinationRow) destinationRow--;
+
+ originalRow += offset;
+
+ // For safety reasons
+ if(originalRow > [favorites count]-1) originalRow = [favorites count] - 1;
+
+ NSMutableDictionary *draggedRow = [NSMutableDictionary dictionaryWithDictionary:[favorites objectAtIndex:originalRow]];
+ [favorites removeObjectAtIndex:originalRow];
+ [favoritesTableView reloadData];
+
+ if(destinationRow+i >= [favorites count])
+ [favorites addObject:draggedRow];
+ else
+ [favorites insertObject:draggedRow atIndex:destinationRow+i];
+
+ if(originalRow < row) offset--;
+
+ }
+
+ [favoritesTableView reloadData];
+ [favoritesArrayController rearrangeObjects];
+
+ return YES;
+}
+
+#pragma mark -
+#pragma mark Other
+
+/**
+ * Sheet did end method
+ */
+- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo
+{
+ // Is disabled - do we need that?
+ // if ([contextInfo isEqualToString:@"removeAllFavorites"]) {
+ // if (returnCode == NSAlertAlternateReturn) {
+ // [favorites removeObjects:[queryFavoritesController arrangedObjects]];
+ // }
+ // }
+ if([contextInfo isEqualToString:@"removeSelectedFavorites"]) {
+ if (returnCode == NSAlertAlternateReturn) {
+ NSIndexSet *indexes = [favoritesTableView selectedRowIndexes];
+
+ // get last index
+ NSUInteger currentIndex = [indexes lastIndex];
+
+ while (currentIndex != NSNotFound) {
+ [favorites removeObjectAtIndex:currentIndex];
+ // get next index (beginning from the end)
+ currentIndex = [indexes indexLessThanIndex:currentIndex];
+ }
+
+ [favoritesArrayController rearrangeObjects];
+ [favoritesTableView reloadData];
+
+ // Set focus to favorite list to avoid an unstable state
+ [[self window] makeFirstResponder:favoritesTableView];
+
+ [removeButton setEnabled:([favoritesTableView numberOfSelectedRows] > 0)];
+ }
+ }
+}
+
+/**
+ * Import panel did end method.
+ */
+- (void)importPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(NSString *)contextInfo
+{
+
+ if (returnCode == NSOKButton) {
+
+ NSString *filename = [[panel filenames] objectAtIndex:0];
+ NSError *readError = nil;
+ NSString *convError = nil;
+ NSPropertyListFormat format;
+
+ NSDictionary *spf = nil;
+
+ if([[[filename pathExtension] lowercaseString] isEqualToString:@"spf"]) {
+ NSData *pData = [NSData dataWithContentsOfFile:filename options:NSUncachedRead error:&readError];
+
+ spf = [[NSPropertyListSerialization propertyListFromData:pData
+ mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain];
+
+ if(!spf || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
+ NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading data file", @"error while reading data file")]
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:NSLocalizedString(@"File couldn't be read.", @"error while reading data file")];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+ [alert runModal];
+ return;
+ }
+
+ if([spf objectForKey:@"queryFavorites"] && [[spf objectForKey:@"queryFavorites"] count]) {
+ if([favoritesTableView numberOfSelectedRows] > 0) {
+ // Insert imported queries after the last selected favorite
+ NSUInteger insertIndex = [[favoritesTableView selectedRowIndexes] lastIndex] + 1;
+ NSUInteger i;
+ for(i=0; i<[[spf objectForKey:@"queryFavorites"] count]; i++) {
+ [favorites insertObject:[[spf objectForKey:@"queryFavorites"] objectAtIndex:i] atIndex:insertIndex+i];
+ }
+ } else {
+ // If no selection add them
+ [favorites addObjectsFromArray:[spf objectForKey:@"queryFavorites"]];
+ }
+ [favoritesArrayController rearrangeObjects];
+ [favoritesTableView reloadData];
+ } else {
+ NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while reading data file", @"error while reading data file")]
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:NSLocalizedString(@"No query favorites found.", @"error that no query favorites found")];
+
+ [alert setAlertStyle:NSInformationalAlertStyle];
+ [alert runModal];
+ return;
+ }
+ }
+ }
+}
+
+
+/**
+ * Save panel did end method.
+ */
+- (void)savePanelDidEnd:(NSSavePanel *)panel returnCode:(int)returnCode contextInfo:(NSString *)contextInfo
+{
+
+ if([contextInfo isEqualToString:@"saveQuery"]) {
+ if (returnCode == NSOKButton) {
+ NSError *error = nil;
+
+ [prefs setInteger:[[encodingPopUp selectedItem] tag] forKey:@"lastSqlFileEncoding"];
+ [prefs synchronize];
+
+ [[favoriteQueryTextView string] writeToFile:[panel filename] atomically:YES encoding:[[encodingPopUp selectedItem] tag] error:&error];
+
+ if (error) [[NSAlert alertWithError:error] runModal];
+ }
+ }
+ else if([contextInfo isEqualToString:@"exportFavorites"]) {
+ if (returnCode == NSOKButton) {
+
+ // Build a SPF with format = "query favorites"
+ NSMutableDictionary *spfdata = [NSMutableDictionary dictionary];
+ NSMutableArray *favoriteData = [NSMutableArray array];
+
+
+ [spfdata setObject:[NSNumber numberWithInt:1] forKey:@"version"];
+ [spfdata setObject:@"query favorites" forKey:@"format"];
+ [spfdata setObject:[NSNumber numberWithBool:NO] forKey:@"encrypted"];
+
+ NSIndexSet *indexes = [favoritesTableView selectedRowIndexes];
+
+ // Get selected items and preserve the order
+ NSUInteger i;
+ for (i=1; i<[favorites count]; i++)
+ if([indexes containsIndex:i])
+ [favoriteData addObject:[favorites objectAtIndex:i]];
+
+ [spfdata setObject:favoriteData forKey:@"queryFavorites"];
+
+ NSString *err = nil;
+ NSData *plist = [NSPropertyListSerialization dataFromPropertyList:spfdata
+ format:NSPropertyListXMLFormat_v1_0
+ errorDescription:&err];
+
+ if(err != nil) {
+ NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while converting query favorite data", @"error while converting query favorite data")]
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:err];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+ [alert runModal];
+ return;
+ }
+
+ NSError *error = nil;
+ [plist writeToFile:[panel filename] options:NSAtomicWrite error:&error];
+ if (error) [[NSAlert alertWithError:error] runModal];
+
+ }
+ }
+}
+
+- (void)_initWithNoSelection
+{
+ [favoritesTableView selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO];
+ [[favoriteNameTextField cell] setPlaceholderString:SP_NO_SELECTION_PLACEHOLDER_STRING];
+ [favoriteNameTextField setStringValue:@""];
+ [favoriteQueryTextView setString:@""];
+}
+@end