Index: trunk/ICeCoffEE/ICeCoffEE/ICeCoffEE.m
===================================================================
--- trunk/ICeCoffEE/ICeCoffEE/ICeCoffEE.m	(revision 181)
+++ trunk/ICeCoffEE/ICeCoffEE/ICeCoffEE.m	(revision 182)
@@ -1,18 +1,8 @@
-// ICeCoffEE - Internet Config Cocoa Editor Extension
+// ICeCoffEE - Internet Config Carbon/Cocoa Editor Extension
 // Nicholas Riley <mailto:icecoffee@sabi.net>
 
 /* To do/think about:
 
-- TXNClick - MLTE has its own support in Jaguar and later, but it's lousy
-
-Done:
-
-- TEClick - TextEdit
-- flash on success (like BBEdit)
-- display dialog on failure (decode OSStatus)
-- adjust URL blinking
-- app exclusion list - make a pref pane (see AquaShade config)
-- _LSCopyApplicationURLsForItemURL - list apps
-- Menu on command-option-click: add bookmark, open with other helper, pass to configurable service, ...?
+- TXNClick - MLTE has its own (lousy) support in Jaguar, seems improved in Panther, good enough to leave?
 
 */
@@ -22,4 +12,5 @@
 #include <unistd.h>
 #import "ICeCoffEESuper.h"
+#import "ICeCoffEESetServicesMenu.h"
 
 iccfPrefRec ICCF_prefs;
@@ -91,7 +82,7 @@
 
         [set autorelease];
-        [set formUnionWithCharacterSet: [[NSCharacterSet characterSetWithRange: NSMakeRange(0, 128)] invertedSet]]; // remove non-ASCII characters
+        [set formUnionWithCharacterSet: [[NSCharacterSet characterSetWithRange: NSMakeRange(0x21, 0x5e)] invertedSet]]; // nonprintable and non-ASCII characters
         [set formUnionWithCharacterSet: [NSCharacterSet punctuationCharacterSet]];
-        [set removeCharactersInString: @";/?:@&=+$,-_.!~*'()%#"]; // RFC 2396 ¤2.2, 2.3, 2.4, plus #
+        [set removeCharactersInString: @";/?:@&=+$,-_.!~*'()%#"]; // RFC 2396 ¤2.2, 2.3, 2.4, plus % and # from "delims" set
 
         tmpSet = [[set mutableCopy] autorelease];
@@ -198,19 +189,25 @@
 }
 
+// input/output 'range' is the range of source document which contains 'string'
 void ICCF_ParseURL(NSString *string, NSRange *range) {
     OSStatus err;
     Handle h;
-    long selStart, selEnd;
+    long selStart = 0, selEnd = range->length; // local offsets within 'string'
     char *urlData = NULL;
 
-    NSCAssert(range->length == [string length], @"Internal error: URL string is wrong length");
+    NSCAssert(selEnd == [string length], @"Internal error: URL string is wrong length");
     
     NS_DURING
         if ([[NSCharacterSet characterSetWithCharactersInString: @";,."] characterIsMember:
-            [string characterAtIndex: range->length - 1]]) {
-            range->length--;
-        }
-
-        string = [string substringToIndex: range->length];
+            [string characterAtIndex: selEnd - 1]]) {
+            selEnd--;
+        }
+	NSCharacterSet *alphanumericCharacterSet = [NSCharacterSet alphanumericCharacterSet];
+	while (![alphanumericCharacterSet characterIsMember: [string characterAtIndex: selStart]]) {
+	    selStart++;
+	    NSCAssert(selStart < selEnd, @"No URL is selected");
+	}
+
+        string = [string substringWithRange: NSMakeRange(selStart, selEnd - selStart)];
 
         ICLog(@"Parsing URL |%@|", string);
@@ -221,6 +218,5 @@
         NSCAssert(urlData != NULL, @"Internal error: can't allocate memory for URL string");
 
