diff options
-rw-r--r-- | Source/SPAppController.h | 9 | ||||
-rw-r--r-- | Source/SPAppController.m | 126 | ||||
-rw-r--r-- | Source/SPConstants.h | 3 | ||||
-rw-r--r-- | Source/SPConstants.m | 35 | ||||
-rw-r--r-- | Source/SPDatabaseDocument.m | 10 | ||||
-rw-r--r-- | Source/SPEditSheetTextView.m | 91 | ||||
-rw-r--r-- | Source/SPTextView.h | 9 | ||||
-rw-r--r-- | Source/SPTextView.m | 179 | ||||
-rw-r--r-- | Source/SPTextViewAdditions.h | 2 | ||||
-rw-r--r-- | Source/SPTextViewAdditions.m | 261 |
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 |