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

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

ICeCoffEE.[hm]: Move parsing functions (ICCF_CheckRange,
ICCF_Delimiters, ICCF_ParseURL) and Internet Config start/stop
routines (ICCF_Stop/StartIC) to ICeCoffEEParser.[hm] so they can be
tested outside the APE. Also move ICCF_MAX_URL_LEN definition, and
extract guts of NSTextView parsing into ICCF_URLEnclosingRange.
Remove comment about TXNClick; if MLTE is deprecated I'm not going to
mess with it.

ICeCoffEEParser.[hm]: Moved everything discussed above to here.

ICeCoffEEServices.m: Some comments, now I realize how irritating the
service localization problem is.

ICeCoffEETerminal.m: Remove long-unused reference to
ICeCoffEEScanner.h (was from 1.2?).

ICeCoffEEScanner.[hm]: Removed, no longer in use.

TestParser.m: Very simple first pass at testing. There's much more I
want to do here.

urls.plist: First pass at URL test cases.

ICeCoffEE.xcodeproj: Add TestParser target (yes, it uses ZeroLink
because my machine is slow and it actually helps).

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 // 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
148IconRef 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}
Note: See TracBrowser for help on using the repository browser.