source: trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEBookmarks.c@ 88

Last change on this file since 88 was 82, checked in by Nicholas Riley, 21 years ago

ICeCoffEE 1.3

File size: 7.9 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 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
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
148OSStatus 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);
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);
181 CFUserNotificationRef notification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationPlainAlertLevel, &err, dict);
182 dup2(errfd, STDERR_FILENO);
183 close(errfd);
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}
Note: See TracBrowser for help on using the repository browser.