diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/SPContentFilterManager.h | 69 | ||||
-rw-r--r-- | Source/SPContentFilterManager.m | 832 |
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 |