source: trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEActionMenu.c@ 82

Last change on this file since 82 was 82, checked in by Nicholas Riley, 20 years ago

ICeCoffEE 1.3

File size: 9.2 KB
Line 
1/*
2 * ICeCoffEEActionMenu.c
3 * ICeCoffEE APE
4 *
5 * Created by Nicholas Riley on Wed Jan 29 2003.
6 * Copyright (c) 2003 Nicholas Riley. All rights reserved.
7 *
8 */
9
10#include "ICeCoffEEActionMenu.h"
11#include "ICeCoffEEConfig.h"
12#include "ICeCoffEEBookmarks.h"
13
14/* thanks to Slava Karpenko for this one! */
15OSStatus _LSCopyApplicationURLsForItemURL(CFURLRef inURL, LSRolesMask inRoleMask, CFArrayRef *outApps); // outApps to be CFReleased()
16
17#define THROW_ERR(e) { err = e; goto END; }
18
19static CFStringRef ICCF_NameWithLocation(CFStringRef name, CFURLRef url) {
20 CFURLRef urlMinus = CFURLCreateCopyDeletingLastPathComponent(NULL, url);
21 CFStringRef urlString = CFURLCopyFileSystemPath(urlMinus, kCFURLPOSIXPathStyle);
22 CFStringRef nameWithLocation = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ %s %@"), name, "Ñ", urlString);
23
24 SAFE_RELEASE(urlMinus);
25 SAFE_RELEASE(urlString);
26 return nameWithLocation;
27}
28
29static const MenuItemIndex kICAppMenuItemHasPath = 0xfffe;
30
31typedef struct {
32 CFURLRef defaultAppURL; // URL of default app, set to NULL after added to menu
33 CFMutableSetRef appPaths;
34 CFMutableDictionaryRef appNames; // keys: app display names (CFString), values: MenuItemIndex of matching item without path appended, or kICAppMenuItemHasPath if already appended (at least 2 items with this name exist)
35 MenuRef menu;
36} icAppMenuContext;
37
38static OSStatus ICCF_AddAppToMenu(icAppMenuContext *ctx, CFURLRef appURL, MenuCommand menuCommand) {
39 CFStringRef appName = NULL, appItemTitle = NULL;
40 CFBundleRef appBundle = NULL;
41 IconRef appIcon = NULL;
42 FSRef appRef;
43 SInt16 label;
44 MenuItemIndex menuItemIndex;
45 OSStatus err = noErr;
46
47 CFStringRef appPath = CFURLCopyPath(appURL);
48 // only one entry for each path
49 if (CFSetContainsValue(ctx->appPaths, appPath))
50 return noErr;
51 CFSetAddValue(ctx->appPaths, appPath);
52
53 if ( (err = LSCopyDisplayNameForURL(appURL, &appName)) != noErr)
54 return err;
55
56 menuItemIndex = (long)CFDictionaryGetValue(ctx->appNames, appName);
57 if (menuItemIndex != 0) {
58 CFURLRef sameAppRef;
59 if (menuItemIndex != kICAppMenuItemHasPath &&
60 GetMenuItemRefCon(ctx->menu, menuItemIndex, (void *)&sameAppRef) == noErr) {
61 CFStringRef sameAppItemTitle = NULL;
62 if ( (err = CopyMenuItemTextAsCFString(ctx->menu, menuItemIndex, &sameAppItemTitle)) != noErr)
63 return err;
64 CFStringRef appItemTitleWithVersion = ICCF_NameWithLocation(sameAppItemTitle, sameAppRef);
65 SetMenuItemTextWithCFString(ctx->menu, menuItemIndex, appItemTitleWithVersion);
66 SAFE_RELEASE(appItemTitleWithVersion);
67 CFDictionarySetValue(ctx->appNames, appName, (void *)(long)kICAppMenuItemHasPath);
68 }
69 }
70
71 CFRetain(appName);
72 appItemTitle = appName;
73
74 if ( (appBundle = CFBundleCreate(NULL, appURL)) != NULL) {
75 // prefer a short version string, e.g. "1.0 Beta" instead of "51" for Safari
76 CFStringRef appVersion = CFBundleGetValueForInfoDictionaryKey(appBundle, CFSTR("CFBundleShortVersionString"));
77 if (appVersion == NULL)
78 appVersion = CFBundleGetValueForInfoDictionaryKey(appBundle, kCFBundleVersionKey);
79 if (appVersion != NULL) {
80 appItemTitle = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@)"), appName, appVersion);
81 CFRelease(appName);
82 }
83 CFRelease(appBundle);
84 }
85
86 if (menuItemIndex != 0) {
87 CFStringRef appItemTitleWithVersion = ICCF_NameWithLocation(appItemTitle, appURL);
88 CFRelease(appItemTitle);
89 appItemTitle = appItemTitleWithVersion;
90 }
91
92 if (ctx->defaultAppURL != NULL && CFEqual(appURL, ctx->defaultAppURL)) {
93 CFStringRef defaultFormat = ICCF_CopyLocalizedString(CFSTR("DefaultApp%@"));
94 CFStringRef appItemTitleWithDefault = CFStringCreateWithFormat(NULL, NULL, defaultFormat, appItemTitle);
95 CFRelease(appItemTitle);
96 appItemTitle = appItemTitleWithDefault;
97 ctx->defaultAppURL = NULL;
98 }
99
100 err = AppendMenuItemTextWithCFString(ctx->menu, appItemTitle, 0, 0, &menuItemIndex);
101 CFRelease(appItemTitle);
102 if (err != noErr) return err;
103
104 if (!CFDictionaryContainsKey(ctx->appNames, appName)) {
105 CFDictionarySetValue(ctx->appNames, appName, (void *)(long)menuItemIndex);
106 CFRelease(appName);
107 }
108
109 if (!CFURLGetFSRef(appURL, &appRef)) return paramErr;
110 err = GetIconRefFromFileInfo(&appRef, 0, NULL, kFSCatInfoNone, NULL, kIconServicesNormalUsageFlag, &appIcon, &label);
111 if (err != noErr) return err;
112
113 SetMenuItemIconHandle(ctx->menu, menuItemIndex, kMenuIconRefType, (Handle)appIcon);
114 SetMenuItemCommandID(ctx->menu, menuItemIndex, menuCommand);
115 SetMenuItemRefCon(ctx->menu, menuItemIndex, (UInt32)appURL);
116 ReleaseIconRef(appIcon);
117
118 return err;
119}
120
121enum {
122 kICURLActionOpenWith = 'OpnW',
123 kICURLActionAddBookmark = 'AddB'
124};
125
126OSStatus ICCF_DoURLActionMenu(ICInstance inst, ConstStr255Param hint, const char *urlData, long startIndex, long endIndex) {
127 Handle h = NewHandle(0);
128 CFURLRef url = NULL;
129 CFArrayRef appURLs = NULL; // matching app URLs
130 CFArrayRef urlArray = NULL; // single-URL array
131 icAppMenuContext ctx = {NULL, NULL, NULL, NULL};
132 OSStatus err;
133
134 if (h == NULL) return MemError();
135
136 if ( (err = ICParseURL(inst, hint, urlData + startIndex, endIndex - startIndex + 1,
137 &startIndex, &endIndex, h)) != noErr) THROW_ERR(err);
138
139 if ( (url = CFURLCreateWithBytes(NULL, *h, GetHandleSize(h), kCFStringEncodingASCII,
140 NULL)) == NULL) THROW_ERR(paramErr);
141
142 CFIndex appCount = 0;
143 if ( (err = _LSCopyApplicationURLsForItemURL(url, kLSRolesAll, &appURLs)) != noErr)
144 THROW_ERR(err);
145
146 if (appURLs == NULL || (appCount = CFArrayGetCount(appURLs)) == 0)
147 THROW_ERR(kLSApplicationNotFoundErr);
148
149 if ( (ctx.appPaths = CFSetCreateMutable(NULL, appCount, &kCFCopyStringSetCallBacks)) == NULL)
150 THROW_ERR(memFullErr);
151
152 // values: index of single item with name; 0/NULL if no name; kICAppMenuItemHasPath if multiple items have name
153 if ( (ctx.appNames = CFDictionaryCreateMutable(NULL, appCount, &kCFCopyStringDictionaryKeyCallBacks, NULL)) == NULL)
154 THROW_ERR(memFullErr);
155
156 if ( (err = CreateNewMenu(0, kMenuAttrExcludesMarkColumn, &ctx.menu)) != noErr)
157 THROW_ERR(err);
158
159 MenuItemIndex menuItemIndex;
160 if ( (err = AppendMenuItemTextWithCFString(ctx.menu, ICCF_CopyLocalizedString(CFSTR("Open Location With")), kMenuItemAttrDisabled, 0, &menuItemIndex)) != noErr)
161 THROW_ERR(err);
162
163 if ( (urlArray = CFArrayCreate(NULL, (const void **)&url, 1, &kCFTypeArrayCallBacks)) == NULL)
164 THROW_ERR(memFullErr);
165
166 LSGetApplicationForURL(url, kLSRolesAll, NULL, &ctx.defaultAppURL);
167
168 CFIndex appIndex;
169 CFURLRef appURL;
170
171 for (appIndex = 0 ; appIndex < appCount ; appIndex++) {
172 appURL = CFArrayGetValueAtIndex(appURLs, appIndex);
173
174 if ( (err = ICCF_AddAppToMenu(&ctx, appURL, kICURLActionOpenWith)) != noErr)
175 THROW_ERR(err);
176 }
177 // sometimes the default protocol handler won't be on the list because it doesnÕt claim to handle that protocol; add it anyway
178 if (ctx.defaultAppURL != NULL) {
179 if ( (err = ICCF_AddAppToMenu(&ctx, ctx.defaultAppURL, kICURLActionOpenWith)) != noErr)
180 THROW_ERR(err);
181 }
182
183 appURL = ICCF_GetBookmarkHelperURL(inst);
184 if (appURL != NULL) {
185 if ( (err = AppendMenuItemTextWithCFString(ctx.menu, CFSTR(""), kMenuItemAttrSeparator, 0, NULL)) != noErr)
186 THROW_ERR(err);
187 if ( (err = AppendMenuItemTextWithCFString(ctx.menu, ICCF_CopyLocalizedString(CFSTR("Add Bookmark")), kMenuItemAttrDisabled, 0, &menuItemIndex)) != noErr)
188 THROW_ERR(err);
189 // the app won't show up at the bottom if someone cmd-option-clicks on 'bookmark:', but they'd have to be crazy to do that anyway
190 if ( (err = ICCF_AddAppToMenu(&ctx, appURL, kICURLActionAddBookmark)) != noErr)
191 THROW_ERR(err);
192 }
193
194 InsertMenu(ctx.menu, -1);
195 Point mousePoint;
196 GetGlobalMouse(&mousePoint);
197 long selectedAppIndex = PopUpMenuSelect(ctx.menu, mousePoint.v + 18, mousePoint.h - 30, 0/*popUpItem*/);
198 if (selectedAppIndex == 0) {
199 err = userCanceledErr;
200 } else {
201 CFURLRef appURL = NULL;
202 MenuCommand menuCommand;
203 err = GetMenuItemRefCon(ctx.menu, selectedAppIndex, (void *)&appURL);
204 if (err == noErr) {
205 err = GetMenuItemCommandID(ctx.menu, selectedAppIndex, &menuCommand);
206 if (err == noErr) {
207 if (menuCommand == kICURLActionOpenWith) {
208 LSLaunchURLSpec lsSpec = {appURL, urlArray, NULL, kLSLaunchDefaults};
209 err = LSOpenFromURLSpec(&lsSpec, NULL);
210 } else if (menuCommand == kICURLActionAddBookmark) {
211 err = ICCF_DoBookmarkDialog(inst, CFURLGetString(url));
212 }
213 }
214 }
215 }
216
217END:
218 if (h != NULL) DisposeHandle(h);
219 if (ctx.menu != NULL) {
220 DeleteMenu(GetMenuID(ctx.menu));
221 DisposeMenu(ctx.menu);
222 }
223 SAFE_RELEASE(url);
224 SAFE_RELEASE(urlArray);
225 SAFE_RELEASE(appURLs);
226 SAFE_RELEASE(ctx.appNames);
227 SAFE_RELEASE(ctx.appPaths);
228
229 return err;
230}
Note: See TracBrowser for help on using the repository browser.