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

Last change on this file since 435 was 322, checked in by Nicholas Riley, 13 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.