diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3238e1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.mode1v3 +*.pbxuser +*.xcworkspace +xcuserdata diff --git a/Info.plist b/Info.plist index ac47f95..dc82a7c 100644 --- a/Info.plist +++ b/Info.plist @@ -6,31 +6,29 @@ English CFBundleExecutable ${EXECUTABLE_NAME} - CFBundleName - ${PRODUCT_NAME} CFBundleIconFile CFBundleIdentifier - com.yourcompany.${PRODUCT_NAME:rfc1034Identifier} + com.omnigroup.${PRODUCT_NAME:rfc1034Identifier} CFBundleInfoDictionaryVersion 6.0 + CFBundleName + ${PRODUCT_NAME} CFBundlePackageType BNDL + CFBundleShortVersionString + 1.0 CFBundleSignature ???? CFBundleVersion 1 - CFBundleShortVersionString - 1.0 - LoadAtLaunch YES XCGCReady YES XCPluginHasUI NO + XC4Compatible + diff --git a/README.markdown b/README.markdown index 8ce89dd..e875f77 100644 --- a/README.markdown +++ b/README.markdown @@ -1,7 +1,7 @@ XcodeSelectionColorFix =========== -When an Xcode editor is in a non-key window, it draws with `+secondarySelectedControlColor` as the background color for selected text. But, if you prefer to use a dark theme (like the included Midnight), this is illegible since the light text color and light background color are too similar. +When an Xcode 4 editor is in a non-key window, it draws with `+secondarySelectedControlColor` as the background color for selected text. But, if you prefer to use a dark theme (like the included Midnight), this is illegible since the light text color and light background color are too similar. This Xcode plugin hacks around and makes Xcode draw using a 50/50 mixture of the background color and selection color you specified in your theme. @@ -11,6 +11,6 @@ Installation - Build and install the bundle where ever you like - Inform Xcode of its location with: - defaults write com.apple.Xcode ExtraPlugInFolders -array-add "/path/to/folder/containing/plugin" + defaults write com.apple.dt.Xcode ExtraPlugInFolders -array-add "/path/to/folder/containing/plugin" -- Relaunch Xcode +- Relaunch Xcode 4 diff --git a/XcodeSelectionColorFix.xcodeproj/project.pbxproj b/XcodeSelectionColorFix.xcodeproj/project.pbxproj index 6b54eaf..c46351f 100644 --- a/XcodeSelectionColorFix.xcodeproj/project.pbxproj +++ b/XcodeSelectionColorFix.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 344DC542101A2768003A6019 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 344DC541101A2768003A6019 /* main.m */; }; 34849A9B101A51FE003602A4 /* OBMethodReplacement.m in Sources */ = {isa = PBXBuildFile; fileRef = 34849A9A101A51FE003602A4 /* OBMethodReplacement.m */; }; 34849ADE101A56FE003602A4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34849ADD101A56FE003602A4 /* Cocoa.framework */; }; + 34C076AA130350E60075B54B /* cursor.png in Resources */ = {isa = PBXBuildFile; fileRef = 34C076A9130350E60075B54B /* cursor.png */; }; 8D5B49A804867FD3000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8D5B49A704867FD3000E48DA /* InfoPlist.strings */; }; /* End PBXBuildFile section */ @@ -20,6 +21,7 @@ 34849A99101A51FE003602A4 /* OBMethodReplacement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OBMethodReplacement.h; sourceTree = ""; }; 34849A9A101A51FE003602A4 /* OBMethodReplacement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OBMethodReplacement.m; sourceTree = ""; }; 34849ADD101A56FE003602A4 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 34C076A9130350E60075B54B /* cursor.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cursor.png; sourceTree = ""; }; 8D576316048677EA00EA77CD /* XcodeSelectionColorFix.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XcodeSelectionColorFix.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 8D576317048677EA00EA77CD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ @@ -60,6 +62,7 @@ isa = PBXGroup; children = ( 8D576317048677EA00EA77CD /* Info.plist */, + 34C076A9130350E60075B54B /* cursor.png */, 8D5B49A704867FD3000E48DA /* InfoPlist.strings */, ); name = Resources; @@ -111,7 +114,11 @@ isa = PBXProject; buildConfigurationList = 1DEB911E08733D790010E9CD /* Build configuration list for PBXProject "XcodeSelectionColorFix" */; compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* XcodeSelectionColorFix */; projectDirPath = ""; projectRoot = ""; @@ -127,6 +134,7 @@ buildActionMask = 2147483647; files = ( 8D5B49A804867FD3000E48DA /* InfoPlist.strings in Resources */, + 34C076AA130350E60075B54B /* cursor.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/cursor.png b/cursor.png new file mode 100644 index 0000000..90a7a72 Binary files /dev/null and b/cursor.png differ diff --git a/main.m b/main.m index 0601ff2..5c05729 100644 --- a/main.m +++ b/main.m @@ -12,22 +12,32 @@ @implementation com_omnigroup_XcodeSelectionColorFix static void (*original_drawBackgroundForGlyphRange)(id self, SEL _cmd, NSRange glyphsToShow, NSPoint origin) = NULL; static int32_t drawBackgroundForGlyphRangeNesting = 0; -static Class XCLayoutManager = Nil; -static Class XCTextView = Nil; +static Class DVTLayoutManager = Nil; +static Class DVTFontAndColorTheme = Nil; +static Class DVTSourceTextView = Nil; +static Class IDEConsoleTextView = Nil; #define REQUIRE_CLASS(x) \ do { \ x = NSClassFromString((id)CFSTR(#x)); \ if (!x) { \ - NSLog(@"%s: Unable to find class '%s'!", __PRETTY_FUNCTION__, #x); \ + NSLog(@"%s: Unable to find class \"%s\"!", __PRETTY_FUNCTION__, #x); \ return; \ } \ } while (0) -#define REQUIRE_METHOD(object, name) \ +#define REQUIRE_CLASS_METHOD(cls, name) \ do { \ - if (![object respondsToSelector:@selector(name)]) { \ - NSLog(@"%s: '%s' doesn't respond to '%s'.", __PRETTY_FUNCTION__, #object, #name); \ + if (![cls respondsToSelector:@selector(name)]) { \ + NSLog(@"%s: \"%s\" doesn't respond to +%s.", __PRETTY_FUNCTION__, #cls, #name); \ + return; \ + } \ +} while (0) + +#define REQUIRE_INSTANCE_METHOD(cls, name) \ +do { \ + if (![cls instancesRespondToSelector:@selector(name)]) { \ + NSLog(@"%s: Instances of \"%s\" don't respond to -%s.", __PRETTY_FUNCTION__, #cls, #name); \ return; \ } \ } while (0) @@ -41,11 +51,15 @@ + (void)load; return; // Could also check the superclasses of these, but that seems like overkill - REQUIRE_CLASS(XCLayoutManager); + REQUIRE_CLASS(DVTLayoutManager); - REQUIRE_CLASS(XCTextView); - REQUIRE_METHOD(XCTextView, textEditorBackgroundColor); - REQUIRE_METHOD(XCTextView, textEditorSelectionBackgroundColor); + REQUIRE_CLASS(DVTFontAndColorTheme); + REQUIRE_CLASS_METHOD(DVTFontAndColorTheme, currentTheme); + REQUIRE_INSTANCE_METHOD(DVTFontAndColorTheme, sourceTextSelectionColor); + REQUIRE_INSTANCE_METHOD(DVTFontAndColorTheme, consoleTextSelectionColor); + + REQUIRE_CLASS(DVTSourceTextView); + REQUIRE_CLASS(IDEConsoleTextView); original_secondarySelectedControlColor = (typeof(original_secondarySelectedControlColor))OBReplaceMethodImplementationWithSelectorOnClass(object_getClass([NSColor class]), @selector(secondarySelectedControlColor), self, @selector(replacement_secondarySelectedControlColor)); if (!original_secondarySelectedControlColor) { @@ -53,11 +67,21 @@ + (void)load; return; } - original_drawBackgroundForGlyphRange = (typeof(original_drawBackgroundForGlyphRange))OBReplaceMethodImplementationWithSelectorOnClass(XCLayoutManager, @selector(drawBackgroundForGlyphRange:atPoint:), self, @selector(replacement_drawBackgroundForGlyphRange:atPoint:)); + original_drawBackgroundForGlyphRange = (typeof(original_drawBackgroundForGlyphRange))OBReplaceMethodImplementationWithSelectorOnClass(DVTLayoutManager, @selector(drawBackgroundForGlyphRange:atPoint:), self, @selector(replacement_drawBackgroundForGlyphRange:atPoint:)); if (!original_drawBackgroundForGlyphRange) { NSLog(@"Unable to replace method."); return; } + + // Install light-mode cursors for the source and debug console + if (!OBReplaceMethodImplementationWithSelectorOnClass(DVTSourceTextView, @selector(_mouseInside:), self, @selector(_mouseInside:))) { + NSLog(@"Unable to replace method."); + return; + } + if (!OBReplaceMethodImplementationWithSelectorOnClass(IDEConsoleTextView, @selector(_mouseInside:), self, @selector(_mouseInside:))) { + NSLog(@"Unable to replace method."); + return; + } } - (NSColor *)replacement_secondarySelectedControlColor; @@ -66,9 +90,10 @@ - (NSColor *)replacement_secondarySelectedControlColor; return original_secondarySelectedControlColor(self, _cmd); // Return a color interpolated between the user's background color and selection color. - NSColor *color0 = [[XCTextView performSelector:@selector(textEditorBackgroundColor)] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; - NSColor *color1 = [[XCTextView performSelector:@selector(textEditorSelectionBackgroundColor)] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; - + id theme = [DVTFontAndColorTheme performSelector:@selector(currentTheme)]; + NSColor *color0 = [[theme performSelector:@selector(sourceTextSelectionColor)] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + NSColor *color1 = [[theme performSelector:@selector(sourceTextBackgroundColor)] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + return [color0 blendedColorWithFraction:0.5 ofColor:color1]; } @@ -82,4 +107,26 @@ - (void)replacement_drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(N } } +// Normally the IBeamCursor is set by this hitting a implementation on NSTextView. I'm hooking it on DVTSourceTextView for now, but it might need to be more tightly tuned. +- (void)_mouseInside:(NSEvent *)event; +{ + static NSCursor *cursor = nil; + + if (!cursor) { + NSString *imagePath = [[NSBundle bundleWithIdentifier:@"com.omnigroup.XcodeSelectionColorFix"] pathForImageResource:@"cursor"]; + if (!imagePath) { + NSLog(@"No cursor image found!"); + } else { + NSImage *image = [[NSImage alloc] initWithContentsOfFile:imagePath]; + if (!image) + NSLog(@"Unable to load cursor image"); + else + cursor = [[NSCursor alloc] initWithImage:image hotSpot:[[NSCursor IBeamCursor] hotSpot]]; + } + if (!cursor) + cursor = [[NSCursor IBeamCursor] retain]; + } + [cursor set]; +} + @end