Index: trunk/ICeCoffEE/ICeCoffEE/APEMain.m
===================================================================
--- trunk/ICeCoffEE/ICeCoffEE/APEMain.m	(revision 66)
+++ trunk/ICeCoffEE/ICeCoffEE/APEMain.m	(revision 74)
@@ -17,4 +17,5 @@
 #import <objc/objc-runtime.h>
 #import "ICeCoffEE.h"
+#import "ICeCoffEETextEdit.h"
 
 //¥¥¥ Our settings
@@ -22,4 +23,5 @@
 //¥¥¥ Function prototypes
 static void ICCF_ReloadPrefs();				// reloads our preferences
+static void ICCF_MigratePrefs();			// migrates prefs from 1.0Ð1.2
 
 //¥¥¥ Enter sandman, the code begins --
@@ -28,5 +30,5 @@
     struct objc_class *patchclass = objc_getClass(patchclass ## Name); \
     if (patchclass == NULL) { \
-        apeprintf("can't get %s\n", patchclass ## Name); \
+        ICapeprintf("can't get %s\n", patchclass ## Name); \
         return NO; \
     }
@@ -35,5 +37,5 @@
     Method name = class_getInstanceMethod(patchclass, sel); \
     if (name == NULL) { \
-        apeprintf("can't get %s\n", patchclass ## Name); \
+        ICapeprintf("can't get %s\n", patchclass ## Name); \
         return NO; \
     }
@@ -54,50 +56,66 @@
     ICCF_GET_METHOD(patchSuperMethod, patchSuperclass, selector);
 
-    APEPatchCreate(patchSuperMethod->method_imp,
-                   APEPatchCreate(patcheeMethod->method_imp, patchMethod->method_imp));
+    if (APEPatchCreate(patchSuperMethod->method_imp,
+                       APEPatchCreate(patcheeMethod->method_imp, patchMethod->method_imp)) == NULL) {
+        ICapeprintf("can't patch class %s with [%s %s] super %s", patcheeClassName, patchClassName, selectorString, patchSuperclassName);
+        return NO;
+    }
     return YES;
 }
+
+CFBundleRef ICCF_bundle;
 
 void APEBundleMain(CFBundleRef inBundle)
 {
+    ICCF_MigratePrefs();
+
     // first check if this application is in the exclude list;
     // if it is, simply return and do not apply any patches.
     // APETools will help us with that; APE Manager will take care of
     // exclude list management for us.
-    if (APEToolsIsInExcludeList(CFSTR("net.sabi.ICeCoffEE"), NULL))
+    if (APEToolsIsInExcludeList(kICBundleIdentifier, NULL))
     {
-        apeprintf("ICeCoffEE APE: not loading as this application is excluded.\n");
+        ICapeprintf("ICeCoffEE APE: not loading as this application is excluded.\n");
         return;
     }
-    
-    ICCF_ReloadPrefs();					// load your preferences
-    // Add any of your initialization here...
-
+
+    ICCF_bundle = CFBundleGetBundleWithIdentifier(kICBundleIdentifier);
     CFStringRef bundleID = CFBundleGetIdentifier(CFBundleGetMainBundle());
-    BOOL shouldLoadAlways = YES;
-    
+    BOOL shouldLoadInNSTextView = YES;
+
+    // XXX handle patching error return from ICCF_PatchMethod
     if (bundleID != NULL) {
         if (CFStringCompare(bundleID, CFSTR("com.apple.projectbuilder"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
             ICCF_PatchMethod("PBXTextView", "ICeCoffEEMenuOnly", "ICeCoffEEMenuSuper", "menuForEvent:");
-            apeprintf("ICeCoffEE APE: loading in PB!\n");
-            shouldLoadAlways = NO;
+            ICapeprintf("ICeCoffEE APE: loaded in PBXTextView for PB!\n");
+            shouldLoadInNSTextView = NO;
         } else if (CFStringCompare(bundleID, CFSTR("com.apple.terminal"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
-            ICCF_PatchMethod("TermSubview", "ICeCoffEEMenuOnly", "ICeCoffEEMenuSuper", "menuForEvent:");
-            apeprintf("ICeCoffEE APE: loading in Terminal!\n");
+            ICCF_PatchMethod("TermSubview", "ICeCoffEEMenuOnly", "ICeCoffEEMenuSuper", "menuForEvent:") &&
+            ICCF_PatchMethod("TermSubview", "ICeCoffEETerminal", "ICECoffEETermSubviewSuper", "selectedRange") &&
+            ICCF_PatchMethod("TermSubview", "ICeCoffEETerminal", "ICECoffEETermSubviewSuper", "attributedSubstringFromRange:") &&
+            ICCF_PatchMethod("TermSubview", "ICeCoffEETerminal", "ICECoffEETermSubviewSuper", "mouseDown:") &&
+            ICCF_PatchMethod("TermSubview", "ICeCoffEETerminal", "ICECoffEETermSubviewSuper", "mouseUp:") &&
+            ICCF_PatchMethod("TermSubview", "ICeCoffEETerminal", "ICECoffEETermSubviewSuper", "_optionClickEvent:::") ,
+            ICapeprintf("ICeCoffEE APE: loaded in TermSubview for Terminal!\n");
         } else if (CFStringCompare(bundleID, CFSTR("com.apple.safari"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
-            ICCF_PatchMethod("WebHTMLView", "ICeCoffEEWebKit", "ICeCoffEEWebKitSuper", "mouseUp:");
-            ICCF_PatchMethod("WebHTMLView", "ICeCoffEEWebKit", "ICeCoffEEWebKitSuper", "mouseDown:");
-            ICCF_PatchMethod("WebHTMLView", "ICeCoffEEWebKit", "ICeCoffEEWebKitSuper", "menuForEvent:");
-            apeprintf("ICeCoffEE APE: loading in Safari!\n");
+            ICCF_PatchMethod("WebHTMLView", "ICeCoffEEWebKit", "ICeCoffEEWebKitSuper", "mouseUp:") &&
+            ICCF_PatchMethod("WebHTMLView", "ICeCoffEEWebKit", "ICeCoffEEWebKitSuper", "mouseDown:") &&
+            ICCF_PatchMethod("WebHTMLView", "ICeCoffEEWebKit", "ICeCoffEEWebKitSuper", "menuForEvent:") , 
+            ICapeprintf("ICeCoffEE APE: loaded in WebHTMLView for Safari!\n");
         }
     }
 
-    if (shouldLoadAlways) {
-        ICCF_PatchMethod("NSTextView", "ICeCoffEE", "ICeCoffEESuper", "mouseDown:");
-        ICCF_PatchMethod("NSTextView", "ICeCoffEE", "ICeCoffEESuper", "menuForEvent:");
-        apeprintf("ICeCoffEE APE: loading\n");
-    }
-
-    ICCF_AddServicesMenu();
+    if (shouldLoadInNSTextView) {
+        ICCF_PatchMethod("NSTextView", "ICeCoffEE", "ICeCoffEESuper", "mouseDown:") &&
+        ICCF_PatchMethod("NSTextView", "ICeCoffEE", "ICeCoffEESuper", "menuForEvent:") ,
+        ICapeprintf("ICeCoffEE APE: loaded generic NSTextView support\n");
+    }
+
+    gTEClick = APEPatchCreate(&TEClick, &ICCF_TEClick);
+    if (gTEClick != NULL) {
+        ICapeprintf("ICeCoffEE APE: patched TEClick\n");
+    }
+
+    ICCF_ReloadPrefs();
         
     return;
@@ -107,7 +125,7 @@
 OSStatus APEBundleMessage(CFStringRef message,CFDataRef inData,CFDataRef *outData)
 {
-    apeprintf("ICeCoffEE APE: message '%@' (inData = %@)\n", message, inData);
-    
-    if (CFStringCompare(message, CFSTR("Refresh"), NULL) == kCFCompareEqualTo)		
+    ICapeprintf("ICeCoffEE APE: message '%@' (inData = %@)\n", message, inData);
+    
+    if (CFStringCompare(message, kICPreferencesChanged, NULL) == kCFCompareEqualTo)		
     {	// request to reload prefs from our preference pane
         ICCF_ReloadPrefs();
@@ -117,9 +135,93 @@
 }
 
-// Reload our settings from the (nonexistent) plist
-static void ICCF_ReloadPrefs()
-{
-    CFPreferencesAppSynchronize(CFSTR("net.sabi.ICeCoffEE"));
-
-    // Load your preferences here...
-}
+Boolean ICCF_GetBooleanPref(CFStringRef prefKey, Boolean defaultValue) {
+    Boolean keyExists;
+    Boolean value = CFPreferencesGetAppBooleanValue(prefKey, kICBundleIdentifier, &keyExists);
+    if (keyExists) return value;
+    CFPreferencesSetAppValue(prefKey, defaultValue ? kCFBooleanTrue : kCFBooleanFalse, kICBundleIdentifier);
+    return defaultValue;
+}
+
+CFIndex ICCF_GetCFIndexPref(CFStringRef prefKey, CFIndex defaultValue) {
+    Boolean keyExists;
+    CFIndex value = CFPreferencesGetAppIntegerValue(prefKey, kICBundleIdentifier, &keyExists);
+    if (keyExists) return value;
+    CFNumberRef defaultValueNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &defaultValue);
+    CFPreferencesSetAppValue(prefKey, defaultValueNumber, kICBundleIdentifier);
+    CFRelease(defaultValueNumber);
+    return defaultValue;
+}
+
+// to test: defaults write net.sabi.ICeCoffEE "Excluded Applications" -array '{CFBundleID = "com.apple.projectbuilder"; }' '{CFBundleID = "net.sabi.Pester"; }' '{CFBundleID = "com.apple.foobar"; }'
+static void ICCF_MigratePrefs() {
+    
+    CFArrayRef prefExcludedApps = CFPreferencesCopyAppValue(kIC12PrefExcluded, kICBundleIdentifier);
+
+    if (prefExcludedApps == NULL) return;
+
+    if (CFGetTypeID(prefExcludedApps) != CFArrayGetTypeID()) {
+        CFRelease(prefExcludedApps);
+        return;
+    }
+    
+    CFMutableArrayRef excludedApps = CFArrayCreateMutableCopy(NULL, 0, prefExcludedApps);
+    CFRelease(prefExcludedApps); prefExcludedApps = NULL;
+
+    ICapeprintf("Excluded apps: %@\n", excludedApps);
+
+    CFIndex excludedAppCount = CFArrayGetCount(excludedApps);
+    CFIndex excludedAppIndex;
+
+    CFDictionaryRef excludedAppSpecifiers = NULL;
+    CFStringRef excludedAppBundleID = NULL;
+    CFURLRef excludedAppURL = NULL;
+    CFBundleRef excludedAppBundle = NULL;
+
+    BOOL postponeMigrationForApp;
+
+    for (excludedAppIndex = excludedAppCount - 1 ; excludedAppIndex >= 0 ; excludedAppIndex--) {
+        if ( (excludedAppSpecifiers = CFArrayGetValueAtIndex(excludedApps, excludedAppIndex)) == NULL || CFGetTypeID(excludedAppSpecifiers) != CFDictionaryGetTypeID()) {
+            CFArrayRemoveValueAtIndex(excludedApps, excludedAppIndex);
+            continue;
+        }
+
+        postponeMigrationForApp = NO;
+        if ( (excludedAppBundleID = CFDictionaryGetValue(excludedAppSpecifiers, kIC12PrefExcludedAppSpecifierBundleID)) != NULL
+             && CFGetTypeID(excludedAppBundleID) == CFStringGetTypeID()
+             && CFStringCompare(excludedAppBundleID, CFSTR("com.apple.projectbuilder"), kCFCompareCaseInsensitive) != kCFCompareEqualTo) {
+            if (LSFindApplicationForInfo(kLSUnknownCreator, excludedAppBundleID, NULL, NULL, &excludedAppURL) == noErr) {
+                excludedAppBundle = CFBundleCreate(NULL, excludedAppURL);
+                if (excludedAppBundle != NULL) {
+                    APEToolsAddToExcludeList(kICBundleIdentifier, excludedAppBundle);
+                } else postponeMigrationForApp = YES; // can't create bundle
+            } else postponeMigrationForApp = YES; // can't find app
+        }
+        // don't release excludedAppSpecifiers, used Get
+        // don't release excludedAppBundleID, used Get
+        if (excludedAppURL != NULL) { CFRelease(excludedAppURL); excludedAppURL = NULL; }
+        if (excludedAppBundle != NULL) { CFRelease(excludedAppBundle); excludedAppBundle = NULL; }
+        if (!postponeMigrationForApp) CFArrayRemoveValueAtIndex(excludedApps, excludedAppIndex);
+    }
+
+    ICapeprintf("Excluded apps remaining: %@\n", excludedApps);
+    CFPreferencesSetAppValue(kIC12PrefExcluded, (CFArrayGetCount(excludedApps) == 0 ? NULL : excludedApps), kICBundleIdentifier);
+    CFRelease(excludedApps);
+
+    CFPreferencesAppSynchronize(kICBundleIdentifier);
+}
+
+static void ICCF_ReloadPrefs() {
+    CFPreferencesAppSynchronize(kICBundleIdentifier);
+
+    ICCF_prefs.commandClickEnabled = ICCF_GetBooleanPref(kICCommandClickEnabled, YES);
+    ICCF_prefs.textBlinkEnabled = ICCF_GetBooleanPref(kICTextBlinkEnabled, YES);
+    ICCF_prefs.textBlinkCount = ICCF_GetCFIndexPref(kICTextBlinkCount, 3);
+    ICCF_prefs.servicesInContextualMenu = ICCF_GetBooleanPref(kICServicesInContextualMenu, YES);
+    ICCF_prefs.servicesInMenuBar = ICCF_GetBooleanPref(kICServicesInMenuBar, NO);
+    ICCF_prefs.errorSoundEnabled = ICCF_GetBooleanPref(kICErrorSoundEnabled, NO);
+    ICCF_prefs.errorDialogEnabled = ICCF_GetBooleanPref(kICErrorDialogEnabled, YES);
+
+    CFPreferencesAppSynchronize(kICBundleIdentifier);
+
+    ICCF_AddRemoveServicesMenu();
+}
