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

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

ICeCoffEE.m: I was wrong in [319] about the selection extension stuff
being dumb; explain why in code, even though it's not fixed.
Implement some of the same parens support I did in Terminal. Update
for more robust service info dictionary format.

ICeCoffEEServicePrefController.m: Update for more robust service info
dictionary format.

ICeCoffEEServices.[hm]: Renamed from ICeCoffEESetServicesMenu.[hm],
since we do more now. Service info dictionary-creating code now makes
more sense and no longer has naming collision issues.

ICeCoffEEWebKit.m: Some preliminary Safari 3 compatibility stuff, not
quite working yet. An outstanding question - is it better to rely on
"public" WebCore API or private WebKit API? So far it seems the
latter is more stable.

English.lproj/APEInfo.rtfd: The Bored Zo has a name: use it. Remove
now-erroneous reference to SimpleText since TextEdit support is gone.

File size: 5.9 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 does this actually work in a localized app?
31 CFArrayRef preferredLocales = CFBundleCopyPreferredLocalizationsFromArray(availableLocales);
32 // NSLog(@"%@ => %@", availableLocales, preferredLocales);
33 CFRelease(availableLocales);
34 CFStringRef preferredLocalization;
35 if (preferredLocales != NULL) {
36 if (CFArrayGetCount(preferredLocales) > 0) {
37 CFStringRef preferredLocale = CFArrayGetValueAtIndex(preferredLocales, 0);
38 preferredLocalization = (CFStringRef)CFDictionaryGetValue(localizations, preferredLocale);
39 }
40 CFRelease(preferredLocales);
41 }
42 if (preferredLocalization == NULL) {
43 preferredLocalization = (CFStringRef)CFDictionaryGetValue(localizations, CFSTR("default"));
44 if (preferredLocalization == NULL)
45 preferredLocalization = (CFStringRef)CFDictionaryGetValue(localizations, (CFStringRef)locales[0]);
46 }
47
48 free(locales);
49 // NSLog(@"%@", preferredLocalization);
50 return preferredLocalization;
51}
52
53NSArray *CFServiceControllerCopyServicesEntries(void);
54
55NSDictionary *ICCF_GetServicesInfo(void) {
56 NSArray *services = CFServiceControllerCopyServicesEntries();
57
58 NSEnumerator *e = [services objectEnumerator];
59 NSDictionary *serviceEntry;
60 NSMutableDictionary *serviceDict = [[NSMutableDictionary alloc] init];
61 while ( (serviceEntry = (NSDictionary *)[e nextObject]) != nil) {
62 // XXX once tested, redo all this with CF, and no autoreleasing
63 // XXX items named the same as submenus (figure out how Cocoa itself does it, too)
64 NSString *itemPath = (NSString *)preferredLocalization((CFDictionaryRef)[serviceEntry objectForKey: @"NSMenuItem"]);
65 if (itemPath == nil) continue;
66
67 NSString *bundlePath = [serviceEntry objectForKey: @"NSBundlePath"];
68 BOOL bubbledUp = (bundlePath == nil);
69 NSArray *itemComponents = [itemPath componentsSeparatedByString: @"/"];
70 NSEnumerator *ce = [itemComponents objectEnumerator];
71 NSString *itemComponent;
72 NSMutableDictionary *levelDict = serviceDict;
73 NSMutableDictionary *itemDict = nil;
74 while ( (itemComponent = (NSString *)[ce nextObject]) != nil) {
75 // levelDict is nil if just created
76 if (levelDict != nil && !bubbledUp) {
77 NSString *oldBundlePath = [itemDict objectForKey: (NSString *)kICServiceBundlePath];
78 if ([oldBundlePath isEqualToString: bundlePath])
79 bubbledUp = YES;
80 else if (oldBundlePath != nil) {
81 [oldBundlePath retain];
82 [itemDict removeObjectForKey: (NSString *)kICServiceBundlePath];
83 NSEnumerator *be = [levelDict objectEnumerator];
84 while ( (itemDict = (NSMutableDictionary *)[be nextObject]) != nil)
85 [itemDict setObject: oldBundlePath forKey: (NSString *)kICServiceBundlePath];
86 [oldBundlePath release];
87 }
88 }
89 if (levelDict == nil) {
90 levelDict = [[NSMutableDictionary alloc] init];
91 [itemDict setObject: levelDict forKey: (NSString *)kICServiceSubmenu];
92 [levelDict release];
93 itemDict = nil;
94 } else {
95 itemDict = [levelDict objectForKey: itemComponent];
96 }
97 if (itemDict == nil) {
98 itemDict = [[NSMutableDictionary alloc] init];
99 if (!bubbledUp) {
100 [itemDict setObject: bundlePath forKey: (NSString *)kICServiceBundlePath];
101 bubbledUp = YES;
102 }
103 [levelDict setObject: itemDict forKey: itemComponent];
104 levelDict = nil;
105 [itemDict release];
106 } else {
107 levelDict = [itemDict objectForKey: (NSString *)kICServiceSubmenu];
108 }
109 }
110
111 if (!bubbledUp)
112 [itemDict setObject: bundlePath forKey: (NSString *)kICServiceBundlePath];
113
114 NSString *keyEquivalent = (NSString *)preferredLocalization((CFDictionaryRef)[serviceEntry objectForKey: @"NSKeyEquivalent"]);
115 if (keyEquivalent == nil) continue;
116
117 [itemDict setObject: keyEquivalent forKey: (NSString *)kICServiceShortcut];
118 }
119 [services release];
120
121 return [serviceDict autorelease];
122}
123
124
125void ICCF_SetServicesMenu(NSMenu *servicesMenu) {
126 // in 10.3, this populates the menu; in 10.4, it attaches a delegate (NSServiceMaster)
127 [[NSApplication sharedApplication] setServicesMenu: servicesMenu];
128 id delegate;
129 if ( (delegate = [servicesMenu delegate]) != nil) {
130 // populate menu so we have something to filter
131 if ([delegate respondsToSelector: @selector(menuNeedsUpdate:)]) {
132 [delegate menuNeedsUpdate: servicesMenu];
133 }
134 if ([delegate respondsToSelector: @selector(menu:updateItem:atIndex:shouldCancel:)]) {
135 int itemCount = [delegate numberOfItemsInMenu: servicesMenu];
136 for (int i = 0 ; i < itemCount ; i++) {
137 [servicesMenu addItemWithTitle: @"" action: NULL keyEquivalent: @""];
138 }
139 for (int i = 0 ; i < itemCount ; i++) {
140 if (![delegate menu: servicesMenu updateItem: (NSMenuItem *)[servicesMenu itemAtIndex: i] atIndex: i shouldCancel: NO])
141 break;
142 }
143 }
144 }
145}
146
147IconRef ICCF_CopyIconRefForPath(NSString *path) {
148 IconRef icon;
149 FSRef fsr;
150 SInt16 label;
151 OSStatus err = noErr;
152
153 err = FSPathMakeRef((const UInt8 *)[path fileSystemRepresentation], &fsr, NULL);
154 if (err != noErr) return NULL;
155
156 err = GetIconRefFromFileInfo(&fsr, 0, NULL, kFSCatInfoNone, NULL, kIconServicesNormalUsageFlag, &icon, &label);
157 if (err != noErr) return NULL;
158
159 return icon;
160}
Note: See TracBrowser for help on using the repository browser.