Skip to content

Instantly share code, notes, and snippets.

@knu
Created July 5, 2011 04:37
Show Gist options
  • Save knu/1064253 to your computer and use it in GitHub Desktop.
Save knu/1064253 to your computer and use it in GitHub Desktop.
Make iTerm2's copy also save the selected region in rich text format.
From 9fbd75b0ba43d2e37aae4ed1b48752baa1908cd3 Mon Sep 17 00:00:00 2001
From: Akinori MUSHA <[email protected]>
Date: Tue, 5 Jul 2011 11:00:32 +0900
Subject: [PATCH] Make copy() also save the selected text in rich text format.
---
Headers/iTerm/PTYTextView.h | 2 +
PTYTextView.m | 161 +++++++++++++++++++++++++++++++++++++++++-
2 files changed, 159 insertions(+), 4 deletions(-)
diff --git a/Headers/iTerm/PTYTextView.h b/Headers/iTerm/PTYTextView.h
index 48f3641..eb1aee9 100644
--- a/Headers/iTerm/PTYTextView.h
+++ b/Headers/iTerm/PTYTextView.h
@@ -280,6 +280,7 @@ typedef struct PTYFontInfo PTYFontInfo;
- (void)rightMouseDragged:(NSEvent *)event;
- (void)scrollWheel:(NSEvent *)event;
- (NSString *)contentFromX:(int)startx Y:(int)starty ToX:(int)endx Y:(int)endy pad: (BOOL) pad;
+- (NSAttributedString *)attributedContentFromX:(int)startx Y:(int)starty ToX:(int)endx Y:(int)endy pad: (BOOL) pad;
- (NSString*)contentInBoxFromX:(int)startx Y:(int)starty ToX:(int)nonInclusiveEndx Y:(int)endy pad: (BOOL) pad;
- (NSString *)selectedText;
- (NSString *)selectedTextWithPad: (BOOL) pad;
@@ -526,6 +527,7 @@ typedef enum {
isComplex:(BOOL)complex
fgColor:(int)fgColor
renderBold:(BOOL*)renderBold;
+- (NSDictionary *)charAttributes:(screen_char_t)c;
- (PTYFontInfo*)getOrAddFallbackFont:(NSFont*)font;
- (void)releaseAllFallbackFonts;
diff --git a/PTYTextView.m b/PTYTextView.m
index ba05e3f..4f59c01 100644
--- a/PTYTextView.m
+++ b/PTYTextView.m
@@ -2855,6 +2855,102 @@ static double EuclideanDistance(NSPoint p1, NSPoint p2) {
return result;
}
+static inline void appendToAttributedString(NSMutableAttributedString *target, NSString *source, NSDictionary *attributes)
+{
+ NSAttributedString *string;
+
+ if (attributes == nil) {
+ string = [[NSAttributedString alloc] initWithString:source];
+ } else {
+ string = [[NSAttributedString alloc] initWithString:source
+ attributes:attributes];
+ }
+ [target appendAttributedString:string];
+ [string release];
+}
+
+- (NSAttributedString*)attributedContentInBoxFromX:(int)startx Y:(int)starty ToX:(int)nonInclusiveEndx Y:(int)endy pad: (BOOL) pad
+{
+ int i;
+ NSMutableAttributedString* result = [[[NSMutableAttributedString alloc] initWithString:@""] autorelease];
+ for (i = starty; i < endy; ++i) {
+ NSAttributedString* line = [self attributedContentFromX:startx Y:i ToX:nonInclusiveEndx Y:i pad:pad];
+ [result appendAttributedString:line];
+ if (i < endy-1) {
+ appendToAttributedString(result, @"\n", nil);
+ }
+ }
+ return result;
+}
+
+- (NSAttributedString *)attributedContentFromX:(int)startx
+ Y:(int)starty
+ ToX:(int)nonInclusiveEndx
+ Y:(int)endy
+ pad:(BOOL) pad
+{
+ int endx = nonInclusiveEndx-1;
+ int width = [dataSource width];
+ NSMutableAttributedString* result = [[[NSMutableAttributedString alloc] initWithString:@""] autorelease];
+ int y, x1, x2;
+ screen_char_t *theLine;
+ BOOL endOfLine;
+ int i;
+
+ for (y = starty; y <= endy; y++) {
+ theLine = [dataSource getLineAtIndex:y];
+
+ x1 = y == starty ? startx : 0;
+ x2 = y == endy ? endx : width-1;
+ for ( ; x1 <= x2; x1++) {
+ screen_char_t c = theLine[x1];
+ if (c.code == TAB_FILLER) {
+ // Convert orphan tab fillers (those without a subsequent
+ // tab character) into spaces.
+ if ([self isTabFillerOrphanAtX:x1 Y:y]) {
+ appendToAttributedString(result, @" ", [self charAttributes:c]);
+ }
+ } else if (c.code != DWC_RIGHT &&
+ c.code != DWC_SKIP) {
+ if (c.code == 0) { // end of line?
+ // If there is no text after this, insert a hard line break.
+ endOfLine = YES;
+ for (i = x1 + 1; i <= x2 && endOfLine; i++) {
+ if (theLine[i].code != 0) {
+ endOfLine = NO;
+ }
+ }
+ if (endOfLine) {
+ if (pad) {
+ for (i = x1; i <= x2; i++) {
+ appendToAttributedString(result, @" ", [self charAttributes:theLine[i]]);
+ }
+ }
+ if (y < endy && theLine[width].code == EOL_HARD) {
+ appendToAttributedString(result, @"\n", [self charAttributes:theLine[width]]);
+ }
+ break;
+ } else {
+ // represent end-of-line blank with space
+ appendToAttributedString(result, @" ", [self charAttributes:c]);
+ }
+ } else if (x1 == x2 &&
+ y < endy &&
+ theLine[width].code == EOL_HARD) {
+ // Hard line break
+ appendToAttributedString(result, ScreenCharToStr(&c), [self charAttributes:c]);
+ appendToAttributedString(result, @"\n", [self charAttributes:theLine[width]]);
+ } else {
+ // Normal character
+ appendToAttributedString(result, ScreenCharToStr(&c), [self charAttributes:c]);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
- (IBAction)selectAll:(id)sender
{
// set the selection region for the whole text
@@ -2897,6 +2993,31 @@ static double EuclideanDistance(NSPoint p1, NSPoint p2) {
}
}
+- (NSAttributedString *)selectedAttributedText
+{
+ return [self selectedAttributedTextWithPad:NO];
+}
+
+- (NSAttributedString *)selectedAttributedTextWithPad:(BOOL)pad
+{
+ if (startX <= -1) {
+ return nil;
+ }
+ if (selectMode == SELECT_BOX) {
+ return [self attributedContentInBoxFromX:startX
+ Y:startY
+ ToX:endX
+ Y:endY
+ pad:pad];
+ } else {
+ return ([self attributedContentFromX:startX
+ Y:startY
+ ToX:endX
+ Y:endY
+ pad:pad]);
+ }
+}
+
- (NSString *)content
{
return [self contentFromX:0
@@ -2942,13 +3063,21 @@ static double EuclideanDistance(NSPoint p1, NSPoint p2) {
- (void)copy:(id)sender
{
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
- NSString *copyString;
-
- copyString = [self selectedText];
+ NSString *copyString = [self selectedText];
+ NSAttributedString *copyAttributedString = [self selectedAttributedText];
if (copyString) {
- [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
+ if (copyAttributedString) {
+ [pboard declareTypes:[NSArray arrayWithObjects:NSStringPboardType,
+ NSRTFPboardType, nil] owner:self];
+ } else {
+ [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
+ }
[pboard setString:copyString forType:NSStringPboardType];
+ if (copyAttributedString) {
+ NSData *RTFData = [copyAttributedString RTFFromRange:NSMakeRange(0, [copyAttributedString length]) documentAttributes:nil];
+ [pboard setData:RTFData forType:NSRTFPboardType];
+ }
}
[[PasteboardHistory sharedInstance] save:copyString];
@@ -4229,6 +4358,30 @@ static double EuclideanDistance(NSPoint p1, NSPoint p2) {
return theFont;
}
+// Returns a dictionary to pass to NSAttributedString.
+- (NSDictionary *)charAttributes:(screen_char_t)c
+{
+ NSColor *fgColor = [self colorForCode:c.foregroundColor alternateSemantics:c.alternateForegroundSemantics bold:c.bold];
+ NSColor *bgColor = [self colorForCode:c.backgroundColor alternateSemantics:c.alternateBackgroundSemantics bold:false];
+ int underlineStyle = c.underline ? (NSUnderlineStyleSingle|NSUnderlineByWordMask) : 0;
+ BOOL isBold = c.bold;
+ NSFont *font = [self getFontForChar:c.code
+ isComplex:c.complexChar
+ renderBold:&isBold]->font;
+
+ if (isBold) {
+ font = [[NSFontManager sharedFontManager] convertFont: font
+ toHaveTrait: NSBoldFontMask];
+ }
+
+ return [NSDictionary dictionaryWithObjectsAndKeys:
+ fgColor, NSForegroundColorAttributeName,
+ bgColor, NSBackgroundColorAttributeName,
+ font, NSFontAttributeName,
+ [NSNumber numberWithInt:underlineStyle], NSUnderlineStyleAttributeName,
+ NULL];
+}
+
// Returns true if the sequence of characters starting at (x, y) is not repeated
// TAB_FILLERs followed by a tab.
- (BOOL)isTabFillerOrphanAtX:(int)x Y:(int)y
--
1.7.5.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment