source: trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEBookmarks.c

Last change on this file was 464, checked in by Nicholas Riley, 13 years ago

CFUserNotification changes: info icon no longer displayed, so display bookmark helper app icon; remove no-longer-necessary CFUserNotificationCreate CFLog suppression.

File size: 8.3 KB
Line 
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
17enum {
18    kICBookmarkURLTooLongErr = 93800,
19    kICBookmarkNameTooLongErr = 93801,
20    kICBookmarkDescriptionTooLongErr = 93802,
21    kICBookmarkNoHelperErr = 93803
22};
23
24CFURLRef 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
48static const ProcessSerialNumber kICNoProcess = {0, kNoProcess};
49
50static 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
88static 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    (void)AEDisposeDesc(&event);
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
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
149OSStatus 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
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
178    const CFStringRef textFieldTitles[] = { ICCF_CopyLocalizedString(CFSTR("Location:")), ICCF_CopyLocalizedString(CFSTR("Name:")), ICCF_CopyLocalizedString(CFSTR("Description:")) };
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
183    CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, ICCF_CopyLocalizedString(CFSTR("Add Bookmark")));
184    CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, ICCF_CopyLocalizedString(CFSTR("Bookmark name and description are optional.")));
185    CFDictionaryAddValue(dict, kCFUserNotificationTextFieldTitlesKey, textFieldTitlesArray);
186    CFDictionaryAddValue(dict, kCFUserNotificationTextFieldValuesKey, textFieldValuesArray);
187    CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, ICCF_CopyLocalizedString(CFSTR("Add")));
188    CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, ICCF_CopyLocalizedString(CFSTR("Cancel")));
189    if (textFieldTitlesArray != NULL) CFRelease(textFieldTitlesArray);
190    if (textFieldValuesArray != NULL) CFRelease(textFieldValuesArray);
191
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}
Note: See TracBrowser for help on using the repository browser.