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

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