source: trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEServices.m

Last change on this file was 439, checked in by Nicholas Riley, 16 years ago

Fix up comments and simplify control flow in ICCF_SetServicesMenu

File size: 6.0 KB
Line 
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
13static 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
54NSArray *CFServiceControllerCopyServicesEntries(void);
55
56NSDictionary *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
126void ICCF_SetServicesMenu(NSMenu *servicesMenu) {
127 // attaches a delegate (NSServiceMaster in 10.4, NSServiceMenuDelegate in 10.5)
128 [[NSApplication sharedApplication] setServicesMenu: servicesMenu];
129 id delegate;
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: @""];
141 }
142 for (int i = 0 ; i < itemCount ; i++) {
143 if (![delegate menu: servicesMenu updateItem: (NSMenuItem *)[servicesMenu itemAtIndex: i] atIndex: i shouldCancel: NO])
144 break;
145 }
146 }
147}
148
149IconRef 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}
Note: See TracBrowser for help on using the repository browser.