Changeset 74 for trunk/ICeCoffEE/ICeCoffEE/ICeCoffEE.m
- Timestamp:
- 01/30/03 05:31:29 (22 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/ICeCoffEE/ICeCoffEE/ICeCoffEE.m
r66 r74 6 6 - Carbon contextual menu plugin which presents Services (yah!) 7 7 for both files and text 8 - Carbon version9 - app exclusion list - make a pref pane (see AquaShade config)10 8 - if it's not a URL, try using TextExtras' open list 11 9 - John Hayes' suggestions 12 - ICeCoffEE 2 functionality (bookmark helper app for cmd-option-click) 13 - adjust URL blinking 14 15 Carbon support ideas: 16 TEClick - TextEdit 17 TXNClick - MLTE 18 ? ATSUI 19 WASTE has its own support 10 - _LSCopyApplicationURLsForItemURL - list apps 11 - Menu on command-option-click: add bookmark, open with other helper, pass to configurable service, ...? 12 - TXNClick - MLTE has its own support in Jaguar and later, but it's lousy 20 13 21 14 Done: 22 15 16 - TEClick - TextEdit 23 17 - flash on success (like BBEdit) 24 18 - display dialog on failure (decode OSStatus) 19 - adjust URL blinking 20 - app exclusion list - make a pref pane (see AquaShade config) 25 21 26 22 */ … … 30 26 #include <unistd.h> 31 27 #import "ICeCoffEESuper.h" 32 33 @implementation ICeCoffEE 34 35 typedef struct { 36 OSStatus status; 37 NSString * const desc; 38 } errRec, errList[]; 39 40 static errList ERRS = { 41 // Internet Config errors 42 { icPrefNotFoundErr, @"No helper application is defined for the selected URLÕs scheme (e.g. http:)" }, 43 { icNoURLErr, @"The selection is not a URL" }, 44 { icInternalErr, @"Internet Config experienced an internal error" }, 45 // Misc. errors 46 { paramErr, @"The selection is not a complete URL" }, 47 { 0, NULL } 48 }; 49 50 NSString *ICCF_ErrString(OSStatus err, NSString *context) { 51 errRec *rec; 52 NSString *errDesc = [NSString stringWithFormat: @"An unknown error occurred in %@", context]; 53 if (err == noErr) return nil; 54 for (rec = &(ERRS[0]) ; rec->status != 0 ; rec++) 55 if (rec->status == err) { 56 errDesc = rec->desc; 57 break; 58 } 28 #import "ICeCoffEEAppMenu.h" 29 30 iccfPrefRec ICCF_prefs; 31 32 NSString *ICCF_ErrString(OSStatus err, NSString *context) { 33 if (err == noErr || err == userCanceledErr) return nil; 34 35 NSString *errNum = [NSString stringWithFormat: @"%ld", err]; 36 NSString *errDesc = ICCF_LocalizedString(errNum); 37 38 if (errDesc == NULL || errDesc == errNum) 39 errDesc = [NSString stringWithFormat: ICCF_LocalizedString(@"An unknown error occurred in %@"), context]; 40 59 41 return [NSString stringWithFormat: @"%@ (%d)", errDesc, (int)err]; 60 42 } 61 43 44 CFStringRef ICCF_CopyErrString(OSStatus err, CFStringRef context) { 45 if (err == noErr || err == userCanceledErr) return NULL; 46 47 CFStringRef errNum = CFStringCreateWithFormat(NULL, NULL, CFSTR("%ld"), err); 48 CFStringRef errDesc = ICCF_CopyLocalizedString(errNum); 49 50 if (errDesc == NULL || errDesc == errNum) { 51 CFStringRef errDescFormat = ICCF_CopyLocalizedString(CFSTR("An unknown error occurred in %@")); 52 if (errDesc != NULL) CFRelease(errDesc); 53 errDesc = CFStringCreateWithFormat(NULL, NULL, errDescFormat, context); 54 } 55 56 CFStringRef errStr = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%d)"), errDesc, (int)err); 57 58 if (errNum != NULL) CFRelease(errNum); 59 if (errDesc != NULL) CFRelease(errDesc); 60 return errStr; 61 } 62 62 63 BOOL ICCF_EventIsCommandMouseDown(NSEvent *e) { 63 return ([e type] == NSLeftMouseDown && [e modifierFlags] == NSCommandKeyMask && [e clickCount] == 1); 64 unsigned int modifierFlags = [e modifierFlags]; 65 return ([e type] == NSLeftMouseDown && 66 (modifierFlags == NSCommandKeyMask || modifierFlags == (NSCommandKeyMask | NSAlternateKeyMask)) 67 && [e clickCount] == 1); 68 } 69 70 BOOL ICCF_OptionKeyIsDown() { 71 unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags]; 72 return (modifierFlags & NSAlternateKeyMask) != 0; 64 73 } 65 74 66 75 void ICCF_CheckRange(NSRange range) { 67 NSCAssert(range.length > 0, @"No URL is selected");68 NSCAssert1(range.length <= ICCF_MAX_URL_LEN, @"The potential URL is longer than %ld characters", ICCF_MAX_URL_LEN);76 NSCAssert(range.length > 0, ICCF_LocalizedString(@"No URL is selected")); 77 NSCAssert1(range.length <= ICCF_MAX_URL_LEN, ICCF_LocalizedString(@"The potential URL is longer than %lu characters"), ICCF_MAX_URL_LEN); 69 78 } 70 79 … … 103 112 ICCF_StopIC(); 104 113 } 105 err = ICStart(&ICCF_icInst, 'ICCF');106 NSCAssert1(err == noErr, @"Unable to start Internet Config (error %d)", err);114 err = ICStart(&ICCF_icInst, kICCFCreator); 115 NSCAssert1(err == noErr, ICCF_LocalizedString(@"Unable to start Internet Config (error %d)"), err); 107 116 } 108 117 … … 121 130 } 122 131 132 ConstStringPtr ICCF_GetHint(ICInstance inst, const char *urlData, long startIndex, long endIndex) { 133 Handle h = NewHandle(0); 134 OSStatus err; 135 long tmpStartIndex = startIndex, tmpEndIndex = endIndex; 136 137 if (h == NULL) return NULL; 138 139 // parse the URL providing a bogus protocol, to get rid of escaped forms 140 err = ICParseURL(inst, "\p*", urlData + startIndex, endIndex - startIndex + 1, 141 &tmpStartIndex, &tmpEndIndex, h); 142 if (err != noErr) return NULL; 143 144 // scan through the parsed URL looking for characters not found in email addresses 145 Size hSize = GetHandleSize(h); 146 if (hSize == 0) return NULL; 147 148 const char *urlParsed = *h; 149 long i = 0; 150 Boolean sawAt = false; 151 if (urlParsed[0] == '*' && urlParsed[1] == ':') { 152 // this is an IC-inserted protocol; skip over it 153 i = 2; 154 } 155 for ( ; i < hSize ; i++) { 156 char c = urlParsed[i]; 157 if (c == '@') { 158 sawAt = true; 159 } else if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || 160 (c == '+' || c == '-' || c == '_' || c == '!' || c == '.'))) { 161 DisposeHandle(h); 162 return "\phttp"; 163 } 164 } 165 DisposeHandle(h); 166 return (sawAt ? "\pmailto" : "\phttp"); 167 } 168 123 169 void ICCF_ParseURL(NSString *string, NSRange *range) { 124 170 OSStatus err; … … 140 186 141 187 urlData = (char *)malloc( (range->length + 1) * sizeof(char)); 142 NSCAssert(urlData != NULL, @"Internal error: can 't allocate memory for URL string");188 NSCAssert(urlData != NULL, @"Internal error: canÕt allocate memory for URL string"); 143 189 144 190 selStart = 0; selEnd = range->length; … … 147 193 148 194 h = NewHandle(0); 149 NSCAssert(h != NULL, @"Internal error: can 't allocate URL handle");150 195 NSCAssert(h != NULL, @"Internal error: canÕt allocate URL handle"); 196 151 197 err = ICParseURL(ICCF_GetInst(), "\pmailto", urlData, range->length, &selStart, &selEnd, h); 198 DisposeHandle(h); 199 152 200 ICCF_OSErrCAssert(err, @"ICParseURL"); 153 201 154 DisposeHandle(h); 155 range->length = range->length - (range->length - selEnd) + selStart; 202 range->length = selEnd - selStart; 156 203 range->location += selStart; 157 204 NS_HANDLER … … 163 210 } 164 211 165 void ICCF_LaunchURL(NSString *string ) {212 void ICCF_LaunchURL(NSString *string, BOOL chooseApp) { 166 213 OSStatus err; 167 214 long selStart, selEnd; … … 172 219 NS_DURING 173 220 urlData = (char *)malloc( (len + 1) * sizeof(char)); 174 NSCAssert(urlData != NULL, @"Internal error: can 't allocate memory for URL string");221 NSCAssert(urlData != NULL, @"Internal error: canÕt allocate memory for URL string"); 175 222 176 223 [string getCString: urlData]; … … 178 225 selStart = 0; selEnd = len; 179 226 180 err = ICLaunchURL(ICCF_GetInst(), "\pmailto", urlData, len, &selStart, &selEnd); 181 ICCF_OSErrCAssert(err, @"ICLaunchURL"); 227 ConstStringPtr hint = ICCF_GetHint(ICCF_GetInst(), urlData, selStart, selEnd); 228 NSCAssert(hint != NULL, @"Internal error: canÕt get protocol hint for URL"); 229 230 if (chooseApp) { 231 err = ICCF_LaunchURLWithApplication(ICCF_GetInst(), hint, urlData, selStart, selEnd); 232 ICCF_OSErrCAssert(err, @"ICCF_LaunchURLWithApplication"); 233 } else { 234 err = ICLaunchURL(ICCF_GetInst(), hint, urlData, len, &selStart, &selEnd); 235 ICCF_OSErrCAssert(err, @"ICLaunchURL"); 236 } 182 237 183 238 NS_HANDLER … … 189 244 } 190 245 191 // XXX not sure what to do if there's already a selection; BBEdit extendsit, Tex-Edit Plus doesn't.246 // XXX not sure what to do if there's already a selection; BBEdit and MLTE extend it, Tex-Edit Plus doesn't. 192 247 // RFC-ordained max URL length, just to avoid passing IC multi-megabyte documents 193 248 #if ICCF_DEBUG … … 197 252 #endif 198 253 199 BOOL ICCF_enabled = YES; 200 201 void ICCF_HandleException(NSException *e) { 202 int result = NSRunAlertPanel(@"Open Internet Location", @"The selected Internet location could not be opened.\n\n%@.", @"OK", nil, @"Disable ICeCoffEEÉ", e); 254 Boolean ICCF_enabled = true; 255 256 BOOL ICCF_HandleException(NSException *e) { 257 if ([e reason] == nil || [[e reason] length] == 0) 258 return NO; 259 260 if (ICCF_prefs.errorSoundEnabled) NSBeep(); 261 if (!ICCF_prefs.errorDialogEnabled) return YES; 262 263 int result = NSRunAlertPanel(ICCF_LocalizedString(@"AlertTitle"), ICCF_LocalizedString(@"AlertMessage%@"), nil, nil, ICCF_LocalizedString(@"AlertDisableButton"), e); 203 264 if (result != NSAlertDefaultReturn) { 204 result = NSRunAlertPanel( @"Disable ICeCoffEE", @"If you believe ICeCoffEE is interfering with the normal functioning of this application, you can turn it off in this application until the application has quit.\n\nIf this is the first time you are experiencing this problem, please email icecoffee@sabi.net with the details of the conflict.\n\nTo remove ICeCoffEE permanently, drag its icon to the Trash or use the ICeCoffEE Installer.", @"Disable", @"DonÕt Disable", nil);265 result = NSRunAlertPanel(ICCF_LocalizedString(@"DisableAlertTitle"), ICCF_LocalizedString(@"DisableAlertMessage"), ICCF_LocalizedString(@"DisableAlertDisableButton"), ICCF_LocalizedString(@"DisableAlertDontDisableButton"), nil); 205 266 if (result == NSAlertDefaultReturn) 206 267 ICCF_enabled = NO; 207 268 } 269 return YES; 208 270 } 209 271 … … 218 280 NS_DURING 219 281 220 NSCAssert(range.location != NSNotFound, @"There is no insertion point or selection in the text field you clicked");221 NSCAssert(s != nil, @"Sorry, ICeCoffEE is unable to locate the insertion point or selection");282 NSCAssert(range.location != NSNotFound, ICCF_LocalizedString(@"There is no insertion point or selection in the text field where you clicked")); 283 NSCAssert(s != nil, ICCF_LocalizedString(@"Sorry, ICeCoffEE is unable to locate the insertion point or selection")); 222 284 223 285 ICCF_StartIC(); 224 286 225 NSCAssert([s length] != 0, @"No text was found");287 NSCAssert([s length] != 0, ICCF_LocalizedString(@"No text was found")); 226 288 227 289 if (range.location == [s length]) range.location--; // work around bug in selectionRangeForProposedRange (r. 2845418) … … 273 335 ICCF_ParseURL([s substringWithRange: range], &range); 274 336 275 for (i = 0 ; i < 3 ; i++) {276 NSRange emptyRange = {range.location, 0};277 [self setInsertionPointColor: [self backgroundColor]];278 [self setSelectedRange: range affinity: NSSelectionAffinityDownstream stillSelecting: YES];279 [self display];280 usleep(60000);281 [self setSelectedRange: emptyRange affinity: NSSelectionAffinityDownstream stillSelecting: YES];282 [self display];283 usleep(60000);284 }285 337 [self setSelectedRange: range affinity: NSSelectionAffinityDownstream stillSelecting: NO]; 286 338 [self display]; 287 339 288 ICCF_LaunchURL([s substringWithRange: range]); 340 ICCF_LaunchURL([s substringWithRange: range], ICCF_OptionKeyIsDown()); 341 342 if (ICCF_prefs.textBlinkEnabled) { 343 for (i = 0 ; i < ICCF_prefs.textBlinkCount ; i++) { 344 NSRange emptyRange = {range.location, 0}; 345 [self setSelectedRange: emptyRange affinity: NSSelectionAffinityDownstream stillSelecting: YES]; 346 [self display]; 347 usleep(kICBlinkDelayUsecs); 348 [self setInsertionPointColor: [self backgroundColor]]; 349 [self setSelectedRange: range affinity: NSSelectionAffinityDownstream stillSelecting: YES]; 350 [self display]; 351 usleep(kICBlinkDelayUsecs); 352 } 353 } 289 354 290 355 NS_HANDLER … … 316 381 } 317 382 318 void ICCF_AddServicesMenu() { 319 [ICeCoffEE performSelector: @selector(IC_addServicesMenu) withObject: nil afterDelay: 0.0]; 383 void ICCF_AddRemoveServicesMenu() { 384 // needed because: 385 // (a) we get called before the runloop has properly started and will crash if we don't delay on app startup 386 // (b) the APE message handler calls us from another thread and nothing happens if we try to add a menu on it 387 [ICeCoffEE performSelectorOnMainThread: @selector(IC_addRemoveServicesMenu) withObject: nil waitUntilDone: NO]; 320 388 } 321 389 … … 323 391 if (contextMenu != nil && [e type] == NSRightMouseDown || ([e type] == NSLeftMouseDown && [e modifierFlags] & NSControlKeyMask)) { 324 392 int servicesItemIndex = [contextMenu indexOfItemWithRepresentedObject: ICCF_SERVICES_ITEM]; 325 if (servicesItemIndex == -1 ) {393 if (servicesItemIndex == -1 && ICCF_prefs.servicesInContextualMenu) { 326 394 [contextMenu addItem: [NSMenuItem separatorItem]]; 327 395 [contextMenu addItem: ICCF_ServicesMenuItem()]; 396 } else if (servicesItemIndex != -1 && !ICCF_prefs.servicesInContextualMenu) { 397 [contextMenu removeItemAtIndex: servicesItemIndex]; 398 [contextMenu removeItemAtIndex: servicesItemIndex - 1]; 328 399 } 329 400 } 330 401 return contextMenu; 331 402 } 403 404 @implementation ICeCoffEE 332 405 333 406 + (NSString *)IC_version; … … 337 410 } 338 411 339 + (void)IC_add ServicesMenu;412 + (void)IC_addRemoveServicesMenu; 340 413 { 341 414 NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu]; 342 int insertLoc = [mainMenu indexOfItemWithSubmenu: [NSApp windowsMenu]]; 343 344 if (insertLoc == -1) 345 insertLoc = [mainMenu numberOfItems]; 346 347 [mainMenu insertItem: ICCF_ServicesMenuItem() atIndex: insertLoc]; 415 static NSMenuItem *servicesItem = nil; 416 417 if (servicesItem == nil && ICCF_prefs.servicesInMenuBar) { 418 servicesItem = [ICCF_ServicesMenuItem() retain]; 419 420 int insertLoc = [mainMenu indexOfItemWithSubmenu: [NSApp windowsMenu]]; 421 if (insertLoc == -1) 422 insertLoc = [mainMenu numberOfItems]; 423 424 [mainMenu insertItem: servicesItem atIndex: insertLoc]; 425 } else if (servicesItem != nil && !ICCF_prefs.servicesInMenuBar) { 426 [mainMenu removeItem: servicesItem]; 427 [servicesItem release]; 428 servicesItem = nil; 429 } 348 430 } 349 431 … … 365 447 down = YES; 366 448 ICLog(@"ICeCoffEE down: %@", e); 367 NSLog(@"super is %@", self);368 449 #endif 369 450 // we don't actually get a mouseUp event, just wait for mouseDown to return 370 451 [super mouseDown: e]; 371 if (!ICCF_enabled) return; 372 // don't want command-option-click, command-shift-click, etc. to trigger 452 if (!ICCF_enabled || !ICCF_prefs.commandClickEnabled) { 453 #if ICCF_DEBUG 454 down = NO; 455 #endif 456 return; 457 } 458 // don't want command-shift-click, etc. to trigger 373 459 if (ICCF_EventIsCommandMouseDown(e)) { 374 460 NSEvent *upEvent = [[self window] currentEvent]; … … 377 463 ICLog(@"next: %@", upEvent); 378 464 NSAssert([upEvent type] == NSLeftMouseUp, @"NSTextView mouseDown: did not return with current event as mouse up!"); 379 if (abs(downPt.x - upPt.x) <= 4 && abs(downPt.y - upPt.y) <= 4) {465 if (abs(downPt.x - upPt.x) <= kICHysteresisPixels && abs(downPt.y - upPt.y) <= kICHysteresisPixels) { 380 466 ICCF_LaunchURLFromTextView(self); 381 467 }
Note:
See TracChangeset
for help on using the changeset viewer.