/* * ICeCoffEEActionMenu.c * ICeCoffEE APE * * Created by Nicholas Riley on Wed Jan 29 2003. * Copyright (c) 2003 Nicholas Riley. All rights reserved. * */ #include "ICeCoffEEActionMenu.h" #include "ICeCoffEEConfig.h" #include "ICeCoffEEBookmarks.h" /* thanks to Slava Karpenko for this one! */ OSStatus _LSCopyApplicationURLsForItemURL(CFURLRef inURL, LSRolesMask inRoleMask, CFArrayRef *outApps); // outApps to be CFReleased() #define THROW_ERR(e) { err = e; goto END; } static CFStringRef ICCF_NameWithLocation(CFStringRef name, CFURLRef url) { CFURLRef urlMinus = CFURLCreateCopyDeletingLastPathComponent(NULL, url); CFStringRef urlString = CFURLCopyFileSystemPath(urlMinus, kCFURLPOSIXPathStyle); CFStringRef nameWithLocation = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ %s %@"), name, "Ñ", urlString); SAFE_RELEASE(urlMinus); SAFE_RELEASE(urlString); return nameWithLocation; } static const MenuItemIndex kICAppMenuItemHasPath = 0xfffe; typedef struct { CFURLRef defaultAppURL; // URL of default app, set to NULL after added to menu CFMutableSetRef appPaths; 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) MenuRef menu; } icAppMenuContext; static OSStatus ICCF_AddAppToMenu(icAppMenuContext *ctx, CFURLRef appURL, MenuCommand menuCommand) { CFStringRef appName = NULL, appItemTitle = NULL; CFBundleRef appBundle = NULL; IconRef appIcon = NULL; FSRef appRef; SInt16 label; MenuItemIndex menuItemIndex; OSStatus err = noErr; CFStringRef appPath = CFURLCopyPath(appURL); // only one entry for each path if (CFSetContainsValue(ctx->appPaths, appPath)) return noErr; CFSetAddValue(ctx->appPaths, appPath); if ( (err = LSCopyDisplayNameForURL(appURL, &appName)) != noErr) return err; menuItemIndex = (long)CFDictionaryGetValue(ctx->appNames, appName); if (menuItemIndex != 0) { CFURLRef sameAppRef; if (menuItemIndex != kICAppMenuItemHasPath && GetMenuItemRefCon(ctx->menu, menuItemIndex, (void *)&sameAppRef) == noErr) { CFStringRef sameAppItemTitle = NULL; if ( (err = CopyMenuItemTextAsCFString(ctx->menu, menuItemIndex, &sameAppItemTitle)) != noErr) return err; CFStringRef appItemTitleWithVersion = ICCF_NameWithLocation(sameAppItemTitle, sameAppRef); SetMenuItemTextWithCFString(ctx->menu, menuItemIndex, appItemTitleWithVersion); SAFE_RELEASE(appItemTitleWithVersion); CFDictionarySetValue(ctx->appNames, appName, (void *)(long)kICAppMenuItemHasPath); } } CFRetain(appName); appItemTitle = appName; if ( (appBundle = CFBundleCreate(NULL, appURL)) != NULL) { // prefer a short version string, e.g. "1.0 Beta" instead of "51" for Safari CFStringRef appVersion = CFBundleGetValueForInfoDictionaryKey(appBundle, CFSTR("CFBundleShortVersionString")); if (appVersion == NULL) appVersion = CFBundleGetValueForInfoDictionaryKey(appBundle, kCFBundleVersionKey); if (appVersion != NULL) { appItemTitle = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@)"), appName, appVersion); CFRelease(appName); } CFRelease(appBundle); } if (menuItemIndex != 0) { CFStringRef appItemTitleWithVersion = ICCF_NameWithLocation(appItemTitle, appURL); CFRelease(appItemTitle); appItemTitle = appItemTitleWithVersion; } if (ctx->defaultAppURL != NULL && CFEqual(appURL, ctx->defaultAppURL)) { CFStringRef defaultFormat = ICCF_CopyLocalizedString(CFSTR("DefaultApp%@")); CFStringRef appItemTitleWithDefault = CFStringCreateWithFormat(NULL, NULL, defaultFormat, appItemTitle); CFRelease(appItemTitle); appItemTitle = appItemTitleWithDefault; ctx->defaultAppURL = NULL; } err = AppendMenuItemTextWithCFString(ctx->menu, appItemTitle, 0, 0, &menuItemIndex); CFRelease(appItemTitle); if (err != noErr) return err; if (!CFDictionaryContainsKey(ctx->appNames, appName)) { CFDictionarySetValue(ctx->appNames, appName, (void *)(long)menuItemIndex); CFRelease(appName); } if (!CFURLGetFSRef(appURL, &appRef)) return paramErr; err = GetIconRefFromFileInfo(&appRef, 0, NULL, kFSCatInfoNone, NULL, kIconServicesNormalUsageFlag, &appIcon, &label); if (err != noErr) return err; SetMenuItemIconHandle(ctx->menu, menuItemIndex, kMenuIconRefType, (Handle)appIcon); SetMenuItemCommandID(ctx->menu, menuItemIndex, menuCommand); SetMenuItemRefCon(ctx->menu, menuItemIndex, (UInt32)appURL); ReleaseIconRef(appIcon); return err; } enum { kICURLActionOpenWith = 'OpnW', kICURLActionAddBookmark = 'AddB' }; OSStatus ICCF_DoURLActionMenu(ICInstance inst, ConstStr255Param hint, const char *urlData, long startIndex, long endIndex) { Handle h = NewHandle(0); CFURLRef url = NULL; CFArrayRef appURLs = NULL; // matching app URLs CFArrayRef urlArray = NULL; // single-URL array icAppMenuContext ctx = {NULL, NULL, NULL, NULL}; OSStatus err; if (h == NULL) return MemError(); if ( (err = ICParseURL(inst, hint, urlData + startIndex, endIndex - startIndex + 1, &startIndex, &endIndex, h)) != noErr) THROW_ERR(err); if ( (url = CFURLCreateWithBytes(NULL, *h, GetHandleSize(h), kCFStringEncodingASCII, NULL)) == NULL) THROW_ERR(paramErr); CFIndex appCount = 0; if ( (err = _LSCopyApplicationURLsForItemURL(url, kLSRolesAll, &appURLs)) != noErr) THROW_ERR(err); if (appURLs == NULL || (appCount = CFArrayGetCount(appURLs)) == 0) THROW_ERR(kLSApplicationNotFoundErr); if ( (ctx.appPaths = CFSetCreateMutable(NULL, appCount, &kCFCopyStringSetCallBacks)) == NULL) THROW_ERR(memFullErr); // values: index of single item with name; 0/NULL if no name; kICAppMenuItemHasPath if multiple items have name if ( (ctx.appNames = CFDictionaryCreateMutable(NULL, appCount, &kCFCopyStringDictionaryKeyCallBacks, NULL)) == NULL) THROW_ERR(memFullErr); if ( (err = CreateNewMenu(0, kMenuAttrExcludesMarkColumn, &ctx.menu)) != noErr) THROW_ERR(err); MenuItemIndex menuItemIndex; if ( (err = AppendMenuItemTextWithCFString(ctx.menu, ICCF_CopyLocalizedString(CFSTR("Open Location With")), kMenuItemAttrDisabled, 0, &menuItemIndex)) != noErr) THROW_ERR(err); if ( (urlArray = CFArrayCreate(NULL, (const void **)&url, 1, &kCFTypeArrayCallBacks)) == NULL) THROW_ERR(memFullErr); LSGetApplicationForURL(url, kLSRolesAll, NULL, &ctx.defaultAppURL); CFIndex appIndex; CFURLRef appURL; for (appIndex = 0 ; appIndex < appCount ; appIndex++) { appURL = CFArrayGetValueAtIndex(appURLs, appIndex); if ( (err = ICCF_AddAppToMenu(&ctx, appURL, kICURLActionOpenWith)) != noErr) THROW_ERR(err); } // sometimes the default protocol handler won't be on the list because it doesnÕt claim to handle that protocol; add it anyway if (ctx.defaultAppURL != NULL) { if ( (err = ICCF_AddAppToMenu(&ctx, ctx.defaultAppURL, kICURLActionOpenWith)) != noErr) THROW_ERR(err); } appURL = ICCF_GetBookmarkHelperURL(inst); if (appURL != NULL) { if ( (err = AppendMenuItemTextWithCFString(ctx.menu, CFSTR(""), kMenuItemAttrSeparator, 0, NULL)) != noErr) THROW_ERR(err); if ( (err = AppendMenuItemTextWithCFString(ctx.menu, ICCF_CopyLocalizedString(CFSTR("Add Bookmark")), kMenuItemAttrDisabled, 0, &menuItemIndex)) != noErr) THROW_ERR(err); if ( (err = ICCF_AddAppToMenu(&ctx, appURL, kICURLActionAddBookmark)) != noErr) THROW_ERR(err); } InsertMenu(ctx.menu, -1); Point mousePoint; GetGlobalMouse(&mousePoint); long selectedAppIndex = PopUpMenuSelect(ctx.menu, mousePoint.v + 18, mousePoint.h - 30, 0/*popUpItem*/); if (selectedAppIndex == 0) { err = userCanceledErr; } else { CFURLRef appURL = NULL; MenuCommand menuCommand; err = GetMenuItemRefCon(ctx.menu, selectedAppIndex, (void *)&appURL); if (err == noErr) { err = GetMenuItemCommandID(ctx.menu, selectedAppIndex, &menuCommand); if (err == noErr) { if (menuCommand == kICURLActionOpenWith) { LSLaunchURLSpec lsSpec = {appURL, urlArray, NULL, kLSLaunchDefaults}; err = LSOpenFromURLSpec(&lsSpec, NULL); } else if (menuCommand == kICURLActionAddBookmark) { err = ICCF_DoBookmarkDialog(inst, CFURLGetString(url)); } } } } END: if (h != NULL) DisposeHandle(h); if (ctx.menu != NULL) { DeleteMenu(GetMenuID(ctx.menu)); DisposeMenu(ctx.menu); } SAFE_RELEASE(url); SAFE_RELEASE(urlArray); SAFE_RELEASE(appURLs); SAFE_RELEASE(ctx.appNames); SAFE_RELEASE(ctx.appPaths); return err; }