From 16e45c62511d8752afdb30f2dd0e0238273aac85 Mon Sep 17 00:00:00 2001 From: Bibiko Date: Wed, 17 Nov 2010 22:07:18 +0000 Subject: =?UTF-8?q?=E2=80=A2=20Bundle=20Editor;=20added=20further=20functi?= =?UTF-8?q?onality=20to=20Bundle=20Editor=20including=20the=20chance=20to?= =?UTF-8?q?=20drag=20a=20bundle=20from=20the=20table=20view=20to=20Finder?= =?UTF-8?q?=20which=20creates=20a=20complete=20foo.spBundle=20bundle=20fol?= =?UTF-8?q?der=20for=20exporting=20a=20Bundle=20easily?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/SPBundleEditorController.m | 323 +++++++++++++++++++++++++++++++++----- 1 file changed, 282 insertions(+), 41 deletions(-) (limited to 'Source/SPBundleEditorController.m') diff --git a/Source/SPBundleEditorController.m b/Source/SPBundleEditorController.m index cd5de5f9..1fcfd9c7 100644 --- a/Source/SPBundleEditorController.m +++ b/Source/SPBundleEditorController.m @@ -34,9 +34,9 @@ { if ((self = [super initWithWindowNibName:@"BundleEditor"])) { - - commandBundleArray = [[NSMutableArray alloc] init]; - + commandBundleArray = nil; + draggedFilePath = nil; + bundlePath = [[[NSFileManager defaultManager] applicationSupportDirectoryForSubDirectory:SPBundleSupportFolder createIfNotExists:NO error:nil] retain]; } return self; @@ -45,14 +45,113 @@ - (void)dealloc { - [commandBundleArray release]; + if(commandBundleArray) [commandBundleArray release], commandBundleArray = nil; + if(bundlePath) [bundlePath release], bundlePath = nil; [super dealloc]; } -- (void)awakeFromNib +#pragma mark - + + +- (IBAction)inputPopuButtonChanged:(id)sender +{ + +} + +- (IBAction)duplicateCommandBundle:(id)sender +{ + if ([commandsTableView numberOfSelectedRows] == 1) + [self addCommandBundle:self]; + else + NSBeep(); +} + +- (IBAction)addCommandBundle:(id)sender +{ + NSMutableDictionary *bundle; + NSUInteger insertIndex; + + // Store pending changes in Query + [[self window] makeFirstResponder:nameTextField]; + + // Duplicate a selected favorite if sender == self + if (sender == self) { + NSDictionary *currentDict = [commandBundleArray objectAtIndex:[commandsTableView selectedRow]]; + bundle = [NSMutableDictionary dictionaryWithDictionary:currentDict]; + [bundle setObject:[NSString stringWithFormat:@"%@_Copy", [bundle objectForKey:@"bundleName"]] forKey:@"bundleName"]; + } + // Add a new favorite + else { + bundle = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"New Bundle", @"New Name", @"", nil] + forKeys:[NSArray arrayWithObjects:@"bundleName", @"name", @"command", nil]]; + } + if ([commandsTableView numberOfSelectedRows] > 0) { + insertIndex = [[commandsTableView selectedRowIndexes] lastIndex]+1; + [commandBundleArray insertObject:bundle atIndex:insertIndex]; + } + else { + [commandBundleArray addObject:bundle]; + insertIndex = [commandBundleArray count] - 1; + } + + [commandBundleArrayController rearrangeObjects]; + [commandsTableView reloadData]; + + [commandsTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:insertIndex] byExtendingSelection:NO]; + + [commandsTableView scrollRowToVisible:[commandsTableView selectedRow]]; + + [removeButton setEnabled:([commandsTableView numberOfSelectedRows] > 0)]; + [[self window] makeFirstResponder:commandsTableView]; + +} + +- (IBAction)removeCommandBundle:(id)sender +{ + NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Remove selected Bundles?", @"remove selected bundles message") + defaultButton:NSLocalizedString(@"Remove", @"remove button") + alternateButton:NSLocalizedString(@"Cancel", @"cancel button") + otherButton:nil + informativeTextWithFormat:NSLocalizedString(@"Are you sure you want to remove all selected Bundles? This action cannot be undone.", @"remove all selected bundles 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:0] setKeyEquivalentModifierMask:NSCommandKeyMask]; + [[buttons objectAtIndex:1] setKeyEquivalent:@"\r"]; + + [alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:@"removeSelectedBundles"]; +} + +- (IBAction)revealCommandBundleInFinder:(id)sender +{ + if([commandsTableView numberOfSelectedRows] != 1) return; + [[NSWorkspace sharedWorkspace] selectFile:[NSString stringWithFormat:@"%@/%@.%@/%@", + bundlePath, [[commandBundleArray objectAtIndex:[commandsTableView selectedRow]] objectForKey:@"bundleName"], SPUserBundleFileExtension, SPBundleFileName] inFileViewerRootedAtPath:nil]; +} + +- (IBAction)showHelp:(id)sender +{ + // [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:NSLocalizedString(@"http://www.sequelpro.com/docs/Bundles", @"Localized help page for bundles - do not localize if no translated webpage is available")]]; +} + +- (IBAction)showWindow:(id)sender { - NSString *bundlePath = [[NSFileManager defaultManager] applicationSupportDirectoryForSubDirectory:SPBundleSupportFolder createIfNotExists:NO error:nil]; + // Suppress parsing if window is already opened + if([[self window] isVisible]) return; + + // Order out window + [super showWindow:sender]; + + // Re-init commandBundleArray + if(commandBundleArray) [commandBundleArray release], commandBundleArray = nil; + commandBundleArray = [[NSMutableArray alloc] init]; + + // Load all installed bundle items if(bundlePath) { NSError *error = nil; NSArray *foundBundles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundlePath error:&error]; @@ -80,6 +179,13 @@ NSMutableDictionary *bundleCommand = [NSMutableDictionary dictionary]; [bundleCommand addEntriesFromDictionary:cmdData]; [bundleCommand setObject:[bundle stringByDeletingPathExtension] forKey:@"bundleName"]; + + NSArray *scopes = [[cmdData objectForKey:SPBundleFileScopeKey] componentsSeparatedByString:@" "]; + for(NSString *scope in scopes) { + [bundleCommand setObject:[NSNumber numberWithInt:1] forKey:scope]; + } + + [commandBundleArray addObject:bundleCommand]; } if (cmdData) [cmdData release]; @@ -87,45 +193,100 @@ } } } -} -- (IBAction)scopeButtonChanged:(id)sender -{ - -} + [commandBundleArrayController setContent:commandBundleArray]; + [commandsTableView reloadData]; -- (IBAction)inputPopuButtonChanged:(id)sender -{ - } -- (IBAction)duplicateCommandBundle:(id)sender +- (IBAction)saveAndCloseWindow:(id)sender { - -} -- (IBAction)addCommandBundle:(id)sender -{ - + // Commit all pending edits + if([commandBundleArrayController commitEditing]) { + NSLog(@"%@", commandBundleArray); + [[self window] performClose:self]; + } } -- (IBAction)removeCommandBundle:(id)sender +- (BOOL)saveBundle:(NSDictionary*)bundle atPath:(NSString*)aPath { - -} -- (IBAction)revealCommandBundleInFinder:(id)sender -{ - -} + NSFileManager *fm = [NSFileManager defaultManager]; + BOOL isDir = NO; -- (IBAction)showHelp:(id)sender -{ + // If passed aPath is nil construct the path from bundle's bundleName. + // aPath is mainly used for dragging a bundle from table view. + if(aPath == nil) { + if(![bundle objectForKey:@"bundleName"] || ![[bundle objectForKey:@"bundleName"] length]) { + return NO; + } + aPath = [NSString stringWithFormat:@"%@/%@.%@", bundlePath, [bundle objectForKey:@"bundleName"], SPUserBundleFileExtension]; + } + + // Create spBundle folder if it doesn't exist + if(![fm fileExistsAtPath:aPath isDirectory:&isDir]) { + if(![fm createDirectoryAtPath:aPath withIntermediateDirectories:YES attributes:nil error:nil]) + return NO; + isDir = YES; + } + // If aPath exists but it's not a folder bails + if(!isDir) return NO; + + // The command.plist file path + NSString *cmdFilePath = [NSString stringWithFormat:@"%@/%@", aPath, SPBundleFileName]; + + NSMutableDictionary *saveDict = [NSMutableDictionary dictionary]; + [saveDict addEntriesFromDictionary:bundle]; + + // Build scope key + NSMutableString *scopes = [NSMutableString string]; + if([bundle objectForKey:SPBundleScopeQueryEditor]) { + if([scopes length]) [scopes appendString:@" "]; + [scopes appendString:SPBundleScopeQueryEditor]; + } + if([bundle objectForKey:SPBundleScopeInputField]) { + if([scopes length]) [scopes appendString:@" "]; + [scopes appendString:SPBundleScopeInputField]; + } + if([bundle objectForKey:SPBundleScopeDataTable]) { + if([scopes length]) [scopes appendString:@" "]; + [scopes appendString:SPBundleScopeDataTable]; + } + [saveDict setObject:scopes forKey:SPBundleFileScopeKey]; + + // Remove unnecessary keys + [saveDict removeObjectsForKeys:[NSArray arrayWithObjects: + @"bundleName", + SPBundleScopeQueryEditor, + SPBundleScopeInputField, + SPBundleScopeDataTable, + nil]]; + + // Remove a given old command.plist file + [fm removeItemAtPath:cmdFilePath error:nil]; + [saveDict writeToFile:cmdFilePath atomically:YES]; + + return YES; + } -- (BOOL)windowShouldClose:(id)sender +#pragma mark - +#pragma mark NSWindow delegate + +- (void)windowWillClose:(NSNotification *)notification { + // Release commandBundleArray if window will close to save memory + if(commandBundleArray) [commandBundleArray release], commandBundleArray = nil; + + // Remove temporary drag file if any + if(draggedFilePath) { + [[NSFileManager defaultManager] removeItemAtPath:draggedFilePath error:nil]; + [draggedFilePath release]; + draggedFilePath = nil; + } + return YES; } @@ -173,22 +334,34 @@ [commandsTableView reloadData]; } -/* - * Changes in the name text field will be saved in data source directly - * to update the table view accordingly +/** + * Sheet did end method */ -- (void)controlTextDidChange:(NSNotification *)notification +- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo { - // Do nothing if no favorite is selected - if([commandsTableView numberOfSelectedRows] < 1) return; + if([contextInfo isEqualToString:@"removeSelectedBundles"]) { + if (returnCode == NSAlertDefaultReturn) { + NSIndexSet *indexes = [commandsTableView selectedRowIndexes]; - id object = [notification object]; + // get last index + NSUInteger currentIndex = [indexes lastIndex]; - if(object == nameTextField) { - [[commandBundleArray objectAtIndex:[commandsTableView selectedRow]] setObject:[nameTextField stringValue] forKey:@"name"]; - [commandsTableView reloadData]; - } + while (currentIndex != NSNotFound) { + [commandBundleArray removeObjectAtIndex:currentIndex]; + // get next index (beginning from the end) + currentIndex = [indexes indexLessThanIndex:currentIndex]; + } + + [commandBundleArrayController rearrangeObjects]; + [commandsTableView reloadData]; + + // Set focus to table view to avoid an unstable state + [[self window] makeFirstResponder:commandsTableView]; + + [removeButton setEnabled:([commandsTableView numberOfSelectedRows] > 0)]; + } + } } @@ -201,6 +374,74 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { + SEL action = [menuItem action]; + + if ( (action == @selector(duplicateCommandBundle:)) + || (action == @selector(revealCommandBundleInFinder:)) + ) + { + return ([commandsTableView numberOfSelectedRows] == 1); + } + else if ( (action == @selector(removeCommandBundle:)) ) + { + return ([commandsTableView numberOfSelectedRows] > 0); + } + + return YES; + +} + +#pragma mark - +#pragma mark TableView drag & drop delegate methods + +/** + * Allow for drag-n-drop out of the application as a copy + */ +- (NSUInteger)draggingSourceOperationMaskForLocal:(BOOL)isLocal +{ + return NSDragOperationMove; +} + + +/** + * Drag a table row item as spBundle + */ +- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rows toPasteboard:(NSPasteboard*)aPboard +{ + + if([commandsTableView numberOfSelectedRows] != 1 || [rows count] != 1) return NO; + + // Remove old temporary drag file if any + if(draggedFilePath) { + [[NSFileManager defaultManager] removeItemAtPath:draggedFilePath error:nil]; + [draggedFilePath release]; + draggedFilePath = nil; + } + + NSImage *dragImage; + NSPoint dragPosition; + + NSDictionary *bundleDict = [commandBundleArray objectAtIndex:[rows firstIndex]]; + NSString *bundleFileName = [bundleDict objectForKey:@"bundleName"]; + draggedFilePath = [[NSString stringWithFormat:@"/tmp/%@.%@", bundleFileName, SPUserBundleFileExtension] retain]; + + // Write temporary bundle data to disk but do not save the dict to Bundles folder + if(![self saveBundle:bundleDict atPath:draggedFilePath]) return NO; + + // Write data to the pasteboard + NSArray *fileList = [NSArray arrayWithObjects:draggedFilePath, nil]; + NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + [pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil]; + [pboard setPropertyList:fileList forType:NSFilenamesPboardType]; + + // Start the drag operation + dragImage = [[NSWorkspace sharedWorkspace] iconForFile:draggedFilePath]; + dragPosition = [[[self window] contentView] convertPoint:[[NSApp currentEvent] locationInWindow] fromView:nil]; + dragPosition.x -= 32; + dragPosition.y -= 32; + [[self window] dragImage:dragImage at:dragPosition offset:NSZeroSize + event:[NSApp currentEvent] pasteboard:pboard source:[self window] slideBack:YES]; + return YES; } -- cgit v1.2.3