source: trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEServices.m@ 458

Last change on this file since 458 was 439, checked in by Nicholas Riley, 17 years ago

Fix up comments and simplify control flow in ICCF_SetServicesMenu

File size: 6.0 KB
RevLine 
[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
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
[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
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) {
[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]126void 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
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.