-        selStart = 0; selEnd = range->length;
-
+	// XXX getCString: is deprecated in 10.4, but this is safe and shouldn't assert because we've already verified the string can be converted to ASCII, which should be a subset of any possible system encoding.  The replacement (getCString:maxLength:encoding:) is not available until 10.4, so we leave this until we dump Internet Config and gain IDN friendliness.
         [string getCString: urlData];
 
@@ -402,14 +398,23 @@
 NSMenuItem *ICCF_ServicesMenuItem() {
     NSMenuItem *servicesItem;
-    NSMenu *servicesMenu;
-    // don't want to use [[NSApp servicesMenu] title] because the Services menu may not have been created yet
-    NSString *servicesTitle = [[NSBundle bundleWithIdentifier: @"com.apple.AppKit"] localizedStringForKey: @"Services" value: nil table: @"ServicesMenu"];
-    if (servicesTitle == nil) {
-        ICLog(@"Can't get localized text for 'Services' in AppKit.framework");
-        servicesTitle = @"Services";
+    NSString *servicesTitle = nil;
+    NSMenu *servicesMenu = [NSApp servicesMenu];
+    
+    if (servicesMenu != nil) {
+	servicesTitle = [servicesMenu title];
+	if (servicesTitle == nil) {
+	    ICLog(@"Can't get service menu title");
+	    servicesTitle = @"Services";
+	}
+    } else {
+	servicesTitle = [[NSBundle bundleWithIdentifier: @"com.apple.AppKit"] localizedStringForKey: @"Services" value: nil table: @"ServicesMenu"];
+	if (servicesTitle == nil) {
+	    ICLog(@"Can't get localized text for 'Services' in AppKit.framework");
+	    servicesTitle = @"Services";
+	}
     }
     servicesMenu = [[NSMenu alloc] initWithTitle: servicesTitle];
     servicesItem = [[NSMenuItem alloc] initWithTitle: servicesTitle action:nil keyEquivalent:@""];
-    [[NSApplication sharedApplication] setServicesMenu: servicesMenu];
+    ICCF_SetServicesMenu(servicesMenu);
     [servicesItem setSubmenu: servicesMenu];
     [servicesItem setRepresentedObject: ICCF_SERVICES_ITEM];
@@ -448,4 +453,5 @@
             }
         } else {
+	    [menuItem setKeyEquivalent: @""];
             shouldKeepItem = [menuItem isEnabled];
         }
@@ -494,4 +500,10 @@
 }
 
+
+@interface NSTextView (IC_NSSharing)
+// only in Mac OS X 10.4 and later
+- (NSArray *)selectedRanges;
+@end
+
 @implementation ICeCoffEE
 
@@ -536,5 +548,14 @@
 #endif
     if (ICCF_enabled && ICCF_prefs.commandClickEnabled && ICCF_EventIsCommandMouseDown(e)) {
-        // don't want to trigger selection extension or anything else; pass through as a plain click
+	if ([self respondsToSelector: @selector(selectedRanges)]) {
+	    // discontiguous selection support, Mac OS X 10.4 or later
+	    NSArray *ranges = [self selectedRanges];
+	    ICLog(@"ICeCoffEE selected ranges: %@", ranges);
+	    if ([ranges count] > 1 || [[ranges objectAtIndex: 0] rangeValue].length != 0)
+		goto bypass;
+	} else {
+	    // don't want to trigger selection extension or anything else; pass through as a plain click
+	    // (on Panther and earlier, command does not modify behavior)
+	}
         [super mouseDown: [NSEvent mouseEventWithType: NSLeftMouseDown location: [e locationInWindow] modifierFlags: 0 timestamp: [e timestamp] windowNumber: [e windowNumber] context: [e context] eventNumber: [e eventNumber] clickCount: 1 pressure: 0]];
         // we don't actually get a mouseUp event, just wait for mouseDown to return
@@ -548,4 +569,5 @@
         }
     } else {
+bypass:
         [super mouseDown: e];
     }
