1 | //
|
---|
2 | // ICeCoffEEServices.m
|
---|
3 | // ICeCoffEE APE
|
---|
4 | //
|
---|
5 | // Created by Nicholas Riley on 5/10/05.
|
---|
6 | // Copyright 2005 Nicholas Riley. All rights reserved.
|
---|
7 | //
|
---|
8 |
|
---|
9 | #import "ICeCoffEEServices.h"
|
---|
10 | #import "ICeCoffEEShared.h"
|
---|
11 |
|
---|
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 |
|
---|
30 | // XXX need to obtain in-use app localization list, THEN canonicalize everything...
|
---|
31 | // XXX THEN use CFBundleCopyLocalizationsForPreferences. All to fix RealPlayer. Aieee.
|
---|
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) {
|
---|
76 | // levelDict is nil if just created
|
---|
77 | if (levelDict != nil && !bubbledUp) {
|
---|
78 | NSString *oldBundlePath = [itemDict objectForKey: (NSString *)kICServiceBundlePath];
|
---|
79 | if ([oldBundlePath isEqualToString: bundlePath])
|
---|
80 | bubbledUp = YES;
|
---|
81 | else if (oldBundlePath != nil) {
|
---|
82 | [oldBundlePath retain];
|
---|
83 | [itemDict removeObjectForKey: (NSString *)kICServiceBundlePath];
|
---|
84 | NSEnumerator *be = [levelDict objectEnumerator];
|
---|
85 | while ( (itemDict = (NSMutableDictionary *)[be nextObject]) != nil)
|
---|
86 | [itemDict setObject: oldBundlePath forKey: (NSString *)kICServiceBundlePath];
|
---|
87 | [oldBundlePath release];
|
---|
88 | }
|
---|
89 | }
|
---|
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 | }
|
---|
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];
|
---|
105 | levelDict = nil;
|
---|
106 | [itemDict release];
|
---|
107 | } else {
|
---|
108 | levelDict = [itemDict objectForKey: (NSString *)kICServiceSubmenu];
|
---|
109 | }
|
---|
110 | }
|
---|
111 |
|
---|
112 | if (!bubbledUp)
|
---|
113 | [itemDict setObject: bundlePath forKey: (NSString *)kICServiceBundlePath];
|
---|
114 |
|
---|
115 | NSString *keyEquivalent = (NSString *)preferredLocalization((CFDictionaryRef)[serviceEntry objectForKey: @"NSKeyEquivalent"]);
|
---|
116 | if (keyEquivalent == nil) continue;
|
---|
117 |
|
---|
118 | [itemDict setObject: keyEquivalent forKey: (NSString *)kICServiceShortcut];
|
---|
119 | }
|
---|
120 | [services release];
|
---|
121 |
|
---|
122 | return [serviceDict autorelease];
|
---|
123 | }
|
---|
124 |
|
---|
125 |
|
---|
126 | void ICCF_SetServicesMenu(NSMenu *servicesMenu) {
|
---|
127 | // in 10.3, this populates the menu; in 10.4, it attaches a delegate (NSServiceMaster)
|
---|
128 | [[NSApplication sharedApplication] setServicesMenu: servicesMenu];
|
---|
129 | id delegate;
|
---|
130 | if ( (delegate = [servicesMenu delegate]) != nil) {
|
---|
131 | // populate menu so we have something to filter
|
---|
132 | if ([delegate respondsToSelector: @selector(menuNeedsUpdate:)]) {
|
---|
133 | [delegate menuNeedsUpdate: servicesMenu];
|
---|
134 | }
|
---|
135 | if ([delegate respondsToSelector: @selector(menu:updateItem:atIndex:shouldCancel:)]) {
|
---|
136 | int itemCount = [delegate numberOfItemsInMenu: servicesMenu];
|
---|
137 | for (int i = 0 ; i < itemCount ; i++) {
|
---|
138 | [servicesMenu addItemWithTitle: @"" action: NULL keyEquivalent: @""];
|
---|
139 | }
|
---|
140 | for (int i = 0 ; i < itemCount ; i++) {
|
---|
141 | if (![delegate menu: servicesMenu updateItem: (NSMenuItem *)[servicesMenu itemAtIndex: i] atIndex: i shouldCancel: NO])
|
---|
142 | break;
|
---|
143 | }
|
---|
144 | }
|
---|
145 | }
|
---|
146 | }
|
---|
147 |
|
---|
148 | IconRef ICCF_CopyIconRefForPath(NSString *path) {
|
---|
149 | IconRef icon;
|
---|
150 | FSRef fsr;
|
---|
151 | SInt16 label;
|
---|
152 | OSStatus err = noErr;
|
---|
153 |
|
---|
154 | err = FSPathMakeRef((const UInt8 *)[path fileSystemRepresentation], &fsr, NULL);
|
---|
155 | if (err != noErr) return NULL;
|
---|
156 |
|
---|
157 | err = GetIconRefFromFileInfo(&fsr, 0, NULL, kFSCatInfoNone, NULL, kIconServicesNormalUsageFlag, &icon, &label);
|
---|
158 | if (err != noErr) return NULL;
|
---|
159 |
|
---|
160 | return icon;
|
---|
161 | }
|
---|