[75] | 1 | /*
|
---|
| 2 | * ICeCoffEEBookmarks.c
|
---|
| 3 | * ICeCoffEE APE
|
---|
| 4 | *
|
---|
| 5 | * Created by Nicholas Riley on Sat Feb 01 2003.
|
---|
| 6 | * Copyright (c) 2003 Nicholas Riley. All rights reserved.
|
---|
| 7 | *
|
---|
| 8 | */
|
---|
| 9 |
|
---|
| 10 | #include "ICeCoffEEBookmarks.h"
|
---|
| 11 | #include "ICeCoffEEConfig.h"
|
---|
| 12 |
|
---|
| 13 | // Make sure the encoding of this file is MacRoman, or this constant wonÕt work!
|
---|
| 14 | #define kICBookmarkHelper "\pHelper¥bookmark"
|
---|
| 15 | #define kICBookmarkScheme "bookmark"
|
---|
| 16 |
|
---|
| 17 | enum {
|
---|
| 18 | kICBookmarkURLTooLongErr = 93800,
|
---|
| 19 | kICBookmarkNameTooLongErr = 93801,
|
---|
| 20 | kICBookmarkDescriptionTooLongErr = 93802,
|
---|
| 21 | kICBookmarkNoHelperErr = 93803
|
---|
| 22 | };
|
---|
| 23 |
|
---|
| 24 | CFURLRef ICCF_GetBookmarkHelperURL(ICInstance inst) {
|
---|
| 25 | OSStatus err;
|
---|
| 26 | CFURLRef helperURL;
|
---|
| 27 |
|
---|
| 28 | // try to get creator from Internet Config
|
---|
| 29 | ICAttr junkAttr;
|
---|
| 30 | ICAppSpec helperSpec;
|
---|
| 31 | long helperSpecSize = sizeof(helperSpec);
|
---|
| 32 |
|
---|
| 33 | err = ICGetPref(inst, kICBookmarkHelper, &junkAttr, (void *)&helperSpec, &helperSpecSize);
|
---|
| 34 | if (err == noErr && helperSpecSize == sizeof(helperSpec)) {
|
---|
| 35 | err = LSFindApplicationForInfo(helperSpec.fCreator, NULL, NULL, NULL, &helperURL);
|
---|
| 36 | if (err == noErr) return helperURL;
|
---|
| 37 | }
|
---|
| 38 |
|
---|
| 39 | // try to use LaunchServices
|
---|
| 40 | CFURLRef bookmarkURL = CFURLCreateWithString(NULL, CFSTR(kICBookmarkScheme":"), NULL);
|
---|
| 41 | err = LSGetApplicationForURL(bookmarkURL, kLSRolesAll, NULL, &helperURL);
|
---|
| 42 | if (bookmarkURL != NULL) CFRelease(bookmarkURL);
|
---|
| 43 | if (err == noErr) return helperURL;
|
---|
| 44 |
|
---|
| 45 | return NULL;
|
---|
| 46 | }
|
---|
| 47 |
|
---|
| 48 | static const ProcessSerialNumber kICNoProcess = {0, kNoProcess};
|
---|
| 49 |
|
---|
| 50 | static OSStatus ICCF_GetPSNFromHelperURL(CFURLRef helperURL, ProcessSerialNumber *inPSN) {
|
---|
| 51 | if (helperURL == NULL) return kICBookmarkNoHelperErr;
|
---|
| 52 |
|
---|
| 53 | FSRef helperFSR;
|
---|
| 54 | if (!CFURLGetFSRef(helperURL, &helperFSR)) return kICBookmarkNoHelperErr;
|
---|
| 55 |
|
---|
| 56 | // locate running application
|
---|
| 57 | ProcessSerialNumber psn = kICNoProcess;
|
---|
| 58 | FSRef fsr;
|
---|
| 59 | OSStatus err;
|
---|
| 60 | while ( (err = GetNextProcess(&psn)) == noErr && psn.lowLongOfPSN != kNoProcess) {
|
---|
| 61 | err = GetProcessBundleLocation(&psn, &fsr);
|
---|
| 62 | if (err == noErr && FSCompareFSRefs(&fsr, &helperFSR) == noErr) {
|
---|
| 63 | *inPSN = psn;
|
---|
| 64 | return noErr;
|
---|
| 65 | }
|
---|
| 66 | }
|
---|
| 67 |
|
---|
| 68 | // launch application
|
---|
| 69 | FSSpec fss;
|
---|
| 70 | err = FSGetCatalogInfo(&helperFSR, kFSCatInfoNone, NULL, NULL, &fss, NULL);
|
---|
| 71 | if (err != noErr) return err;
|
---|
| 72 | LaunchParamBlockRec launchPB = {
|
---|
| 73 | 0, 0, extendedBlock, extendedBlockLen,
|
---|
| 74 | kNilOptions, launchNoFileFlags | launchContinue,
|
---|
| 75 | &fss, kICNoProcess, 0, 0, 0, NULL
|
---|
| 76 | };
|
---|
| 77 |
|
---|
| 78 | err = LaunchApplication(&launchPB);
|
---|
| 79 | if (err != noErr) {
|
---|
| 80 | if (err == paramErr) return userCanceledErr; // if app force quit while launching
|
---|
| 81 | return err;
|
---|
| 82 | }
|
---|
| 83 |
|
---|
| 84 | *inPSN = launchPB.launchProcessSN;
|
---|
| 85 | return noErr;
|
---|
| 86 | }
|
---|
| 87 |
|
---|
| 88 | static OSStatus ICCF_AddBookmark(ICInstance inst, CFStringRef url, CFStringRef name, CFStringRef description) {
|
---|
| 89 |
|
---|
| 90 | if (url == NULL || CFStringGetLength(url) == 0) return noErr;
|
---|
| 91 |
|
---|
| 92 | CFURLRef helperURL = ICCF_GetBookmarkHelperURL(inst);
|
---|
| 93 | if (helperURL == NULL) return kICBookmarkNoHelperErr;
|
---|
| 94 |
|
---|
| 95 | OSStatus err;
|
---|
| 96 | ProcessSerialNumber psn;
|
---|
| 97 | err = ICCF_GetPSNFromHelperURL(helperURL, &psn);
|
---|
| 98 | CFRelease(helperURL);
|
---|
| 99 | if (err != noErr) return err;
|
---|
| 100 | if (psn.highLongOfPSN == kICNoProcess.highLongOfPSN && psn.lowLongOfPSN == kICNoProcess.lowLongOfPSN)
|
---|
| 101 | return kICBookmarkNoHelperErr;
|
---|
| 102 |
|
---|
| 103 | AppleEvent event;
|
---|
| 104 | char buf[1024]; // XXX make length a constant
|
---|
| 105 |
|
---|
| 106 | if (!CFStringGetCString(url, buf, sizeof(buf), kCFStringEncodingASCII))
|
---|
| 107 | return kICBookmarkURLTooLongErr;
|
---|
| 108 |
|
---|
| 109 | // XXX can I specify Unicode text ('utxt'?) here to get internationalization support?
|
---|
| 110 | err = AEBuildAppleEvent('SURL', 'SURL', typeProcessSerialNumber, &psn, sizeof(psn),
|
---|
| 111 | kAutoGenerateReturnID, kAnyTransactionID,
|
---|
| 112 | &event, NULL,
|
---|
| 113 | "'----':TEXT(@)", buf);
|
---|
| 114 | if (err != noErr) return err;
|
---|
| 115 |
|
---|
| 116 | if (name != NULL) {
|
---|
| 117 | if (!CFStringGetCString(name, buf, sizeof(buf), CFStringGetSystemEncoding()))
|
---|
| 118 | return kICBookmarkNameTooLongErr;
|
---|
| 119 |
|
---|
| 120 | err = AEBuildParameters(&event, NULL, "'name':TEXT(@)", buf);
|
---|
| 121 | if (err != noErr) return err;
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | if (description != NULL) { // XXX 256 hard-coded from limit of URL Manager Pro notes field
|
---|
| 125 | if (!CFStringGetCString(description, buf, 256, CFStringGetSystemEncoding()))
|
---|
| 126 | return kICBookmarkDescriptionTooLongErr;
|
---|
| 127 |
|
---|
| 128 | err = AEBuildParameters(&event, NULL, "'clip':TEXT(@)", buf);
|
---|
| 129 | if (err != noErr) return err;
|
---|
| 130 | }
|
---|
| 131 |
|
---|
| 132 | AppleEvent nullReply = {typeNull, nil};
|
---|
| 133 |
|
---|
| 134 | err = AESend(&event, &nullReply, kAENoReply, kAENormalPriority, kNoTimeOut, nil, nil);
|
---|
[106] | 135 | (void)AEDisposeDesc(&event);
|
---|
[75] | 136 | if (err != noErr) return err;
|
---|
| 137 |
|
---|
| 138 | (void)AEDisposeDesc(&nullReply); // according to docs, don't call unless AESend returned successfully
|
---|
| 139 |
|
---|
| 140 | return noErr;
|
---|
| 141 | }
|
---|
| 142 |
|
---|
[82] | 143 | // XXX workaround for CFUserNotificationCreate log message in 10.2.4 at least
|
---|
| 144 | #include <stdio.h>
|
---|
| 145 | #include <unistd.h>
|
---|
| 146 | #include <fcntl.h>
|
---|
| 147 | // XXX end workaround
|
---|
| 148 |
|
---|
[75] | 149 | OSStatus ICCF_DoBookmarkDialog(ICInstance inst, CFStringRef url) {
|
---|
| 150 |
|
---|
| 151 | CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 10, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
---|
| 152 |
|
---|
| 153 | if (dict == NULL) return memFullErr;
|
---|
| 154 |
|
---|
| 155 | OSStatus err;
|
---|
| 156 |
|
---|
| 157 | CFURLRef bundleURL = CFBundleCopyBundleURL(ICCF_bundle);
|
---|
| 158 | CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, bundleURL);
|
---|
| 159 | SAFE_RELEASE(bundleURL);
|
---|
| 160 |
|
---|
[464] | 161 | // get the bookmark helper's icon
|
---|
| 162 | bundleURL = ICCF_GetBookmarkHelperURL(inst);
|
---|
| 163 | if (bundleURL == NULL) return kICBookmarkNoHelperErr;
|
---|
| 164 |
|
---|
| 165 | CFBundleRef bundle = CFBundleCreate(NULL, bundleURL);
|
---|
| 166 | SAFE_RELEASE(bundleURL);
|
---|
| 167 | if (bundle == NULL) return kICBookmarkNoHelperErr;
|
---|
| 168 |
|
---|
| 169 | CFStringRef iconFile = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleIconFile"));
|
---|
| 170 | if (iconFile != NULL) {
|
---|
| 171 | CFURLRef iconURL = CFBundleCopyResourceURL(bundle, iconFile, NULL, NULL);
|
---|
| 172 | if (iconURL != NULL) {
|
---|
| 173 | CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL);
|
---|
| 174 | CFRelease(iconURL);
|
---|
| 175 | }
|
---|
| 176 | }
|
---|
| 177 |
|
---|
[122] | 178 | const CFStringRef textFieldTitles[] = { ICCF_CopyLocalizedString(CFSTR("Location:")), ICCF_CopyLocalizedString(CFSTR("Name:")), ICCF_CopyLocalizedString(CFSTR("Description:")) };
|
---|
[75] | 179 | const CFStringRef textFieldValues[] = { url, CFSTR(""), CFSTR("") };
|
---|
| 180 | CFArrayRef textFieldTitlesArray = CFArrayCreate(NULL, (const void **)&textFieldTitles, 3, &kCFTypeArrayCallBacks);
|
---|
| 181 | CFArrayRef textFieldValuesArray = CFArrayCreate(NULL, (const void **)&textFieldValues, 3, &kCFTypeArrayCallBacks);
|
---|
| 182 |
|
---|
[122] | 183 | CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, ICCF_CopyLocalizedString(CFSTR("Add Bookmark")));
|
---|
| 184 | CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, ICCF_CopyLocalizedString(CFSTR("Bookmark name and description are optional.")));
|
---|
[75] | 185 | CFDictionaryAddValue(dict, kCFUserNotificationTextFieldTitlesKey, textFieldTitlesArray);
|
---|
| 186 | CFDictionaryAddValue(dict, kCFUserNotificationTextFieldValuesKey, textFieldValuesArray);
|
---|
[122] | 187 | CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, ICCF_CopyLocalizedString(CFSTR("Add")));
|
---|
| 188 | CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, ICCF_CopyLocalizedString(CFSTR("Cancel")));
|
---|
[75] | 189 | if (textFieldTitlesArray != NULL) CFRelease(textFieldTitlesArray);
|
---|
| 190 | if (textFieldValuesArray != NULL) CFRelease(textFieldValuesArray);
|
---|
[82] | 191 |
|
---|
[75] | 192 | CFUserNotificationRef notification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationPlainAlertLevel, &err, dict);
|
---|
| 193 | if (notification == NULL) {
|
---|
| 194 | CFRelease(dict);
|
---|
| 195 | return err;
|
---|
| 196 | }
|
---|
| 197 |
|
---|
| 198 | CFOptionFlags responseFlags;
|
---|
| 199 | CFUserNotificationReceiveResponse(notification, 0, &responseFlags);
|
---|
| 200 | if (responseFlags != kCFUserNotificationDefaultResponse)
|
---|
| 201 | return userCanceledErr;
|
---|
| 202 |
|
---|
| 203 | CFDictionaryRef responseDict = CFUserNotificationGetResponseDictionary(notification);
|
---|
| 204 | if (responseDict == NULL)
|
---|
| 205 | return userCanceledErr;
|
---|
| 206 |
|
---|
| 207 | textFieldValuesArray = CFDictionaryGetValue(responseDict, kCFUserNotificationTextFieldValuesKey);
|
---|
| 208 | if (textFieldValuesArray == NULL)
|
---|
| 209 | return userCanceledErr;
|
---|
| 210 |
|
---|
| 211 | url = CFArrayGetValueAtIndex(textFieldValuesArray, 0);
|
---|
| 212 | CFStringRef name = CFArrayGetValueAtIndex(textFieldValuesArray, 1);
|
---|
| 213 | CFStringRef description = CFArrayGetValueAtIndex(textFieldValuesArray, 2);
|
---|
| 214 |
|
---|
| 215 | err = ICCF_AddBookmark(inst, url, name, description);
|
---|
| 216 |
|
---|
| 217 | SAFE_RELEASE(url);
|
---|
| 218 | SAFE_RELEASE(name);
|
---|
| 219 | SAFE_RELEASE(description);
|
---|
| 220 |
|
---|
| 221 | return err;
|
---|
| 222 | }
|
---|