/* * ICeCoffEEAppMenu.c * ICeCoffEE APE * * Created by Nicholas Riley on Wed Jan 29 2003. * Copyright (c) 2003 Nicholas Riley. All rights reserved. * */ #include "ICeCoffEEAppMenu.h" #include "ICeCoffEEConfig.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; } #define SAFE_RELEASE(e) { if (e != NULL) CFRelease(e); } 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; static CFStringRef OSStatus ICCF_LaunchURLWithApplication(ICInstance inst, ConstStr255Param hint, const char *urlData, long startIndex, long endIndex) { Handle h = NewHandle(0); CFURLRef url = NULL; CFURLRef defaultAppURL = NULL; CFArrayRef urlArray = NULL; CFArrayRef appURLs = NULL; CFMutableSetRef appPaths = NULL; CFMutableDictionaryRef appNames = NULL; MenuRef menu = 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 ( (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 ( (appNames = CFDictionaryCreateMutable(NULL, appCount, &kCFCopyStringDictionaryKeyCallBacks, NULL)) == NULL) THROW_ERR(memFullErr); if ( (err = CreateNewMenu(0, kMenuAttrExcludesMarkColumn, &menu)) != noErr) THROW_ERR(err); MenuItemIndex menuItemIndex; if ( (err = AppendMenuItemTextWithCFString(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, &defaultAppURL); CFIndex appIndex; for (appIndex = 0 ; appIndex < appCount ; appIndex++) { CFStringRef appName = NULL, appItemTitle = NULL; CFBundleRef appBundle = NULL; IconRef appIcon = NULL; FSRef appRef; CFURLRef appURL; SInt16 label; appURL = CFArrayGetValueAtIndex(appURLs, appIndex); CFStringRef appPath = CFURLCopyPath(appURL); if (CFSetContainsValue(appPaths, appPath)) continue; CFSetAddValue(appPaths, appPath); if ( (err = LSCopyDisplayNameForURL(appURL, &appName)) != noErr) THROW_ERR(err); menuItemIndex = (long)CFDictionaryGetValue(appNames, appName); if (menuItemIndex != 0) { CFURLRef sameAppRef; if (menuItemIndex != kICAppMenuItemHasPath && GetMenuItemRefCon(menu, menuItemIndex, (void *)&sameAppRef) == noErr) { CFStringRef sameAppItemTitle = NULL; if ( (err = CopyMenuItemTextAsCFString(menu, menuItemIndex, &sameAppItemTitle)) != noErr) THROW_ERR(err); CFStringRef appItemTitleWithVersion = ICCF_NameWithLocation(sameAppItemTitle, sameAppRef); SetMenuItemTextWithCFString(menu, menuItemIndex, appItemTitleWithVersion); SAFE_RELEASE(appItemTitleWithVersion); CFDictionarySetValue(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 (defaultAppURL != NULL && CFEqual(appURL, defaultAppURL)) { CFStringRef defaultFormat = ICCF_CopyLocalizedString(CFSTR("DefaultApp%@")); CFStringRef appItemTitleWithDefault = CFStringCreateWithFormat(NULL, NULL, defaultFormat, appItemTitle); CFRelease(appItemTitle); appItemTitle = appItemTitleWithDefault; defaultAppURL = NULL; } err = AppendMenuItemTextWithCFString(menu, appItemTitle, 0, 0, &menuItemIndex); CFRelease(appItemTitle); if (err != noErr) THROW_ERR(err); if (!CFDictionaryContainsKey(appNames, appName)) { CFDictionarySetValue(appNames, appName, (void *)(long)menuItemIndex); CFRelease(appName); } if (!CFURLGetFSRef(appURL, &appRef)) THROW_ERR(paramErr); err = GetIconRefFromFileInfo(&appRef, 0, NULL, kFSCatInfoNone, NULL, kIconServicesNormalUsageFlag, &appIcon, &label); if (err != noErr) THROW_ERR(err); SetMenuItemIconHandle(menu, menuItemIndex, kMenuIconRefType, (Handle)appIcon); SetMenuItemRefCon(menu, menuItemIndex, (UInt32)appURL); ReleaseIconRef(appIcon); } InsertMenu(menu, -1); Point mousePoint; GetGlobalMouse(&mousePoint); long selectedAppIndex = PopUpMenuSelect(menu, mousePoint.v + 18, mousePoint.h - 30, 0/*popUpItem*/); if (selectedAppIndex == 0) { err = userCanceledErr; } else { CFURLRef appURL = NULL; err = GetMenuItemRefCon(menu, selectedAppIndex, (void *)&appURL); if (err == noErr) { LSLaunchURLSpec lsSpec = {appURL, urlArray, NULL, kLSLaunchDefaults}; err = LSOpenFromURLSpec(&lsSpec, NULL); } } END: if (h != NULL) DisposeHandle(h); if (menu != NULL) { DeleteMenu(GetMenuID(menu)); DisposeMenu(menu); } SAFE_RELEASE(url); SAFE_RELEASE(urlArray); SAFE_RELEASE(appURLs); SAFE_RELEASE(appNames); SAFE_RELEASE(appPaths); return err; }