[182] | 1 | //
|
---|
[320] | 2 | // ICeCoffEEServices.m
|
---|
[182] | 3 | // ICeCoffEE APE
|
---|
| 4 | //
|
---|
| 5 | // Created by Nicholas Riley on 5/10/05.
|
---|
| 6 | // Copyright 2005 Nicholas Riley. All rights reserved.
|
---|
| 7 | //
|
---|
| 8 |
|
---|
[320] | 9 | #import "ICeCoffEEServices.h"
|
---|
[319] | 10 | #import "ICeCoffEEShared.h"
|
---|
[182] | 11 |
|
---|
[319] | 12 | // an approximate clone of HIToolbox's CreateServicesLocalizedDictKey
|
---|
| 13 | static CFStringRef preferredLocalization(CFDictionaryRef localizations) {
|
---|
| 14 | if (localizations == NULL)
|
---|
| 15 | return NULL;
|
---|
| 16 |
|
---|
| 17 | CFIndex localizationCount = CFDictionaryGetCount(localizations);
|
---|
| 18 | if (localizationCount == 0)
|
---|
| 19 | return NULL;
|
---|
| 20 |
|
---|
| 21 | const void **locales = malloc(localizationCount * sizeof(void *));
|
---|
| 22 | if (locales == NULL)
|
---|
| 23 | return NULL;
|
---|
| 24 |
|
---|
| 25 | CFDictionaryGetKeysAndValues(localizations, locales, NULL);
|
---|
| 26 | CFArrayRef availableLocales = CFArrayCreate(NULL, locales, localizationCount, NULL);
|
---|
| 27 | if (availableLocales == NULL)
|
---|
| 28 | return NULL;
|
---|
| 29 |
|
---|
[322] | 30 | // XXX need to obtain in-use app localization list, THEN canonicalize everything...
|
---|
| 31 | // XXX THEN use CFBundleCopyLocalizationsForPreferences. All to fix RealPlayer. Aieee.
|
---|
[319] | 32 | CFArrayRef preferredLocales = CFBundleCopyPreferredLocalizationsFromArray(availableLocales);
|
---|
| 33 | // NSLog(@"%@ => %@", availableLocales, preferredLocales);
|
---|
| 34 | CFRelease(availableLocales);
|
---|
| 35 | CFStringRef preferredLocalization;
|
---|
| 36 | if (preferredLocales != NULL) {
|
---|
| 37 | if (CFArrayGetCount(preferredLocales) > 0) {
|
---|
| 38 | CFStringRef preferredLocale = CFArrayGetValueAtIndex(preferredLocales, 0);
|
---|
| 39 | preferredLocalization = (CFStringRef)CFDictionaryGetValue(localizations, preferredLocale);
|
---|
| 40 | }
|
---|
| 41 | CFRelease(preferredLocales);
|
---|
| 42 | }
|
---|
| 43 | if (preferredLocalization == NULL) {
|
---|
| 44 | preferredLocalization = (CFStringRef)CFDictionaryGetValue(localizations, CFSTR("default"));
|
---|
| 45 | if (preferredLocalization == NULL)
|
---|
| 46 | preferredLocalization = (CFStringRef)CFDictionaryGetValue(localizations, (CFStringRef)locales[0]);
|
---|
| 47 | }
|
---|
| 48 |
|
---|
| 49 | free(locales);
|
---|
| 50 | // NSLog(@"%@", preferredLocalization);
|
---|
| 51 | return preferredLocalization;
|
---|
| 52 | }
|
---|
| 53 |
|
---|
| 54 | NSArray *CFServiceControllerCopyServicesEntries(void);
|
---|
| 55 |
|
---|
| 56 | NSDictionary *ICCF_GetServicesInfo(void) {
|
---|
| 57 | NSArray *services = CFServiceControllerCopyServicesEntries();
|
---|
| 58 |
|
---|
| 59 | NSEnumerator *e = [services objectEnumerator];
|
---|
| 60 | NSDictionary *serviceEntry;
|
---|
| 61 | NSMutableDictionary *serviceDict = [[NSMutableDictionary alloc] init];
|
---|
| 62 | while ( (serviceEntry = (NSDictionary *)[e nextObject]) != nil) {
|
---|
| 63 | // XXX once tested, redo all this with CF, and no autoreleasing
|
---|
| 64 | // XXX items named the same as submenus (figure out how Cocoa itself does it, too)
|
---|
| 65 | NSString *itemPath = (NSString *)preferredLocalization((CFDictionaryRef)[serviceEntry objectForKey: @"NSMenuItem"]);
|
---|
| 66 | if (itemPath == nil) continue;
|
---|
| 67 |
|
---|
| 68 | NSString *bundlePath = [serviceEntry objectForKey: @"NSBundlePath"];
|
---|
| 69 | BOOL bubbledUp = (bundlePath == nil);
|
---|
| 70 | NSArray *itemComponents = [itemPath componentsSeparatedByString: @"/"];
|
---|
| 71 | NSEnumerator *ce = [itemComponents objectEnumerator];
|
---|
| 72 | NSString *itemComponent;
|
---|
| 73 | NSMutableDictionary *levelDict = serviceDict;
|
---|
| 74 | NSMutableDictionary *itemDict = nil;
|
---|
| 75 | while ( (itemComponent = (NSString *)[ce nextObject]) != nil) {
|
---|
[320] | 76 | // levelDict is nil if just created
|
---|
| 77 | if (levelDict != nil && !bubbledUp) {
|
---|
| 78 | NSString *oldBundlePath = [itemDict objectForKey: (NSString *)kICServiceBundlePath];
|
---|
[319] | 79 | if ([oldBundlePath isEqualToString: bundlePath])
|
---|
| 80 | bubbledUp = YES;
|
---|
| 81 | else if (oldBundlePath != nil) {
|
---|
| 82 | [oldBundlePath retain];
|
---|
[320] | 83 | [itemDict removeObjectForKey: (NSString *)kICServiceBundlePath];
|
---|
[319] | 84 | NSEnumerator *be = [levelDict objectEnumerator];
|
---|
| 85 | while ( (itemDict = (NSMutableDictionary *)[be nextObject]) != nil)
|
---|
| 86 | [itemDict setObject: oldBundlePath forKey: (NSString *)kICServiceBundlePath];
|
---|
| 87 | [oldBundlePath release];
|
---|
| 88 | }
|
---|
| 89 | }
|
---|
[320] | 90 | if (levelDict == nil) {
|
---|
| 91 | levelDict = [[NSMutableDictionary alloc] init];
|
---|
| 92 | [itemDict setObject: levelDict forKey: (NSString *)kICServiceSubmenu];
|
---|
| 93 | [levelDict release];
|
---|
| 94 | itemDict = nil;
|
---|
| 95 | } else {
|
---|
| 96 | itemDict = [levelDict objectForKey: itemComponent];
|
---|
| 97 | }
|
---|
[319] | 98 | if (itemDict == nil) {
|
---|
| 99 | itemDict = [[NSMutableDictionary alloc] init];
|
---|
| 100 | if (!bubbledUp) {
|
---|
| 101 | [itemDict setObject: bundlePath forKey: (NSString *)kICServiceBundlePath];
|
---|
| 102 | bubbledUp = YES;
|
---|
| 103 | }
|
---|
| 104 | [levelDict setObject: itemDict forKey: itemComponent];
|
---|
[320] | 105 | levelDict = nil;
|
---|
[319] | 106 | [itemDict release];
|
---|
| 107 | } else {
|
---|
[320] | 108 | levelDict = [itemDict objectForKey: (NSString *)kICServiceSubmenu];
|
---|
[319] | 109 | }
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 | if (!bubbledUp)
|
---|
[320] | 113 | [itemDict setObject: bundlePath forKey: (NSString *)kICServiceBundlePath];
|
---|
[319] | 114 |
|
---|
| 115 | NSString *keyEquivalent = (NSString *)preferredLocalization((CFDictionaryRef)[serviceEntry objectForKey: @"NSKeyEquivalent"]);
|
---|
| 116 | if (keyEquivalent == nil) continue;
|
---|
| 117 |
|
---|
[320] | 118 | [itemDict setObject: keyEquivalent forKey: (NSString *)kICServiceShortcut];
|
---|
[319] | 119 | }
|
---|
| 120 | [services release];
|
---|
| 121 |
|
---|
| 122 | return [serviceDict autorelease];
|
---|
| 123 | }
|
---|
| 124 |
|
---|
| 125 |
|
---|
[182] | 126 | void ICCF_SetServicesMenu(NSMenu *servicesMenu) {
|
---|
[439] | 127 | // attaches a delegate (NSServiceMaster in 10.4, NSServiceMenuDelegate in 10.5)
|
---|
[182] | 128 | [[NSApplication sharedApplication] setServicesMenu: servicesMenu];
|
---|
| 129 | id delegate;
|
---|
[439] | 130 | if ( (delegate = [servicesMenu delegate]) == nil)
|
---|
| 131 | return;
|
---|
| 132 |
|
---|
| 133 | // populate menu so we have something to filter; delegate reponds to one of these methods
|
---|
| 134 | if ([delegate respondsToSelector: @selector(menuNeedsUpdate:)]) {
|
---|
| 135 | [delegate menuNeedsUpdate: servicesMenu];
|
---|
| 136 | }
|
---|
| 137 | if ([delegate respondsToSelector: @selector(menu:updateItem:atIndex:shouldCancel:)]) {
|
---|
| 138 | int itemCount = [delegate numberOfItemsInMenu: servicesMenu];
|
---|
| 139 | for (int i = 0 ; i < itemCount ; i++) {
|
---|
| 140 | [servicesMenu addItemWithTitle: @"" action: NULL keyEquivalent: @""];
|
---|
[182] | 141 | }
|
---|
[439] | 142 | for (int i = 0 ; i < itemCount ; i++) {
|
---|
| 143 | if (![delegate menu: servicesMenu updateItem: (NSMenuItem *)[servicesMenu itemAtIndex: i] atIndex: i shouldCancel: NO])
|
---|
| 144 | break;
|
---|
[182] | 145 | }
|
---|
| 146 | }
|
---|
| 147 | }
|
---|
[319] | 148 |
|
---|
| 149 | IconRef ICCF_CopyIconRefForPath(NSString *path) {
|
---|
| 150 | IconRef icon;
|
---|
| 151 | FSRef fsr;
|
---|
| 152 | SInt16 label;
|
---|
| 153 | OSStatus err = noErr;
|
---|
| 154 |
|
---|
| 155 | err = FSPathMakeRef((const UInt8 *)[path fileSystemRepresentation], &fsr, NULL);
|
---|
| 156 | if (err != noErr) return NULL;
|
---|
| 157 |
|
---|
| 158 | err = GetIconRefFromFileInfo(&fsr, 0, NULL, kFSCatInfoNone, NULL, kIconServicesNormalUsageFlag, &icon, &label);
|
---|
| 159 | if (err != noErr) return NULL;
|
---|
| 160 |
|
---|
| 161 | return icon;
|
---|
| 162 | }
|
---|