aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Source/SPAppController.h9
-rw-r--r--Source/SPAppController.m126
-rw-r--r--Source/SPConstants.h3
-rw-r--r--Source/SPConstants.m35
-rw-r--r--Source/SPDatabaseDocument.m10
-rw-r--r--Source/SPEditSheetTextView.m91
-rw-r--r--Source/SPTextView.h9
-rw-r--r--Source/SPTextView.m179
-rw-r--r--Source/SPTextViewAdditions.h2
-rw-r--r--Source/SPTextViewAdditions.m261
10 files changed, 561 insertions, 164 deletions
diff --git a/Source/SPAppController.h b/Source/SPAppController.h
index 6aedd05e..e4508521 100644
--- a/Source/SPAppController.h
+++ b/Source/SPAppController.h
@@ -43,6 +43,12 @@
NSURL *_sessionURL;
NSMutableDictionary *_spfSessionDocData;
+
+ NSMutableDictionary *bundleItems;
+ NSMutableDictionary *bundleCategories;
+ NSMutableArray *bundleUsedScopes;
+ NSMutableDictionary *bundleKeyEquivalents;
+
}
// Window management
@@ -84,5 +90,8 @@
// Others
- (NSString *)contentOfFile:(NSString *)aPath;
+- (NSArray *)bundleCategoriesForScope:(NSString*)scope;
+- (NSArray *)bundleItemsForScope:(NSString*)scope;
+- (NSDictionary *)bundleKeyEquivalentsForScope:(NSString*)scope;
@end
diff --git a/Source/SPAppController.m b/Source/SPAppController.m
index 9614bb6d..ba0257e1 100644
--- a/Source/SPAppController.m
+++ b/Source/SPAppController.m
@@ -48,7 +48,12 @@
_sessionURL = nil;
aboutController = nil;
_spfSessionDocData = [[NSMutableDictionary alloc] init];
-
+
+ bundleItems = [[NSMutableDictionary alloc] initWithCapacity:1];
+ bundleCategories = [[NSMutableDictionary alloc] initWithCapacity:1];
+ bundleUsedScopes = [[NSMutableArray alloc] initWithCapacity:1];
+ bundleKeyEquivalents = [[NSMutableDictionary alloc] initWithCapacity:1];
+
[NSApp setDelegate:self];
}
@@ -103,6 +108,8 @@
// Report any crashes
[[FRFeedbackReporter sharedReporter] reportIfCrash];
+
+ [self reloadBundles:self];
}
/**
@@ -845,7 +852,94 @@
- (IBAction)reloadBundles:(id)sender
{
-
+ NSString *bundlePath = [[NSFileManager defaultManager] applicationSupportDirectoryForSubDirectory:SPBundleSupportFolder createIfNotExists:NO error:nil];
+
+ if(bundlePath) {
+ NSError *error = nil;
+ NSArray *foundBundles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundlePath error:&error];
+ if (foundBundles && [foundBundles count]) {
+ [bundleItems removeAllObjects];
+ [bundleUsedScopes removeAllObjects];
+ [bundleCategories removeAllObjects];
+ [bundleKeyEquivalents removeAllObjects];
+ for(NSString* bundle in foundBundles) {
+ if(![[[bundle pathExtension] lowercaseString] isEqualToString:[SPUserBundleFileExtension lowercaseString]]) continue;
+
+ NSError *readError = nil;
+ NSString *convError = nil;
+ NSPropertyListFormat format;
+ NSDictionary *cmdData = nil;
+ NSString *infoPath = [NSString stringWithFormat:@"%@/%@/%@", bundlePath, bundle, SPBundleFileName];
+ NSData *pData = [NSData dataWithContentsOfFile:infoPath options:NSUncachedRead error:&readError];
+
+ cmdData = [[NSPropertyListSerialization propertyListFromData:pData
+ mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain];
+
+ if(!cmdData || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
+ NSLog(@"“%@/%@” file couldn't be read.", bundle, SPBundleFileName);
+ NSBeep();
+ if (cmdData) [cmdData release];
+ } else {
+ if([cmdData objectForKey:SPBundleFileNameKey] && [[cmdData objectForKey:SPBundleFileNameKey] length] && [cmdData objectForKey:SPBundleFileScopeKey])
+ {
+
+ NSArray *scopes = [[cmdData objectForKey:SPBundleFileScopeKey] componentsSeparatedByString:@" "];
+ for(NSString *scope in scopes) {
+ if(![bundleUsedScopes containsObject:scope]) {
+ [bundleUsedScopes addObject:scope];
+ [bundleItems setObject:[NSMutableArray array] forKey:scope];
+ [bundleCategories setObject:[NSMutableArray array] forKey:scope];
+ [bundleKeyEquivalents setObject:[NSMutableDictionary dictionary] forKey:scope];
+ }
+
+ if([cmdData objectForKey:SPBundleFileCategoryKey] && ![bundleCategories containsObject:[cmdData objectForKey:SPBundleFileCategoryKey]])
+ [[bundleCategories objectForKey:scope] addObject:[cmdData objectForKey:SPBundleFileCategoryKey]];
+ }
+
+ NSMutableDictionary *aDict = [NSMutableDictionary dictionary];
+ [aDict setObject:[cmdData objectForKey:SPBundleFileNameKey] forKey:SPBundleInternLabelKey];
+ [aDict setObject:infoPath forKey:SPBundleInternPathToFileKey];
+
+ if([cmdData objectForKey:SPBundleFileKeyEquivalentKey] && [[cmdData objectForKey:SPBundleFileKeyEquivalentKey] length]) {
+ NSString *theKey = [cmdData objectForKey:SPBundleFileKeyEquivalentKey];
+ NSString *theChar = [theKey substringFromIndex:[theKey length]-1];
+ NSString *theMods = [theKey substringToIndex:[theKey length]-1];
+ NSUInteger mask = 0;
+ if([theMods rangeOfString:@"^"].length)
+ mask = mask | NSControlKeyMask;
+ if([theMods rangeOfString:@"@"].length)
+ mask = mask | NSCommandKeyMask;
+ if([theMods rangeOfString:@"~"].length)
+ mask = mask | NSAlternateKeyMask;
+ if(![[theChar lowercaseString] isEqualToString:theChar])
+ mask = mask | NSShiftKeyMask;
+ for(NSString* scope in scopes)
+ [[bundleKeyEquivalents objectForKey:scope] setObject:[NSArray arrayWithObjects:[theChar lowercaseString],
+ [NSNumber numberWithInteger:mask], infoPath, nil] forKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]];
+ [aDict setObject:[NSArray arrayWithObjects:theChar, [NSNumber numberWithInteger:mask], nil] forKey:SPBundleInternKeyEquivalentKey];
+ }
+
+ if([cmdData objectForKey:SPBundleFileTooltipKey] && [[cmdData objectForKey:SPBundleFileTooltipKey] length])
+ [aDict setObject:[cmdData objectForKey:SPBundleFileTooltipKey] forKey:SPBundleFileTooltipKey];
+
+ if([cmdData objectForKey:SPBundleFileCategoryKey] && [[cmdData objectForKey:SPBundleFileCategoryKey] length])
+ [aDict setObject:[cmdData objectForKey:SPBundleFileCategoryKey] forKey:SPBundleFileCategoryKey];
+
+ for(NSString* scope in scopes)
+ [[bundleItems objectForKey:scope] addObject:aDict];
+ }
+ if (cmdData) [cmdData release];
+ }
+ }
+
+ NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:SPBundleInternLabelKey ascending:YES] autorelease];
+ for(NSString* scope in [bundleItems allKeys]) {
+ [[bundleItems objectForKey:scope] sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
+ [[bundleCategories objectForKey:scope] sortUsingSelector:@selector(compare:)];
+ }
+
+ }
+ }
}
#pragma mark -
@@ -982,6 +1076,21 @@
return @"";
}
+- (NSArray *)bundleCategoriesForScope:(NSString*)scope
+{
+ return [bundleCategories objectForKey:scope];
+}
+
+- (NSArray *)bundleItemsForScope:(NSString*)scope
+{
+ return [bundleItems objectForKey:scope];
+}
+
+- (NSDictionary *)bundleKeyEquivalentsForScope:(NSString*)scope
+{
+ return [bundleKeyEquivalents objectForKey:scope];
+}
+
/**
* Sparkle updater delegate method. Called just before the updater relaunches Sequel Pro and we need to make
* sure that no sheets are currently open, which will prevent the app from being quit.
@@ -1010,15 +1119,20 @@
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
-
+
+ if(bundleItems) [bundleItems release];
+ if(bundleUsedScopes) [bundleUsedScopes release];
+ if(bundleCategories) [bundleCategories release];
+ if(bundleKeyEquivalents) [bundleKeyEquivalents release];
+
[prefsController release], prefsController = nil;
-
+
if (aboutController) [aboutController release], aboutController = nil;
if (bundleEditorController) [bundleEditorController release], bundleEditorController = nil;
-
+
if (_sessionURL) [_sessionURL release], _sessionURL = nil;
if (_spfSessionDocData) [_spfSessionDocData release], _spfSessionDocData = nil;
-
+
[super dealloc];
}
diff --git a/Source/SPConstants.h b/Source/SPConstants.h
index f320bd3e..897c0c26 100644
--- a/Source/SPConstants.h
+++ b/Source/SPConstants.h
@@ -449,6 +449,9 @@ extern NSString *SPBundleFileInputSourceFallBackKey;
extern NSString *SPBundleFileOutputActionKey;
extern NSString *SPBundleFileKeyEquivalentKey;
extern NSString *SPBundleFileTooltipKey;
+extern NSString *SPBundleInternLabelKey;
+extern NSString *SPBundleInternPathToFileKey;
+extern NSString *SPBundleInternKeyEquivalentKey;
extern NSString *SPBundleFileName;
extern NSString *SPBundleTaskInputFilePath;
diff --git a/Source/SPConstants.m b/Source/SPConstants.m
index 22653a67..f0277c72 100644
--- a/Source/SPConstants.m
+++ b/Source/SPConstants.m
@@ -244,22 +244,22 @@ NSString *SPFavoriteSSLCACertFileLocationKey = @"sslCACertFileLocati
// Bundle Files and Bundle Editor
NSString *SPBundleScopeQueryEditor = @"editor";
-NSString *SPBundleScopeDataTable = @"dataTable";
-NSString *SPBundleScopeInputField = @"inputField";
+NSString *SPBundleScopeDataTable = @"datatable";
+NSString *SPBundleScopeInputField = @"inputfield";
NSString *SPBundleScopeGeneral = @"general";
-NSString *SPBundleInputSourceSelectedText = @"selectedText";
-NSString *SPBundleInputSourceEntireContent = @"entireContent";
-NSString *SPBundleInputSourceCurrentWord = @"currentWord";
-NSString *SPBundleInputSourceCurrentQuery = @"currentQuery";
-NSString *SPBundleInputSourceCurrentLine = @"currentLine";
-NSString *SPBundleInputSourceNone = @"None";
-NSString *SPBundleOutputActionNone = @"None";
-NSString *SPBundleOutputActionInsertAsText = @"insertAsText";
-NSString *SPBundleOutputActionInsertAsSnippet = @"insertAsSnippet";
-NSString *SPBundleOutputActionReplaceSelection = @"replaceSelection";
-NSString *SPBundleOutputActionReplaceContent = @"replaceContent";
-NSString *SPBundleOutputActionShowAsTextTooltip = @"showAsTextTooltip";
-NSString *SPBundleOutputActionShowAsHTMLTooltip = @"showAsHTMLTooltip";
+NSString *SPBundleInputSourceSelectedText = @"selectedtext";
+NSString *SPBundleInputSourceEntireContent = @"entirecontent";
+NSString *SPBundleInputSourceCurrentWord = @"currentword";
+NSString *SPBundleInputSourceCurrentQuery = @"currentquery";
+NSString *SPBundleInputSourceCurrentLine = @"currentline";
+NSString *SPBundleInputSourceNone = @"none";
+NSString *SPBundleOutputActionNone = @"none";
+NSString *SPBundleOutputActionInsertAsText = @"insertastext";
+NSString *SPBundleOutputActionInsertAsSnippet = @"insertassnippet";
+NSString *SPBundleOutputActionReplaceSelection = @"replaceselection";
+NSString *SPBundleOutputActionReplaceContent = @"replacecontent";
+NSString *SPBundleOutputActionShowAsTextTooltip = @"showastexttooltip";
+NSString *SPBundleOutputActionShowAsHTMLTooltip = @"showashtmltooltip";
NSString *SPBundleFileCommandKey = @"command";
NSString *SPBundleFileScopeKey = @"scope";
NSString *SPBundleFileNameKey = @"name";
@@ -269,11 +269,14 @@ NSString *SPBundleFileInputSourceFallBackKey = @"input_fallback";
NSString *SPBundleFileOutputActionKey = @"output";
NSString *SPBundleFileKeyEquivalentKey = @"keyEquivalent";
NSString *SPBundleFileTooltipKey = @"tooltip";
+NSString *SPBundleInternLabelKey = @"label";
+NSString *SPBundleInternPathToFileKey = @"path";
+NSString *SPBundleInternKeyEquivalentKey = @"keyEquivalent";
NSString *SPBundleFileName = @"info.plist";
NSString *SPBundleTaskInputFilePath = @"/tmp/SP_BUNDLE_TASK_INPUT";
// sequel URL scheme
NSString *SPURLSchemeQueryInputPathHeader = @"/tmp/SP_QUERY_";
NSString *SPURLSchemeQueryResultPathHeader = @"/tmp/SP_QUERY_RESULT_";
-NSString *SPURLSchemeQueryResultStatusPathHeader = @"/tmp/SP_QUERY_STATUS_";
+NSString *SPURLSchemeQueryResultStatusPathHeader = @"/tmp/SP_QUERY_RESULT_STATUS_";
NSString *SPURLSchemeQueryResultMetaPathHeader = @"/tmp/SP_QUERY_META_";
diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m
index abcd0b9f..837c5387 100644
--- a/Source/SPDatabaseDocument.m
+++ b/Source/SPDatabaseDocument.m
@@ -242,8 +242,6 @@
[taskProgressWindow setAlphaValue:0.0];
[taskProgressWindow setContentView:taskProgressLayer];
- [[customQueryInstance valueForKeyPath:@"textView"] reloadBundleItems];
-
[contentViewSplitter setDelegate:self];
}
@@ -4433,10 +4431,10 @@
BOOL writeAsCsv = ([outputFormat isEqualToString:@"csv"]) ? YES : NO;
- NSString *queryFileName = [NSString stringWithFormat:@"/private/tmp/SP_QUERY_%@", docProcessID];
- NSString *resultFileName = [NSString stringWithFormat:@"/private/tmp/SP_QUERY_RESULT_%@", docProcessID];
- NSString *metaFileName = [NSString stringWithFormat:@"/private/tmp/SP_QUERY_RESULT_META_%@", docProcessID];
- NSString *statusFileName = [NSString stringWithFormat:@"/private/tmp/SP_QUERY_RESULT_STATUS_%@", docProcessID];
+ NSString *queryFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, docProcessID];
+ NSString *resultFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, docProcessID];
+ NSString *metaFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, docProcessID];
+ NSString *statusFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, docProcessID];
NSFileManager *fm = [NSFileManager defaultManager];
NSString *status = @"0";
BOOL isDir;
diff --git a/Source/SPEditSheetTextView.m b/Source/SPEditSheetTextView.m
index ab6530e9..babac98e 100644
--- a/Source/SPEditSheetTextView.m
+++ b/Source/SPEditSheetTextView.m
@@ -126,10 +126,101 @@
[[self delegate] setDoGroupDueToChars];
}
+ // Check for assign key equivalents inside user-defined bundle commands
+ NSDictionary *keyEquivalents = [[NSApp delegate] bundleKeyEquivalentsForScope:SPBundleScopeInputField];
+ if([keyEquivalents count]) {
+ for(NSString* key in [keyEquivalents allKeys]) {
+ NSArray *keyData = [keyEquivalents objectForKey:key];
+ if([[keyData objectAtIndex:0] isEqualToString:charactersIgnMod] && [[[keyEquivalents objectForKey:key] objectAtIndex:1] intValue] == curFlags) {
+ NSMenuItem *item = [[[NSMenuItem alloc] init] autorelease];
+ [item setToolTip:[[keyEquivalents objectForKey:key] objectAtIndex:2]];
+ [item setTag:0];
+ [self executeBundleItemForInputField:item];
+ return;
+ }
+ }
+ }
+
[super keyDown: theEvent];
}
+/**
+ * Add Bundle menu items.
+ */
+- (NSMenu *)menuForEvent:(NSEvent *)event
+{
+
+ NSMenu *menu = [[self class] defaultMenu];
+
+ // Remove 'Bundles' sub menu and separator
+ NSMenuItem *bItem = [menu itemWithTag:10000000];
+ if(bItem) {
+ NSInteger sepIndex = [menu indexOfItem:bItem]-1;
+ [menu removeItemAtIndex:sepIndex];
+ [menu removeItem:bItem];
+ }
+
+ if([[[[[[NSApp delegate] frontDocumentWindow] delegate] selectedTableDocument] connectionID] isEqualToString:@"_"]) return menu;
+
+ [[NSApp delegate] reloadBundles:self];
+
+ NSArray *bundleCategories = [[NSApp delegate] bundleCategoriesForScope:SPBundleScopeInputField];
+ NSArray *bundleItems = [[NSApp delegate] bundleItemsForScope:SPBundleScopeInputField];
+
+ // Add 'Bundles' sub menu
+ [menu addItem:[NSMenuItem separatorItem]];
+
+ NSMenu *bundleMenu = [[[NSMenu alloc] init] autorelease];
+ NSMenuItem *bundleSubMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Bundles", @"bundles menu item label") action:nil keyEquivalent:@""];
+ [bundleSubMenuItem setTag:10000000];
+
+ [menu addItem:bundleSubMenuItem];
+ [menu setSubmenu:bundleMenu forItem:bundleSubMenuItem];
+
+ NSMutableArray *categorySubMenus = [NSMutableArray array];
+ NSMutableArray *categoryMenus = [NSMutableArray array];
+ if([bundleCategories count]) {
+ for(NSString* title in bundleCategories) {
+ [categorySubMenus addObject:[[[NSMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""] autorelease]];
+ [categoryMenus addObject:[[[NSMenu alloc] init] autorelease]];
+ [bundleMenu addItem:[categorySubMenus lastObject]];
+ [bundleMenu setSubmenu:[categoryMenus lastObject] forItem:[categorySubMenus lastObject]];
+ }
+ }
+
+ NSInteger i = 0;
+ for(NSDictionary *item in bundleItems) {
+
+ NSString *keyEq;
+ if([item objectForKey:SPBundleFileKeyEquivalentKey])
+ keyEq = [[item objectForKey:SPBundleFileKeyEquivalentKey] objectAtIndex:0];
+ else
+ keyEq = @"";
+
+ NSMenuItem *mItem = [[[NSMenuItem alloc] initWithTitle:[item objectForKey:SPBundleInternLabelKey] action:@selector(executeBundleItemForInputField:) keyEquivalent:keyEq] autorelease];
+
+ if([keyEq length])
+ [mItem setKeyEquivalentModifierMask:[[[item objectForKey:SPBundleFileKeyEquivalentKey] objectAtIndex:1] intValue]];
+
+ if([item objectForKey:SPBundleFileTooltipKey])
+ [mItem setToolTip:[item objectForKey:SPBundleFileTooltipKey]];
+
+ [mItem setTag:1000000 + i++];
+
+ if([item objectForKey:SPBundleFileCategoryKey]) {
+ [[categoryMenus objectAtIndex:[bundleCategories indexOfObject:[item objectForKey:SPBundleFileCategoryKey]]] addItem:mItem];
+ } else {
+ [bundleMenu addItem:mItem];
+ }
+ }
+
+ [bundleSubMenuItem release];
+
+ return menu;
+
+}
+
/*
* Insert the content of a dragged file path or if ⌘ is pressed
* while dragging insert the file path
diff --git a/Source/SPTextView.h b/Source/SPTextView.h
index ed1b7666..02482611 100644
--- a/Source/SPTextView.h
+++ b/Source/SPTextView.h
@@ -86,11 +86,6 @@
NSRange queryRange;
BOOL shouldHiliteQuery;
- NSMutableArray *bundleItems;
- NSMutableArray *bundleCategories;
- NSMutableArray *bundleUsedScopes;
- NSMutableDictionary *bundleKeyEquivalents;
-
}
@property(retain) NSColor* queryHiliteColor;
@@ -149,8 +144,6 @@
- (BOOL)isSnippetMode;
-- (void)reloadBundleItems;
-
-- (IBAction)executeBundleItem:(id)sender;
+- (IBAction)executeBundleItemForEditor:(id)sender;
@end
diff --git a/Source/SPTextView.m b/Source/SPTextView.m
index da4c3f04..21ab5260 100644
--- a/Source/SPTextView.m
+++ b/Source/SPTextView.m
@@ -130,11 +130,6 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
isProcessingMirroredSnippets = NO;
completionWasRefreshed = NO;
- bundleItems = [[NSMutableArray alloc] initWithCapacity:1];
- bundleCategories = [[NSMutableArray alloc] initWithCapacity:1];
- bundleUsedScopes = [[NSMutableArray alloc] initWithCapacity:1];
- bundleKeyEquivalents = [[NSMutableDictionary alloc] initWithCapacity:1];
-
lineNumberView = [[NoodleLineNumberView alloc] initWithScrollView:scrollView];
[scrollView setVerticalRulerView:lineNumberView];
[scrollView setHasHorizontalRuler:NO];
@@ -2126,14 +2121,15 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
// Check for assign key equivalents inside user-defined bundle commands
- if([bundleKeyEquivalents count]) {
- for(NSString* key in [bundleKeyEquivalents allKeys]) {
- NSArray *keyData = [bundleKeyEquivalents objectForKey:key];
- if([[keyData objectAtIndex:0] isEqualToString:charactersIgnMod] && [[[bundleKeyEquivalents objectForKey:key] objectAtIndex:1] intValue] == curFlags) {
+ NSDictionary *keyEquivalents = [[NSApp delegate] bundleKeyEquivalentsForScope:SPBundleScopeQueryEditor];
+ if([keyEquivalents count]) {
+ for(NSString* key in [keyEquivalents allKeys]) {
+ NSArray *keyData = [keyEquivalents objectForKey:key];
+ if([[keyData objectAtIndex:0] isEqualToString:charactersIgnMod] && [[[keyEquivalents objectForKey:key] objectAtIndex:1] intValue] == curFlags) {
NSMenuItem *item = [[[NSMenuItem alloc] init] autorelease];
- [item setToolTip:[[bundleKeyEquivalents objectForKey:key] objectAtIndex:2]];
+ [item setToolTip:[[keyEquivalents objectForKey:key] objectAtIndex:2]];
[item setTag:0];
- [self executeBundleItem:item];
+ [self executeBundleItemForEditor:item];
return;
}
}
@@ -2921,7 +2917,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[[menu itemAtIndex:6] setHidden:YES];
}
- [self reloadBundleItems];
+ [[NSApp delegate] reloadBundles:self];
// Remove 'Bundles' sub menu and separator
NSMenuItem *bItem = [menu itemWithTag:10000000];
@@ -2931,6 +2927,9 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[menu removeItem:bItem];
}
+ NSArray *bundleCategories = [[NSApp delegate] bundleCategoriesForScope:SPBundleScopeQueryEditor];
+ NSArray *bundleItems = [[NSApp delegate] bundleItemsForScope:SPBundleScopeQueryEditor];
+
// Add 'Bundles' sub menu for custom query editor only so far if bundles with scope 'editor' were found
if(customQueryInstance && bundleItems && [bundleItems count]) {
[menu addItem:[NSMenuItem separatorItem]];
@@ -2957,23 +2956,23 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
for(NSDictionary *item in bundleItems) {
NSString *keyEq;
- if([item objectForKey:@"keyEquivalent"])
- keyEq = [[item objectForKey:@"keyEquivalent"] objectAtIndex:0];
+ if([item objectForKey:SPBundleFileKeyEquivalentKey])
+ keyEq = [[item objectForKey:SPBundleFileKeyEquivalentKey] objectAtIndex:0];
else
keyEq = @"";
- NSMenuItem *mItem = [[[NSMenuItem alloc] initWithTitle:[item objectForKey:@"label"] action:@selector(executeBundleItem:) keyEquivalent:keyEq] autorelease];
+ NSMenuItem *mItem = [[[NSMenuItem alloc] initWithTitle:[item objectForKey:SPBundleInternLabelKey] action:@selector(executeBundleItemForEditor:) keyEquivalent:keyEq] autorelease];
if([keyEq length])
- [mItem setKeyEquivalentModifierMask:[[[item objectForKey:@"keyEquivalent"] objectAtIndex:1] intValue]];
+ [mItem setKeyEquivalentModifierMask:[[[item objectForKey:SPBundleFileKeyEquivalentKey] objectAtIndex:1] intValue]];
- if([item objectForKey:@"tooltip"])
- [mItem setToolTip:[item objectForKey:@"tooltip"]];
+ if([item objectForKey:SPBundleFileTooltipKey])
+ [mItem setToolTip:[item objectForKey:SPBundleFileTooltipKey]];
[mItem setTag:1000000 + i++];
- if([item objectForKey:@"category"]) {
- [[categoryMenus objectAtIndex:[bundleCategories indexOfObject:[item objectForKey:@"category"]]] addItem:mItem];
+ if([item objectForKey:SPBundleFileCategoryKey]) {
+ [[categoryMenus objectAtIndex:[bundleCategories indexOfObject:[item objectForKey:SPBundleFileCategoryKey]]] addItem:mItem];
} else {
[bundleMenu addItem:mItem];
}
@@ -3404,86 +3403,14 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
#pragma mark -
-- (void)reloadBundleItems
+- (IBAction)executeBundleItemForEditor:(id)sender
{
- NSString *bundlePath = [[NSFileManager defaultManager] applicationSupportDirectoryForSubDirectory:SPBundleSupportFolder createIfNotExists:NO error:nil];
-
- if(bundlePath) {
- NSError *error = nil;
- NSArray *foundBundles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundlePath error:&error];
- if (foundBundles && [foundBundles count]) {
- [bundleItems removeAllObjects];
- [bundleUsedScopes removeAllObjects];
- [bundleCategories removeAllObjects];
- [bundleKeyEquivalents removeAllObjects];
- for(NSString* bundle in foundBundles) {
- if(![[[bundle pathExtension] lowercaseString] isEqualToString:[SPUserBundleFileExtension lowercaseString]]) continue;
-
- NSError *readError = nil;
- NSString *convError = nil;
- NSPropertyListFormat format;
- NSDictionary *cmdData = nil;
- NSString *infoPath = [NSString stringWithFormat:@"%@/%@/info.plist", bundlePath, bundle];
- NSData *pData = [NSData dataWithContentsOfFile:infoPath options:NSUncachedRead error:&readError];
-
- cmdData = [[NSPropertyListSerialization propertyListFromData:pData
- mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain];
-
- if(!cmdData || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
- NSLog(@"“%@/info.plist” file couldn't be read.", bundle);
- NSBeep();
- if (cmdData) [cmdData release];
- } else {
- if([cmdData objectForKey:@"name"] && [[cmdData objectForKey:@"name"] length]
- && [cmdData objectForKey:@"scope"] && [[cmdData objectForKey:@"scope"] isEqualToString:@"editor"])
- {
- if(![bundleUsedScopes containsObject:@"editor"])
- [bundleUsedScopes addObject:@"editor"];
- if([cmdData objectForKey:@"category"] && ![bundleCategories containsObject:[cmdData objectForKey:@"category"]])
- [bundleCategories addObject:[cmdData objectForKey:@"category"]];
- NSMutableDictionary *aDict = [NSMutableDictionary dictionary];
- [aDict setObject:[cmdData objectForKey:@"name"] forKey:@"label"];
- [aDict setObject:infoPath forKey:@"path"];
- if([cmdData objectForKey:@"keyEquivalent"] && [[cmdData objectForKey:@"keyEquivalent"] length]) {
- NSString *theKey = [cmdData objectForKey:@"keyEquivalent"];
- NSString *theChar = [theKey substringFromIndex:[theKey length]-1];
- NSString *theMods = [theKey substringToIndex:[theKey length]-1];
- NSUInteger mask = 0;
- if([theMods rangeOfString:@"^"].length)
- mask = mask | NSControlKeyMask;
- if([theMods rangeOfString:@"@"].length)
- mask = mask | NSCommandKeyMask;
- if([theMods rangeOfString:@"~"].length)
- mask = mask | NSAlternateKeyMask;
- if(![[theChar lowercaseString] isEqualToString:theChar])
- mask = mask | NSShiftKeyMask;
- [bundleKeyEquivalents setObject:[NSArray arrayWithObjects:[theChar lowercaseString], [NSNumber numberWithInteger:mask], infoPath, nil] forKey:[cmdData objectForKey:@"keyEquivalent"]];
- [aDict setObject:[NSArray arrayWithObjects:theChar, [NSNumber numberWithInteger:mask], nil] forKey:@"keyEquivalent"];
- }
- if([cmdData objectForKey:@"tooltip"] && [[cmdData objectForKey:@"tooltip"] length])
- [aDict setObject:[cmdData objectForKey:@"tooltip"] forKey:@"tooltip"];
- if([cmdData objectForKey:@"category"] && [[cmdData objectForKey:@"category"] length])
- [aDict setObject:[cmdData objectForKey:@"category"] forKey:@"category"];
- [bundleItems addObject:aDict];
- }
- if (cmdData) [cmdData release];
- }
- }
- NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"label" ascending:YES] autorelease];
- [bundleItems sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
- [bundleCategories sortUsingSelector:@selector(compare:)];
- }
- }
-
-}
-
-- (IBAction)executeBundleItem:(id)sender
-{
NSInteger idx = [sender tag] - 1000000;
NSString *infoPath = nil;
+ NSArray *bundleItems = [[NSApp delegate] bundleItemsForScope:SPBundleScopeQueryEditor];
if(idx >=0 && idx < [bundleItems count]) {
- infoPath = [[bundleItems objectAtIndex:idx] objectForKey:@"path"];
+ infoPath = [[bundleItems objectAtIndex:idx] objectForKey:SPBundleInternPathToFileKey];
} else {
if([sender tag] == 0 && [[sender toolTip] length]) {
infoPath = [sender toolTip];
@@ -3510,21 +3437,21 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
if (cmdData) [cmdData release];
return;
} else {
- if([cmdData objectForKey:@"command"] && [[cmdData objectForKey:@"command"] length] && [cmdData objectForKey:@"scope"] && [[cmdData objectForKey:@"scope"] isEqualToString:@"editor"]) {
+ if([cmdData objectForKey:SPBundleFileCommandKey] && [[cmdData objectForKey:SPBundleFileCommandKey] length]) {
- NSString *cmd = [cmdData objectForKey:@"command"];
+ NSString *cmd = [cmdData objectForKey:SPBundleFileCommandKey];
NSString *inputAction = @"";
NSString *inputFallBackAction = @"";
NSError *err = nil;
- NSString *inputTempFileName = @"/tmp/SP_BUNDLE_TASK_INPUT";
+
NSRange currentWordRange, currentQueryRange, currentSelectionRange, currentLineRange;
- [[NSFileManager defaultManager] removeItemAtPath:inputTempFileName error:nil];
+ [[NSFileManager defaultManager] removeItemAtPath:SPBundleTaskInputFilePath error:nil];
- if([cmdData objectForKey:@"input"])
- inputAction = [[cmdData objectForKey:@"input"] lowercaseString];
- if([cmdData objectForKey:@"input_fallback"])
- inputFallBackAction = [[cmdData objectForKey:@"input_fallback"] lowercaseString];
+ if([cmdData objectForKey:SPBundleFileInputSourceKey])
+ inputAction = [[cmdData objectForKey:SPBundleFileInputSourceKey] lowercaseString];
+ if([cmdData objectForKey:SPBundleFileInputSourceFallBackKey])
+ inputFallBackAction = [[cmdData objectForKey:SPBundleFileInputSourceFallBackKey] lowercaseString];
currentSelectionRange = [self selectedRange];
currentWordRange = [self getRangeForCurrentWord];
@@ -3536,28 +3463,28 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
currentLineRange = [[self string] lineRangeForRange:NSMakeRange([self selectedRange].location, 0)];
NSRange replaceRange = NSMakeRange(currentSelectionRange.location, 0);
- if([inputAction isEqualToString:@"selectedtext"]) {
+ if([inputAction isEqualToString:SPBundleInputSourceSelectedText]) {
if(!currentSelectionRange.length) {
- if([inputFallBackAction isEqualToString:@"currentword"])
+ if([inputFallBackAction isEqualToString:SPBundleInputSourceCurrentWord])
replaceRange = currentWordRange;
- else if([inputFallBackAction isEqualToString:@"currentline"])
+ else if([inputFallBackAction isEqualToString:SPBundleInputSourceCurrentLine])
replaceRange = currentLineRange;
- else if([inputFallBackAction isEqualToString:@"currentquery"])
+ else if([inputFallBackAction isEqualToString:SPBundleInputSourceCurrentQuery])
replaceRange = currentQueryRange;
- else if([inputAction isEqualToString:@"entirecontent"])
+ else if([inputAction isEqualToString:SPBundleInputSourceEntireContent])
replaceRange = NSMakeRange(0,[[self string] length]);
} else {
replaceRange = currentSelectionRange;
}
}
- else if([inputAction isEqualToString:@"entirecontent"]) {
- replaceRange = NSMakeRange(0,[[self string] length]);
+ else if([inputAction isEqualToString:SPBundleInputSourceEntireContent]) {
+ replaceRange = NSMakeRange(0, [[self string] length]);
}
NSMutableDictionary *env = [NSMutableDictionary dictionary];
[env setObject:[infoPath stringByDeletingLastPathComponent] forKey:@"SP_BUNDLE_PATH"];
- [env setObject:inputTempFileName forKey:@"SP_BUNDLE_INPUT_FILE"];
+ [env setObject:SPBundleTaskInputFilePath forKey:@"SP_BUNDLE_INPUT_FILE"];
if(currentSelectionRange.length)
[env setObject:[[self string] substringWithRange:currentSelectionRange] forKey:@"SP_SELECTED_TEXT"];
@@ -3600,7 +3527,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSError *inputFileError = nil;
NSString *input = [NSString stringWithString:[[self string] substringWithRange:replaceRange]];
- [input writeToFile:inputTempFileName
+ [input writeToFile:SPBundleTaskInputFilePath
atomically:YES
encoding:NSUTF8StringEncoding
error:&inputFileError];
@@ -3615,36 +3542,37 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSString *output = [cmd runBashCommandWithEnvironment:env atCurrentDirectoryPath:nil error:&err];
- [[NSFileManager defaultManager] removeItemAtPath:inputTempFileName error:nil];
+ [[NSFileManager defaultManager] removeItemAtPath:SPBundleTaskInputFilePath error:nil];
- if(err == nil && [cmdData objectForKey:@"output"]) {
- if([[cmdData objectForKey:@"output"] length] && ![[cmdData objectForKey:@"output"] isEqualToString:@"nop"]) {
- NSString *action = [[cmdData objectForKey:@"output"] lowercaseString];
+ if(err == nil && [cmdData objectForKey:SPBundleFileOutputActionKey]) {
+ if([[cmdData objectForKey:SPBundleFileOutputActionKey] length]
+ && ![[cmdData objectForKey:SPBundleFileOutputActionKey] isEqualToString:SPBundleOutputActionNone]) {
+ NSString *action = [[cmdData objectForKey:SPBundleFileOutputActionKey] lowercaseString];
- if([action isEqualToString:@"insertastext"]) {
+ if([action isEqualToString:SPBundleOutputActionInsertAsText]) {
[self insertText:output];
}
- else if([action isEqualToString:@"insertassnippet"]) {
+ else if([action isEqualToString:SPBundleOutputActionInsertAsSnippet]) {
[self insertAsSnippet:output atRange:replaceRange];
}
- else if([action isEqualToString:@"replacecontent"]) {
+ else if([action isEqualToString:SPBundleOutputActionReplaceContent]) {
if([[self string] length])
- [self setSelectedRange:NSMakeRange(0,[[self string] length])];
+ [self setSelectedRange:NSMakeRange(0, [[self string] length])];
[self insertText:output];
}
- else if([action isEqualToString:@"replaceselection"]) {
+ else if([action isEqualToString:SPBundleOutputActionReplaceSelection]) {
[self shouldChangeTextInRange:replaceRange replacementString:output];
[self replaceCharactersInRange:replaceRange withString:output];
}
- else if([action isEqualToString:@"showastexttooltip"]) {
+ else if([action isEqualToString:SPBundleOutputActionShowAsTextTooltip]) {
[SPTooltip showWithObject:output];
}
- else if([action isEqualToString:@"showashtmltooltip"]) {
+ else if([action isEqualToString:SPBundleOutputActionShowAsHTMLTooltip]) {
[SPTooltip showWithObject:output ofType:@"html"];
}
}
@@ -3686,11 +3614,6 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[prefs removeObserver:self forKeyPath:SPCustomQueryEditorTabStopWidth];
[prefs removeObserver:self forKeyPath:SPCustomQueryAutoUppercaseKeywords];
- if(bundleItems) [bundleItems release];
- if(bundleUsedScopes) [bundleUsedScopes release];
- if(bundleCategories) [bundleCategories release];
- if(bundleKeyEquivalents) [bundleKeyEquivalents release];
-
if (completionIsOpen) [completionPopup close], completionIsOpen = NO;
[prefs release];
[lineNumberView release];
diff --git a/Source/SPTextViewAdditions.h b/Source/SPTextViewAdditions.h
index 0a2e69b1..013d8872 100644
--- a/Source/SPTextViewAdditions.h
+++ b/Source/SPTextViewAdditions.h
@@ -42,6 +42,8 @@
- (IBAction)moveSelectionLineUp:(id)sender;
- (IBAction)moveSelectionLineDown:(id)sender;
+- (IBAction)executeBundleItemForInputField:(id)sender;
+
- (void)makeTextSizeLarger;
- (void)makeTextSizeSmaller;
diff --git a/Source/SPTextViewAdditions.m b/Source/SPTextViewAdditions.m
index 262fa47c..381f8f0a 100644
--- a/Source/SPTextViewAdditions.m
+++ b/Source/SPTextViewAdditions.m
@@ -22,6 +22,9 @@
//
// More info at <http://code.google.com/p/sequel-pro/>
+#import "SPAlertSheets.h"
+#import "SPTooltip.h"
+
@implementation NSTextView (SPTextViewAdditions)
/*
@@ -482,6 +485,264 @@
[self setEditable:editableStatus];
}
+- (IBAction)executeBundleItemForInputField:(id)sender
+{
+
+ NSInteger idx = [sender tag] - 1000000;
+ NSString *infoPath = nil;
+ NSArray *bundleItems = [[NSApp delegate] bundleItemsForScope:SPBundleScopeInputField];
+ if(idx >=0 && idx < [bundleItems count]) {
+ infoPath = [[bundleItems objectAtIndex:idx] objectForKey:SPBundleInternPathToFileKey];
+ } else {
+ if([sender tag] == 0 && [[sender toolTip] length]) {
+ infoPath = [sender toolTip];
+ }
+ }
+
+ if(!infoPath) {
+ NSBeep();
+ return;
+ }
+
+ NSError *readError = nil;
+ NSString *convError = nil;
+ NSPropertyListFormat format;
+ NSDictionary *cmdData = nil;
+ NSData *pData = [NSData dataWithContentsOfFile:infoPath options:NSUncachedRead error:&readError];
+
+ cmdData = [[NSPropertyListSerialization propertyListFromData:pData
+ mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain];
+
+ if(!cmdData || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
+ NSLog(@"“%@” file couldn't be read.", infoPath);
+ NSBeep();
+ if (cmdData) [cmdData release];
+ return;
+ } else {
+ if([cmdData objectForKey:SPBundleFileCommandKey] && [[cmdData objectForKey:SPBundleFileCommandKey] length]) {
+
+ NSString *cmd = [cmdData objectForKey:SPBundleFileCommandKey];
+ NSString *inputAction = @"";
+ NSString *inputFallBackAction = @"";
+ NSError *err = nil;
+
+ NSRange currentWordRange, currentSelectionRange, currentLineRange;
+
+ [[NSFileManager defaultManager] removeItemAtPath:SPBundleTaskInputFilePath error:nil];
+
+ if([cmdData objectForKey:SPBundleFileInputSourceKey])
+ inputAction = [[cmdData objectForKey:SPBundleFileInputSourceKey] lowercaseString];
+ if([cmdData objectForKey:SPBundleFileInputSourceFallBackKey])
+ inputFallBackAction = [[cmdData objectForKey:SPBundleFileInputSourceFallBackKey] lowercaseString];
+
+ currentSelectionRange = [self selectedRange];
+ currentWordRange = [self getRangeForCurrentWord];
+ currentLineRange = [[self string] lineRangeForRange:NSMakeRange([self selectedRange].location, 0)];
+
+ NSRange replaceRange = NSMakeRange(currentSelectionRange.location, 0);
+ if([inputAction isEqualToString:SPBundleInputSourceSelectedText]) {
+ if(!currentSelectionRange.length) {
+ if([inputFallBackAction isEqualToString:SPBundleInputSourceCurrentWord])
+ replaceRange = currentWordRange;
+ else if([inputFallBackAction isEqualToString:SPBundleInputSourceCurrentLine])
+ replaceRange = currentLineRange;
+ else if([inputAction isEqualToString:SPBundleInputSourceEntireContent])
+ replaceRange = NSMakeRange(0,[[self string] length]);
+ } else {
+ replaceRange = currentSelectionRange;
+ }
+
+ }
+ else if([inputAction isEqualToString:SPBundleInputSourceEntireContent]) {
+ replaceRange = NSMakeRange(0, [[self string] length]);
+ }
+
+ NSMutableDictionary *env = [NSMutableDictionary dictionary];
+ [env setObject:[infoPath stringByDeletingLastPathComponent] forKey:@"SP_BUNDLE_PATH"];
+ [env setObject:SPBundleTaskInputFilePath forKey:@"SP_BUNDLE_INPUT_FILE"];
+
+ if(currentSelectionRange.length)
+ [env setObject:[[self string] substringWithRange:currentSelectionRange] forKey:@"SP_SELECTED_TEXT"];
+
+ if(![[[[[[NSApp delegate] frontDocumentWindow] delegate] selectedTableDocument] connectionID] isEqualToString:@"_"]) {
+
+ id tablesListInstance = [[[[[NSApp delegate] frontDocumentWindow] delegate] selectedTableDocument] valueForKeyPath:@"tablesListInstance"];
+
+ if (tablesListInstance && [tablesListInstance selectedDatabase])
+ [env setObject:[tablesListInstance selectedDatabase] forKey:@"SP_SELECTED_DATABASE"];
+
+ if (tablesListInstance && [tablesListInstance allDatabaseNames])
+ [env setObject:[[tablesListInstance allDatabaseNames] componentsJoinedBySpacesAndQuoted] forKey:@"SP_ALL_DATABASES"];
+
+ if (tablesListInstance && [tablesListInstance allTableNames])
+ [env setObject:[[tablesListInstance allTableNames] componentsJoinedBySpacesAndQuoted] forKey:@"SP_ALL_TABLES"];
+
+ if (tablesListInstance && [tablesListInstance allViewNames])
+ [env setObject:[[tablesListInstance allViewNames] componentsJoinedBySpacesAndQuoted] forKey:@"SP_ALL_VIEWS"];
+
+ if (tablesListInstance && [tablesListInstance allFunctionNames])
+ [env setObject:[[tablesListInstance allFunctionNames] componentsJoinedBySpacesAndQuoted] forKey:@"SP_ALL_FUNCTIONS"];
+
+ if (tablesListInstance && [tablesListInstance allProcedureNames])
+ [env setObject:[[tablesListInstance allProcedureNames] componentsJoinedBySpacesAndQuoted] forKey:@"SP_ALL_PROCEDURES"];
+
+ if (tablesListInstance && [tablesListInstance tableName])
+ [env setObject:[tablesListInstance tableName] forKey:@"SP_SELECTED_TABLE"];
+
+ if([[[[NSApp delegate] frontDocumentWindow] delegate] selectedTableDocument] && [[[[[NSApp delegate] frontDocumentWindow] delegate] selectedTableDocument] mySQLVersion])
+ [env setObject:[[[[[NSApp delegate] frontDocumentWindow] delegate] selectedTableDocument] mySQLVersion] forKey:@"SP_RDBMS_VERSION"];
+
+ }
+
+ if(1)
+ [env setObject:@"mysql" forKey:@"SP_RDBMS_TYPE"];
+
+ if(currentWordRange.length)
+ [env setObject:[[self string] substringWithRange:currentWordRange] forKey:@"SP_CURRENT_WORD"];
+
+ if(currentLineRange.length)
+ [env setObject:[[self string] substringWithRange:currentLineRange] forKey:@"SP_CURRENT_LINE"];
+
+ NSError *inputFileError = nil;
+ NSString *input = [NSString stringWithString:[[self string] substringWithRange:replaceRange]];
+ [input writeToFile:SPBundleTaskInputFilePath
+ atomically:YES
+ encoding:NSUTF8StringEncoding
+ error:&inputFileError];
+
+ if(inputFileError != nil) {
+ NSString *errorMessage = [inputFileError localizedDescription];
+ SPBeginAlertSheet(NSLocalizedString(@"Bundle Error", @"bundle error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil,
+ [NSString stringWithFormat:@"%@ “%@”:\n%@", NSLocalizedString(@"Error for", @"error for message"), [cmdData objectForKey:@"name"], errorMessage]);
+ if (cmdData) [cmdData release];
+ return;
+ }
+
+ NSString *output = [cmd runBashCommandWithEnvironment:env atCurrentDirectoryPath:nil error:&err];
+
+ [[NSFileManager defaultManager] removeItemAtPath:SPBundleTaskInputFilePath error:nil];
+
+ if(err == nil && [cmdData objectForKey:SPBundleFileOutputActionKey]) {
+ if([[cmdData objectForKey:SPBundleFileOutputActionKey] length]
+ && ![[cmdData objectForKey:SPBundleFileOutputActionKey] isEqualToString:SPBundleOutputActionNone]) {
+ NSString *action = [[cmdData objectForKey:SPBundleFileOutputActionKey] lowercaseString];
+
+ if([action isEqualToString:SPBundleOutputActionInsertAsText]) {
+ [self insertText:output];
+ }
+
+ else if([action isEqualToString:SPBundleOutputActionInsertAsSnippet]) {
+ [self insertAsSnippet:output atRange:replaceRange];
+ }
+
+ else if([action isEqualToString:SPBundleOutputActionReplaceContent]) {
+ if([[self string] length])
+ [self setSelectedRange:NSMakeRange(0, [[self string] length])];
+ [self insertText:output];
+ }
+
+ else if([action isEqualToString:SPBundleOutputActionReplaceSelection]) {
+ [self shouldChangeTextInRange:replaceRange replacementString:output];
+ [self replaceCharactersInRange:replaceRange withString:output];
+ }
+
+ else if([action isEqualToString:SPBundleOutputActionShowAsTextTooltip]) {
+ [SPTooltip showWithObject:output];
+ }
+
+ else if([action isEqualToString:SPBundleOutputActionShowAsHTMLTooltip]) {
+ [SPTooltip showWithObject:output ofType:@"html"];
+ }
+ }
+ } else {
+ NSString *errorMessage = [err localizedDescription];
+ SPBeginAlertSheet(NSLocalizedString(@"BASH Error", @"bash error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil,
+ [NSString stringWithFormat:@"%@ “%@”:\n%@", NSLocalizedString(@"Error for", @"error for message"), [cmdData objectForKey:@"name"], errorMessage]);
+ }
+
+ }
+
+ if (cmdData) [cmdData release];
+
+ }
+
+}
+
+/**
+ * Add Bundle menu items.
+ */
+- (NSMenu *)menuForEvent:(NSEvent *)event
+{
+
+ NSMenu *menu = [[self class] defaultMenu];
+
+ // Remove 'Bundles' sub menu and separator
+ NSMenuItem *bItem = [menu itemWithTag:10000000];
+ if(bItem) {
+ NSInteger sepIndex = [menu indexOfItem:bItem]-1;
+ [menu removeItemAtIndex:sepIndex];
+ [menu removeItem:bItem];
+ }
+
+ if([[[[[[NSApp delegate] frontDocumentWindow] delegate] selectedTableDocument] connectionID] isEqualToString:@"_"]) return menu;
+
+ [[NSApp delegate] reloadBundles:self];
+
+ NSArray *bundleCategories = [[NSApp delegate] bundleCategoriesForScope:SPBundleScopeInputField];
+ NSArray *bundleItems = [[NSApp delegate] bundleItemsForScope:SPBundleScopeInputField];
+
+ // Add 'Bundles' sub menu
+ [menu addItem:[NSMenuItem separatorItem]];
+
+ NSMenu *bundleMenu = [[[NSMenu alloc] init] autorelease];
+ NSMenuItem *bundleSubMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Bundles", @"bundles menu item label") action:nil keyEquivalent:@""];
+ [bundleSubMenuItem setTag:10000000];
+
+ [menu addItem:bundleSubMenuItem];
+ [menu setSubmenu:bundleMenu forItem:bundleSubMenuItem];
+
+ NSMutableArray *categorySubMenus = [NSMutableArray array];
+ NSMutableArray *categoryMenus = [NSMutableArray array];
+ if([bundleCategories count]) {
+ for(NSString* title in bundleCategories) {
+ [categorySubMenus addObject:[[[NSMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""] autorelease]];
+ [categoryMenus addObject:[[[NSMenu alloc] init] autorelease]];
+ [bundleMenu addItem:[categorySubMenus lastObject]];
+ [bundleMenu setSubmenu:[categoryMenus lastObject] forItem:[categorySubMenus lastObject]];
+ }
+ }
+
+ NSInteger i = 0;
+ for(NSDictionary *item in bundleItems) {
+
+ NSString *keyEq = @"";
+ // if([item objectForKey:SPBundleFileKeyEquivalentKey])
+ // keyEq = [[item objectForKey:SPBundleFileKeyEquivalentKey] objectAtIndex:0];
+ // else
+ // keyEq = @"";
+
+ NSMenuItem *mItem = [[[NSMenuItem alloc] initWithTitle:[item objectForKey:SPBundleInternLabelKey] action:@selector(executeBundleItemForInputField:) keyEquivalent:keyEq] autorelease];
+
+ if([keyEq length])
+ [mItem setKeyEquivalentModifierMask:[[[item objectForKey:SPBundleFileKeyEquivalentKey] objectAtIndex:1] intValue]];
+
+ if([item objectForKey:SPBundleFileTooltipKey])
+ [mItem setToolTip:[item objectForKey:SPBundleFileTooltipKey]];
+
+ [mItem setTag:1000000 + i++];
+
+ if([item objectForKey:SPBundleFileCategoryKey]) {
+ [[categoryMenus objectAtIndex:[bundleCategories indexOfObject:[item objectForKey:SPBundleFileCategoryKey]]] addItem:mItem];
+ } else {
+ [bundleMenu addItem:mItem];
+ }
+ }
+
+ [bundleSubMenuItem release];
+
+ return menu;
+
+}
#pragma mark -
#pragma mark multi-touch trackpad support