[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);
|
---|
| 135 | if (err != noErr) return err;
|
---|
| 136 |
|
---|
| 137 | (void)AEDisposeDesc(&nullReply); // according to docs, don't call unless AESend returned successfully
|
---|
| 138 |
|
---|
| 139 | return noErr;
|
---|
| 140 | }
|
---|
| 141 |
|
---|
[82] | 142 | // XXX workaround for CFUserNotificationCreate log message in 10.2.4 at least
|
---|
| 143 | #include <stdio.h>
|
---|
| 144 | #include <unistd.h>
|
---|
| 145 | #include <fcntl.h>
|
---|
| 146 | // XXX end workaround
|
---|
| 147 |
|
---|
[75] | 148 | OSStatus ICCF_DoBookmarkDialog(ICInstance inst, CFStringRef url) {
|
---|
| 149 |
|
---|
| 150 | CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 10, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
---|
| 151 |
|
---|
| 152 | if (dict == NULL) return memFullErr;
|
---|
| 153 |
|
---|
| 154 | OSStatus err;
|
---|
| 155 |
|
---|
| 156 | CFURLRef bundleURL = CFBundleCopyBundleURL(ICCF_bundle);
|
---|
| 157 | CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, bundleURL);
|
---|
| 158 | SAFE_RELEASE(bundleURL);
|
---|
| 159 |
|
---|
| 160 | const CFStringRef textFieldTitles[] = { CFSTR("Location:"), CFSTR("Name:"), CFSTR("Description:") };
|
---|
| 161 | const CFStringRef textFieldValues[] = { url, CFSTR(""), CFSTR("") };
|
---|
| 162 | CFArrayRef textFieldTitlesArray = CFArrayCreate(NULL, (const void **)&textFieldTitles, 3, &kCFTypeArrayCallBacks);
|
---|
| 163 | CFArrayRef textFieldValuesArray = CFArrayCreate(NULL, (const void **)&textFieldValues, 3, &kCFTypeArrayCallBacks);
|
---|
| 164 |
|
---|
| 165 | CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, CFSTR("Add Bookmark"));
|
---|
| 166 | CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, CFSTR("Bookmark name and description are optional."));
|
---|
| 167 | CFDictionaryAddValue(dict, kCFUserNotificationTextFieldTitlesKey, textFieldTitlesArray);
|
---|
| 168 | CFDictionaryAddValue(dict, kCFUserNotificationTextFieldValuesKey, textFieldValuesArray);
|
---|
| 169 | CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFSTR("Add"));
|
---|
| 170 | CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFSTR("Cancel"));
|
---|
| 171 | if (textFieldTitlesArray != NULL) CFRelease(textFieldTitlesArray);
|
---|
| 172 | if (textFieldValuesArray != NULL) CFRelease(textFieldValuesArray);
|
---|
[82] | 173 |
|
---|
| 174 | // XXX suppress log message from Apple's code:
|
---|
| 175 | // 2003-02-02 17:48:26.896 TextEdit[2790] CFLog (20): Add Bookmark: Bookmark name and description are optional.
|
---|
| 176 | int errfd = dup(STDERR_FILENO), nullfd = open("/dev/null", O_WRONLY, 0);
|
---|
| 177 | // need to have something open in STDERR_FILENO because if it isn't,
|
---|
| 178 | // CFLog will log to /dev/console
|
---|
| 179 | dup2(nullfd, STDERR_FILENO);
|
---|
| 180 | close(nullfd);
|
---|
[75] | 181 | CFUserNotificationRef notification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationPlainAlertLevel, &err, dict);
|
---|
[82] | 182 | dup2(errfd, STDERR_FILENO);
|
---|
| 183 | close(errfd);
|
---|
[75] | 184 | if (notification == NULL) {
|
---|
| 185 | CFRelease(dict);
|
---|
| 186 | return err;
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | CFOptionFlags responseFlags;
|
---|
| 190 | CFUserNotificationReceiveResponse(notification, 0, &responseFlags);
|
---|
| 191 | if (responseFlags != kCFUserNotificationDefaultResponse)
|
---|
| 192 | return userCanceledErr;
|
---|
| 193 |
|
---|
| 194 | CFDictionaryRef responseDict = CFUserNotificationGetResponseDictionary(notification);
|
---|
| 195 | if (responseDict == NULL)
|
---|
| 196 | return userCanceledErr;
|
---|
| 197 |
|
---|
| 198 | textFieldValuesArray = CFDictionaryGetValue(responseDict, kCFUserNotificationTextFieldValuesKey);
|
---|
| 199 | if (textFieldValuesArray == NULL)
|
---|
| 200 | return userCanceledErr;
|
---|
| 201 |
|
---|
| 202 | url = CFArrayGetValueAtIndex(textFieldValuesArray, 0);
|
---|
| 203 | CFStringRef name = CFArrayGetValueAtIndex(textFieldValuesArray, 1);
|
---|
| 204 | CFStringRef description = CFArrayGetValueAtIndex(textFieldValuesArray, 2);
|
---|
| 205 |
|
---|
| 206 | err = ICCF_AddBookmark(inst, url, name, description);
|
---|
| 207 |
|
---|
| 208 | SAFE_RELEASE(url);
|
---|
| 209 | SAFE_RELEASE(name);
|
---|
| 210 | SAFE_RELEASE(description);
|
---|
| 211 |
|
---|
| 212 | return err;
|
---|
| 213 | }
|
---|