Skip to content
Browse files

This is an input manager which is supposed to replace the Edit in Tex…

…tMate service (when it's had a bit more polish and has been tested).

It has a few advantages:
 o doesn't block the calling application
 o does html -> text and text -> html conversion for Mail (which edits text via an WebHTMLView, which is a little messy)
 o gives as filename the window title + calling application as extension (so one can bind languages based on application name)
 o can be called without having to first make a selection
 o even works from within TextMate (e.g. bundle editor), although not before next build (save/close will deadlock in current public build)
 o the activation key doesn't need to be command + something

It is of course a hack. It adds an editInTextMate: action method to NSTextView and WebKit. To install build the target, then:

   mkdir -p ~/Library/InputManagers/Edit\ in\ TextMate
   cd ~/Library/InputManagers/Edit\ in\ TextMate
   ln -s "/path/to/Edit in TextMate.bundle" "Edit in TextMate.bundle"
   ln -s "/path/to/source/Info" Info

In addition you'll need to add an entry in your key bindings file (~/Library/KeyBindings/DefaultKeyBinding.dict), I have this:

   "^E" = "editInTextMate:";

If all this sounds complicated, just hold off using it till it gets bundled with TM.

Currently it doesn't correctly “escape” tab characters when sending back text to Mail. The way it retains the calling view, and checks [view window] is also a little dirty, but it seems to work (and so it ought to).


git-svn-id: http://svn.textmate.org/trunk/Tools/Edit%20in%20TextMate@2299 dfb7d73b-c2ec-0310-8fea-fb051d288c6d
  • Loading branch information...
0 parents commit 7065b6044716c134fdbfd102e13fa8aa12434529 @sorbits sorbits committed
85 Edit in TextMate.xcodeproj/duff.pbxuser
@@ -0,0 +1,85 @@
+// !$*UTF8*$!
+{
+ 089C1669FE841209C02AAC07 /* Project object */ = {
+ activeBuildConfigurationName = Release;
+ activeBuildStyle = 014CEA420018CDE011CA2923 /* Debug */;
+ activeTarget = 8D5B49AC048680CD000E48DA /* Edit in TextMate */;
+ addToTargets = (
+ 8D5B49AC048680CD000E48DA /* Edit in TextMate */,
+ );
+ breakpointsGroup = 1780BC15093917A800783BDB /* XCBreakpointsBucket */;
+ codeSenseManager = 1780BBEB093907D900783BDB /* Code sense */;
+ perUserDictionary = {
+ PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
+ PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
+ PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
+ PBXFileTableDataSourceColumnWidthsKey = (
+ 20,
+ 243,
+ 20,
+ 48,
+ 43,
+ 43,
+ 20,
+ );
+ PBXFileTableDataSourceColumnsKey = (
+ PBXFileDataSource_FiletypeID,
+ PBXFileDataSource_Filename_ColumnID,
+ PBXFileDataSource_Built_ColumnID,
+ PBXFileDataSource_ObjectSize_ColumnID,
+ PBXFileDataSource_Errors_ColumnID,
+ PBXFileDataSource_Warnings_ColumnID,
+ PBXFileDataSource_Target_ColumnID,
+ );
+ };
+ PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = {
+ PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
+ PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
+ PBXFileTableDataSourceColumnWidthsKey = (
+ 20,
+ 200,
+ 63,
+ 20,
+ 48,
+ 43,
+ 43,
+ );
+ PBXFileTableDataSourceColumnsKey = (
+ PBXFileDataSource_FiletypeID,
+ PBXFileDataSource_Filename_ColumnID,
+ PBXTargetDataSource_PrimaryAttribute,
+ PBXFileDataSource_Built_ColumnID,
+ PBXFileDataSource_ObjectSize_ColumnID,
+ PBXFileDataSource_Errors_ColumnID,
+ PBXFileDataSource_Warnings_ColumnID,
+ );
+ };
+ PBXPerProjectTemplateStateSaveDate = 154773983;
+ PBXWorkspaceStateSaveDate = 154773983;
+ };
+ sourceControlManager = 1780BBEA093907D900783BDB /* Source Control */;
+ userBuildSettings = {
+ };
+ };
+ 1780BBEA093907D900783BDB /* Source Control */ = {
+ isa = PBXSourceControlManager;
+ fallbackIsa = XCSourceControlManager;
+ isSCMEnabled = 0;
+ scmConfiguration = {
+ };
+ scmType = "";
+ };
+ 1780BBEB093907D900783BDB /* Code sense */ = {
+ isa = PBXCodeSenseManager;
+ indexTemplatePath = "";
+ };
+ 1780BC15093917A800783BDB /* XCBreakpointsBucket */ = {
+ isa = XCBreakpointsBucket;
+ name = "Project Breakpoints";
+ objects = (
+ );
+ };
+ 8D5B49AC048680CD000E48DA /* Edit in TextMate */ = {
+ activeExec = 0;
+ };
+}
304 Edit in TextMate.xcodeproj/project.pbxproj
@@ -0,0 +1,304 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 42;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 17085DDA0939627C000D0081 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17085DD90939627C000D0081 /* Carbon.framework */; };
+ 1796636A093A122C00138851 /* Edit in TextMate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 17966367093A122C00138851 /* Edit in TextMate.mm */; };
+ 1796636B093A122C00138851 /* NSTextView: Edit in TextMate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 17966368093A122C00138851 /* NSTextView: Edit in TextMate.mm */; };
+ 1796636C093A122C00138851 /* WebView: Edit in TextMate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 17966369093A122C00138851 /* WebView: Edit in TextMate.mm */; };
+ 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; };
+ 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXBuildStyle section */
+ 014CEA420018CDE011CA2923 /* Debug */ = {
+ isa = PBXBuildStyle;
+ buildSettings = {
+ };
+ name = Debug;
+ };
+ 014CEA430018CDE011CA2923 /* Release */ = {
+ isa = PBXBuildStyle;
+ buildSettings = {
+ };
+ name = Release;
+ };
+/* End PBXBuildStyle section */
+
+/* Begin PBXFileReference section */
+ 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+ 089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
+ 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+ 17085DD90939627C000D0081 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
+ 17966367093A122C00138851 /* Edit in TextMate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "Edit in TextMate.mm"; path = "src/Edit in TextMate.mm"; sourceTree = "<group>"; };
+ 17966368093A122C00138851 /* NSTextView: Edit in TextMate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "NSTextView: Edit in TextMate.mm"; path = "src/NSTextView: Edit in TextMate.mm"; sourceTree = "<group>"; };
+ 17966369093A122C00138851 /* WebView: Edit in TextMate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "WebView: Edit in TextMate.mm"; path = "src/WebView: Edit in TextMate.mm"; sourceTree = "<group>"; };
+ 32DBCF630370AF2F00C91783 /* Edit in TextMate_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Edit in TextMate_Prefix.pch"; sourceTree = "<group>"; };
+ 8D5B49B6048680CD000E48DA /* Edit in TextMate.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Edit in TextMate.bundle"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Info.plist; sourceTree = "<group>"; };
+ D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8D5B49B3048680CD000E48DA /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */,
+ 17085DDA0939627C000D0081 /* Carbon.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 089C166AFE841209C02AAC07 /* Edit in TextMate */ = {
+ isa = PBXGroup;
+ children = (
+ 08FB77AFFE84173DC02AAC07 /* Classes */,
+ 32C88E010371C26100C91783 /* Other Sources */,
+ 089C167CFE841241C02AAC07 /* Resources */,
+ 089C1671FE841209C02AAC07 /* Frameworks and Libraries */,
+ 19C28FB8FE9D52D311CA2CBB /* Products */,
+ );
+ name = "Edit in TextMate";
+ sourceTree = "<group>";
+ };
+ 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ 17085DD90939627C000D0081 /* Carbon.framework */,
+ 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */,
+ 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */,
+ );
+ name = "Frameworks and Libraries";
+ sourceTree = "<group>";
+ };
+ 089C167CFE841241C02AAC07 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 8D5B49B7048680CD000E48DA /* Info.plist */,
+ 089C167DFE841241C02AAC07 /* InfoPlist.strings */,
+ );
+ name = Resources;
+ sourceTree = "<group>";
+ };
+ 08FB77AFFE84173DC02AAC07 /* Classes */ = {
+ isa = PBXGroup;
+ children = (
+ 17966367093A122C00138851 /* Edit in TextMate.mm */,
+ 17966368093A122C00138851 /* NSTextView: Edit in TextMate.mm */,
+ 17966369093A122C00138851 /* WebView: Edit in TextMate.mm */,
+ );
+ name = Classes;
+ sourceTree = "<group>";
+ };
+ 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */,
+ );
+ name = "Linked Frameworks";
+ sourceTree = "<group>";
+ };
+ 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 089C167FFE841241C02AAC07 /* AppKit.framework */,
+ D2F7E65807B2D6F200F64583 /* CoreData.framework */,
+ 089C1672FE841209C02AAC07 /* Foundation.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 19C28FB8FE9D52D311CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8D5B49B6048680CD000E48DA /* Edit in TextMate.bundle */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 32C88E010371C26100C91783 /* Other Sources */ = {
+ isa = PBXGroup;
+ children = (
+ 32DBCF630370AF2F00C91783 /* Edit in TextMate_Prefix.pch */,
+ );
+ name = "Other Sources";
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 8D5B49AC048680CD000E48DA /* Edit in TextMate */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "Edit in TextMate" */;
+ buildPhases = (
+ 8D5B49AF048680CD000E48DA /* Resources */,
+ 8D5B49B1048680CD000E48DA /* Sources */,
+ 8D5B49B3048680CD000E48DA /* Frameworks */,
+ );
+ buildRules = (
+ );
+ buildSettings = {
+ };
+ dependencies = (
+ );
+ name = "Edit in TextMate";
+ productInstallPath = "$(HOME)/Library/Bundles";
+ productName = "Edit in TextMate";
+ productReference = 8D5B49B6048680CD000E48DA /* Edit in TextMate.bundle */;
+ productType = "com.apple.product-type.bundle";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 089C1669FE841209C02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Edit in TextMate" */;
+ buildSettings = {
+ };
+ buildStyles = (
+ 014CEA420018CDE011CA2923 /* Debug */,
+ 014CEA430018CDE011CA2923 /* Release */,
+ );
+ hasScannedForEncodings = 1;
+ mainGroup = 089C166AFE841209C02AAC07 /* Edit in TextMate */;
+ projectDirPath = "";
+ targets = (
+ 8D5B49AC048680CD000E48DA /* Edit in TextMate */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8D5B49AF048680CD000E48DA /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8D5B49B1048680CD000E48DA /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 1796636A093A122C00138851 /* Edit in TextMate.mm in Sources */,
+ 1796636B093A122C00138851 /* NSTextView: Edit in TextMate.mm in Sources */,
+ 1796636C093A122C00138851 /* WebView: Edit in TextMate.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 089C167DFE841241C02AAC07 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 089C167EFE841241C02AAC07 /* English */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB913B08733D840010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "Edit in TextMate_Prefix.pch";
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Library/Bundles";
+ OTHER_LDFLAGS = (
+ "-undefined",
+ define_a_way,
+ );
+ PRODUCT_NAME = "Edit in TextMate";
+ WRAPPER_EXTENSION = bundle;
+ ZERO_LINK = YES;
+ };
+ name = Debug;
+ };
+ 1DEB913C08733D840010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = (
+ ppc,
+ i386,
+ );
+ GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "Edit in TextMate_Prefix.pch";
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Library/Bundles";
+ OTHER_LDFLAGS = (
+ "-undefined",
+ define_a_way,
+ );
+ PRODUCT_NAME = "Edit in TextMate";
+ WRAPPER_EXTENSION = bundle;
+ };
+ name = Release;
+ };
+ 1DEB913F08733D840010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+ };
+ name = Debug;
+ };
+ 1DEB914008733D840010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "Edit in TextMate" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB913B08733D840010E9CD /* Debug */,
+ 1DEB913C08733D840010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Edit in TextMate" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB913F08733D840010E9CD /* Debug */,
+ 1DEB914008733D840010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 089C1669FE841209C02AAC07 /* Project object */;
+}
7 Edit in TextMate_Prefix.pch
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'Edit in TextMate' target in the 'Edit in TextMate' project.
+//
+
+#ifdef __OBJC__
+ #import <Cocoa/Cocoa.h>
+#endif
BIN English.lproj/InfoPlist.strings
Binary file not shown.
17 Info
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>BundleName</key>
+ <string>Edit in TextMate.bundle</string>
+ <key>LoadBundleOnLaunch</key>
+ <string>YES</string>
+ <key>LocalizedNames</key>
+ <dict>
+ <key>English</key>
+ <string>Edit in TextMate</string>
+ </dict>
+ <key>NoMenuEntry</key>
+ <string>YES</string>
+</dict>
+</plist>
26 Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.macromates.edit_in_textmate</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSPrincipalClass</key>
+ <string>EditInTextMate</string>
+</dict>
+</plist>
14 src/Edit in TextMate.h
@@ -0,0 +1,14 @@
+//
+// Edit in TextMate.h
+//
+// Created by Allan Odgaard on 2005-11-26.
+// Copyright (c) 2005 MacroMates. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface EditInTextMate : NSObject
+{
+}
++ (void)externalEditString:(NSString*)aString forView:(NSView*)aView;
+@end
120 src/Edit in TextMate.mm
@@ -0,0 +1,120 @@
+//
+// Edit in TextMate.mm
+//
+// Created by Allan Odgaard on 2005-11-26.
+// Copyright (c) 2005 MacroMates. All rights reserved.
+//
+
+#import <Carbon/Carbon.h>
+#import "Edit in TextMate.h"
+
+// from ODBEditorSuite.h
+#define keyFileSender 'FSnd'
+#define kODBEditorSuite 'R*ch'
+#define kAEModifiedFile 'FMod'
+#define kAEClosedFile 'FCls'
+
+static NSMutableDictionary* OpenFiles;
+
+@implementation EditInTextMate
++ (void)willProcessFirstEventNotification:(id)aNotification
+{
+ NSAppleEventManager* eventManager = [NSAppleEventManager sharedAppleEventManager];
+ [eventManager setEventHandler:self andSelector:@selector(handleModifiedFileEvent:withReplyEvent:) forEventClass:kODBEditorSuite andEventID:kAEModifiedFile];
+ [eventManager setEventHandler:self andSelector:@selector(handleClosedFileEvent:withReplyEvent:) forEventClass:kODBEditorSuite andEventID:kAEClosedFile];
+}
+
++ (void)asyncEditStringWithOptions:(NSDictionary*)someOptions
+{
+ NSAutoreleasePool* pool = [NSAutoreleasePool new];
+
+ NSString* aString = [someOptions objectForKey:@"string"];
+ NSString* fileName = [someOptions objectForKey:@"fileName"];
+ [[aString dataUsingEncoding:NSUTF8StringEncoding] writeToFile:fileName atomically:NO];
+
+ BOOL success = [[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier:@"com.macromates.textmate" options:0L additionalEventParamDescriptor:nil launchIdentifier:nil];
+ if(!success)
+ return;
+
+ /* =========== */
+
+ NSData* targetBundleID = [@"com.macromates.textmate" dataUsingEncoding:NSUTF8StringEncoding];
+ NSAppleEventDescriptor* targetDescriptor = [NSAppleEventDescriptor descriptorWithDescriptorType:typeApplicationBundleID data:targetBundleID];
+ NSAppleEventDescriptor* appleEvent = [NSAppleEventDescriptor appleEventWithEventClass:kCoreEventClass eventID:kAEOpenDocuments targetDescriptor:targetDescriptor returnID:kAutoGenerateReturnID transactionID:kAnyTransactionID];
+ NSAppleEventDescriptor* replyDescriptor = nil;
+ NSAppleEventDescriptor* errorDescriptor = nil;
+ AEDesc reply = { typeNull, NULL };
+
+ [appleEvent setParamDescriptor:[NSAppleEventDescriptor descriptorWithDescriptorType:typeFileURL data:[[[NSURL fileURLWithPath:fileName] absoluteString] dataUsingEncoding:NSUTF8StringEncoding]] forKeyword:keyDirectObject];
+
+ UInt32 packageType = 0, packageCreator = 0;
+ CFBundleGetPackageInfo(CFBundleGetMainBundle(), &packageType, &packageCreator);
+ [appleEvent setParamDescriptor:[NSAppleEventDescriptor descriptorWithTypeCode:packageCreator] forKeyword:keyFileSender];
+
+ OSStatus status = AESend([appleEvent aeDesc], &reply, kAEWaitReply, kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
+ if(status == noErr)
+ {
+ replyDescriptor = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&reply] autorelease];
+ errorDescriptor = [replyDescriptor paramDescriptorForKeyword:keyErrorNumber];
+ if(errorDescriptor != nil)
+ status = [errorDescriptor int32Value];
+
+ if(status != noErr)
+ return;
+ }
+
+ [pool release];
+}
+
++ (void)externalEditString:(NSString*)aString forView:(NSView*)aView
+{
+ NSString* appName = [[[[NSWorkspace sharedWorkspace] activeApplication] objectForKey:@"NSApplicationName"] lowercaseString];
+ NSString* windowTitle = [[aView window] title] ?: @"untitled";
+ windowTitle = [[windowTitle componentsSeparatedByString:@"/"] componentsJoinedByString:@"-"];
+
+ NSString* fileName = [[NSString stringWithFormat:@"%@/%@.%@", NSTemporaryDirectory(), windowTitle, appName] stringByStandardizingPath];
+ NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:
+ aString, @"string",
+ aView, @"view",
+ fileName, @"fileName",
+ nil];
+
+ [OpenFiles setObject:options forKey:fileName];
+ [NSThread detachNewThreadSelector:@selector(asyncEditStringWithOptions:) toTarget:self withObject:options];
+}
+
++ (void)handleModifiedFileEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent
+{
+ NSAppleEventDescriptor* fileURL = [[event paramDescriptorForKeyword:keyDirectObject] coerceToDescriptorType:typeFileURL];
+ NSString* urlString = [[[NSString alloc] initWithData:[fileURL data] encoding:NSUTF8StringEncoding] autorelease];
+ NSString* fileName = [[[NSURL URLWithString:urlString] path] stringByStandardizingPath];
+
+ NSView* view = [[OpenFiles objectForKey:fileName] objectForKey:@"view"];
+ if([view window] && [view respondsToSelector:@selector(didModifyString:)])
+ {
+ [view performSelector:@selector(didModifyString:) withObject:[NSString stringWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:nil]];
+ }
+ else
+ {
+ NSLog(@"%s view %p, %@, window %@", _cmd, view, view, [view window]);
+ NSLog(@"%s options %@", _cmd, [[OpenFiles objectForKey:fileName] description]);
+ NSLog(@"%s all %@", _cmd, [OpenFiles description]);
+ NSBeep();
+ }
+}
+
++ (void)handleClosedFileEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent
+{
+ NSAppleEventDescriptor* fileURL = [[event paramDescriptorForKeyword:keyDirectObject] coerceToDescriptorType:typeFileURL];
+ NSString* urlString = [[[NSString alloc] initWithData:[fileURL data] encoding:NSUTF8StringEncoding] autorelease];
+ NSString* fileName = [[[NSURL URLWithString:urlString] path] stringByStandardizingPath];
+ [OpenFiles removeObjectForKey:fileName];
+ [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
+}
+
++ (void)load
+{
+ OpenFiles = [NSMutableDictionary new];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willProcessFirstEventNotification:) name:NSAppleEventManagerWillProcessFirstEventNotification object:nil];
+}
+@end
39 src/NSTextView: Edit in TextMate.mm
@@ -0,0 +1,39 @@
+//
+// NSTextView: Edit in TextMate.mm
+//
+// Created by Allan Odgaard on 2005-11-27.
+// Copyright (c) 2005 MacroMates. All rights reserved.
+//
+
+#import "Edit in TextMate.h"
+
+@interface NSTextView (EditInTextMate)
+- (void)editInTextMate:(id)sender;
+@end
+
+@implementation NSTextView (EditInTextMate)
+- (void)editInTextMate:(id)sender
+{
+ if(![self isEditable])
+ return (void)NSBeep();
+
+ NSString* str = [[self textStorage] string];
+ NSRange selectedRange = [self selectedRange];
+ if(selectedRange.length == 0)
+ selectedRange = NSMakeRange(0, [str length]);
+
+ [EditInTextMate externalEditString:[str substringWithRange:selectedRange] forView:self];
+}
+
+- (void)didModifyString:(NSString*)newString
+{
+ BOOL skipReselect = NO;
+ NSRange selectedRange = [self selectedRange];
+ if(skipReselect = selectedRange.length == 0)
+ selectedRange = NSMakeRange(0, [[self textStorage] length]);
+
+ [[self textStorage] replaceCharactersInRange:selectedRange withString:newString];
+ if(!skipReselect)
+ [self setSelectedRange:NSMakeRange(selectedRange.location, [newString length])];
+}
+@end
198 src/WebView: Edit in TextMate.mm
@@ -0,0 +1,198 @@
+//
+// WebView: Edit in TextMate.mm
+//
+// Created by Allan Odgaard on 2005-11-27.
+// Copyright (c) 2005 MacroMates. All rights reserved.
+//
+
+#import <WebKit/WebKit.h>
+#import "Edit in TextMate.h"
+
+@interface WebView (EditInTextMate)
+- (void)editInTextMate:(id)sender;
+@end
+
+struct convert_dom_to_text
+{
+ convert_dom_to_text (DOMTreeWalker* treeWalker) : string([NSMutableString new]), quoteLevel(0), pendingFlush(NO), didOutputText(NO), atBeginOfLine(YES) { visit_nodes(treeWalker); }
+ ~convert_dom_to_text () { [string autorelease]; }
+ operator NSString* () const { return string; }
+
+private:
+ void enter_block_tag ()
+ {
+ pendingFlush |= didOutputText;
+ didOutputText = NO;
+ }
+
+ void leave_block_tag ()
+ {
+ pendingFlush |= didOutputText;
+ didOutputText = NO;
+ }
+
+ void output_text (NSString* str)
+ {
+/* NSMutableCharacterSet* whitespace = [NSMutableCharacterSet whitespaceCharacterSet];
+ [whitespace removeCharactersInRange:NSMakeRange(0xA0, 1)];
+ str = [str stringByTrimmingCharactersInSet:whitespace];
+*/
+ while([str hasPrefix:@" "])
+ str = [str substringFromIndex:1];
+
+ while([str hasSuffix:@" "])
+ str = [str substringToIndex:[str length]-1];
+
+ if([str isEqualToString:@""])
+ return;
+ str = [[str componentsSeparatedByString:[NSString stringWithUTF8String:" "]] componentsJoinedByString:@" "];
+
+ if(pendingFlush)
+ {
+ [string appendString:@"\n"];
+ pendingFlush = NO;
+ atBeginOfLine = YES;
+ }
+
+ if(atBeginOfLine && quoteLevel)
+ {
+ for(unsigned i = 0; i < quoteLevel; i++)
+ [string appendString:@"> "];
+ }
+
+ [string appendString:str];
+ atBeginOfLine = NO;
+ didOutputText = YES;
+ }
+
+ void visit_nodes (DOMTreeWalker* treeWalker);
+
+ NSMutableString* string;
+ unsigned quoteLevel;
+ BOOL pendingFlush;
+ BOOL didOutputText;
+ BOOL atBeginOfLine;
+};
+
+void convert_dom_to_text::visit_nodes (DOMTreeWalker* treeWalker)
+{
+ for(DOMNode* node = [treeWalker currentNode]; node; node = [treeWalker nextSibling])
+ {
+ if([node nodeType] == DOM_TEXT_NODE)
+ output_text([node nodeValue]);
+ else if([[[node nodeName] uppercaseString] isEqualToString:@"BR"])
+ output_text(@"\n"), (atBeginOfLine = YES), (didOutputText = NO);
+ else if([[[node nodeName] uppercaseString] isEqualToString:@"DIV"])
+ enter_block_tag();
+ else if([[[node nodeName] uppercaseString] isEqualToString:@"BLOCKQUOTE"])
+ enter_block_tag(), ++quoteLevel;
+ else if([[[node nodeName] uppercaseString] isEqualToString:@"P"])
+ enter_block_tag();
+
+ if([treeWalker firstChild])
+ {
+ visit_nodes(treeWalker);
+ [treeWalker parentNode];
+ }
+
+ if([[[node nodeName] uppercaseString] isEqualToString:@"DIV"])
+ leave_block_tag();
+ else if([[[node nodeName] uppercaseString] isEqualToString:@"BLOCKQUOTE"])
+ leave_block_tag(), --quoteLevel;
+ else if([[[node nodeName] uppercaseString] isEqualToString:@"P"])
+ leave_block_tag();
+ }
+}
+
+@implementation WebView (EditInTextMate)
+- (void)editInTextMate:(id)sender
+{
+ if(![self isEditable])
+ return (void)NSBeep();
+
+ DOMDocumentFragment* selection = [[self selectedDOMRange] cloneContents];
+ if(!selection)
+ selection = ([self selectAll:nil], [[self selectedDOMRange] cloneContents]);
+
+ NSLog(@"%s %@", _cmd, [(DOMHTMLElement*)[[[self mainFrame] DOMDocument] documentElement] outerHTML]);
+ NSString* str = convert_dom_to_text([[[self mainFrame] DOMDocument] createTreeWalker:selection :DOM_SHOW_ALL :nil :YES]);
+ if(![str isEqualToString:@""])
+ {
+ while([str hasSuffix:@"\n\n"])
+ str = [str substringToIndex:[str length]-1];
+
+ [EditInTextMate externalEditString:str forView:self];
+ }
+}
+
+- (void)didModifyString:(NSString*)newString
+{
+ NSMutableString* res = [NSMutableString string];
+ unsigned quoteLevel = 0;
+ NSArray* lines = [newString componentsSeparatedByString:@"\n"];
+ for(unsigned i = 0; i != [lines count]; i++)
+ {
+ NSString* line = [lines objectAtIndex:i];
+ unsigned newQuoteLevel = 0;
+ while([line hasPrefix:@"> "])
+ {
+ line = [line substringFromIndex:2];
+ newQuoteLevel++;
+ }
+
+ if(newQuoteLevel > quoteLevel)
+ {
+ for(unsigned j = 0; j != newQuoteLevel - quoteLevel; j++)
+ [res appendString:@"<BLOCKQUOTE type=\"cite\">"];
+ }
+ else if(newQuoteLevel < quoteLevel)
+ {
+ for(unsigned j = 0; j != quoteLevel - newQuoteLevel; j++)
+ [res appendString:@"</BLOCKQUOTE>"];
+ }
+ quoteLevel = newQuoteLevel;
+
+ if([line isEqualToString:@""])
+ {
+ [res appendString:@"<DIV><BR></DIV>"];
+ }
+ else
+ {
+ [res appendString:@"<DIV>"];
+
+// if([line rangeOfString:@" "].location != NSNotFound)
+ {
+ char const* str = [line UTF8String];
+ size_t len = strlen(str);
+ char* buf = new char[len+1];
+ size_t j = 0;
+ for(size_t i = 0; i != len; i++)
+ {
+ if(str[i] == ' ' && (str[i+1] == ' ' || !str[i+1]))
+ {
+ buf[j++] = '\xC2';
+ buf[j++] = '\xA0';
+ }
+ else
+ {
+ buf[j++] = str[i];
+ }
+ }
+ buf[j] = 0;
+ line = [NSString stringWithUTF8String:buf] ?: @"";
+ }
+
+ line = [[line componentsSeparatedByString:@"&"] componentsJoinedByString:@"&amp;"];
+ line = [[line componentsSeparatedByString:@"<"] componentsJoinedByString:@"&lt;"];
+ line = [[line componentsSeparatedByString:@">"] componentsJoinedByString:@"&gt;"];
+ [res appendString:line];
+ [res appendString:@"</DIV>"];
+ }
+ }
+ NSLog(@"%s %@", _cmd, res);
+
+ [self replaceSelectionWithMarkupString:res];
+ if(![[self selectedDOMRange] cloneContents])
+ [self selectAll:nil];
+}
+@end

0 comments on commit 7065b60

Please sign in to comment.
Something went wrong with that request. Please try again.