Skip to content
Snippets Groups Projects
Commit 82aa1ad5 authored by George Nachman's avatar George Nachman
Browse files

Initial hackery

parent 34bf00b1
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -22,4 +22,6 @@
// Cancels the lookup for |hostname|.
- (void)cancelRequestForHostname:(NSString *)hostname;
 
- (BOOL)isLookingUpHostname:(NSString *)hostname;
@end
Loading
Loading
@@ -97,7 +97,6 @@ static NSImage* alertImage;
@interface PTYTextView () <iTermSelectionDelegate>
// Set the hostname this view is currently waiting for AsyncHostLookupController to finish looking
// up.
@property(nonatomic, copy) NSString *currentUnderlineHostname;
@property(nonatomic, retain) iTermSelection *selection;
@property(nonatomic, retain) NSColor *cachedBackgroundColor;
@property(nonatomic, retain) NSColor *unfocusedSelectionColor;
Loading
Loading
@@ -146,7 +145,8 @@ static NSImage* alertImage;
PTYFontInfo *secondaryFont; // non-ascii font, only used if self.useNonAsciiFont is set.
 
// Underlined selection range (inclusive of all values), indicating clickable url.
VT100GridWindowedRange _underlineRange;
// Each subselection's object, if set, is a string whose name lookup is pending.
iTermSelection _underlinedLinks;
BOOL mouseDown;
BOOL mouseDragged;
BOOL mouseDownOnSelection;
Loading
Loading
@@ -405,7 +405,7 @@ static NSImage* alertImage;
_selection = [[iTermSelection alloc] init];
_selection.delegate = self;
_oldSelection = [_selection copy];
_underlineRange = VT100GridWindowedRangeMake(VT100GridCoordRangeMake(-1, -1, -1, -1), 0, 0);
_underlinedLinks = [[iTermSelection alloc] init];
markedText = nil;
gettimeofday(&lastBlink, NULL);
[[self window] useOptimizedDrawing:YES];
Loading
Loading
@@ -526,14 +526,23 @@ static NSImage* alertImage;
[threeFingerTapGestureRecognizer_ release];
 
[_findContext release];
if (self.currentUnderlineHostname) {
[[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
}
[_currentUnderlineHostname release];
[self removeUnderlines];
[_underlinedLinks release];
 
[super dealloc];
}
 
- (void)removeUnderlines {
for (iTermSubSelection *sub in [_underlinedLinks allSubSelections]) {
if (sub.object) {
[[AsyncHostLookupController sharedInstance] cancelRequestForHostname:(NSString *)sub.object];
}
}
[_underlinedLinks clearSelection];
[self setNeedsDisplay:YES]; // It would be better to just display the underlined/formerly underlined area.
[self updateTrackingAreas]; // Cause mouseMoved to be (not) called on movement if cmd is down (up).
}
- (NSString *)description {
return [NSString stringWithFormat:@"<PTYTextView: %p frame=%@ visibleRect=%@ dataSource=%@>",
self,
Loading
Loading
@@ -619,7 +628,7 @@ static NSImage* alertImage;
 
- (BOOL)resignFirstResponder
{
[self removeUnderline];
[self removeUnderlines];
return YES;
}
 
Loading
Loading
@@ -2598,18 +2607,6 @@ NSMutableArray* screens=0;
}
}
 
// Reset underlined chars indicating cmd-clicakble url.
- (void)removeUnderline
{
_underlineRange = VT100GridWindowedRangeMake(VT100GridCoordRangeMake(-1, -1, -1, -1), 0, 0);
if (self.currentUnderlineHostname) {
[[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
}
self.currentUnderlineHostname = nil;
[self setNeedsDisplay:YES]; // It would be better to just display the underlined/formerly underlined area.
[self updateTrackingAreas]; // Cause mouseMoved to be (not) called on movement if cmd is down (up).
}
- (BOOL)reportingMouseClicks {
if ([self xtermMouseReporting]) {
VT100Terminal *terminal = [_dataSource terminal];
Loading
Loading
@@ -2654,61 +2651,45 @@ NSMutableArray* screens=0;
0,
0)];
NSPoint locationInTextView = [self convertPoint:windowRect.origin fromView: nil];
if (!NSPointInRect(locationInTextView, [self bounds])) {
[self removeUnderline];
return;
}
NSPoint viewPoint = [self windowLocationToRowCol:windowRect.origin];
int x = viewPoint.x;
int y = viewPoint.y;
if (y < 0) {
[self removeUnderline];
return;
} else {
URLAction *action = [self urlActionForClickAtX:x
y:y
respectingHardNewlines:![iTermSettingsModel ignoreHardNewlinesInURLs]];
if (action) {
_underlineRange = action.range;
if (action.actionType == kURLActionOpenURL) {
NSURL *url = [NSURL URLWithString:action.string];
if (![url.host isEqualToString:self.currentUnderlineHostname]) {
if (self.currentUnderlineHostname) {
[[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
}
if (NSPointInRect(locationInTextView, [self bounds])) {
NSPoint viewPoint = [self windowLocationToRowCol:windowRect.origin];
int x = viewPoint.x;
int y = viewPoint.y;
if (y >= 0) {
URLAction *action = [self urlActionForClickAtX:x
y:y
respectingHardNewlines:![iTermSettingsModel ignoreHardNewlinesInURLs]];
if (action) {
iTermSubSelection *sub = [iTermSubSelection subSelectionWithRange:action.range
mode:kiTermSelectionModeCharacter];
if ([_underlinedLinks containsSubSelection:sub]) {
return;
}
[self removeUnderlines];
[_underlinedLinks addSubSelection:sub];
if (action.actionType == kURLActionOpenURL) {
NSURL *url = [NSURL URLWithString:action.string];
if (url && url.host) {
self.currentUnderlineHostname = url.host;
sub.object = url.host;
[[AsyncHostLookupController sharedInstance] getAddressForHost:url.host
completion:^(BOOL ok, NSString *hostname) {
if (!ok) {
[[NSNotificationCenter defaultCenter] postNotificationName:kHostnameLookupFailed
object:hostname];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:kHostnameLookupSucceeded
object:hostname];
}
}];
if (!ok) {
[[NSNotificationCenter defaultCenter] postNotificationName:kHostnameLookupFailed
object:hostname];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:kHostnameLookupSucceeded
object:hostname];
}
}];
}
}
} else {
if (self.currentUnderlineHostname) {
[[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
}
self.currentUnderlineHostname = nil;
return;
}
} else {
[self removeUnderline];
return;
}
}
} else {
[self removeUnderline];
return;
}
[self setNeedsDisplay:YES]; // It would be better to just display the underlined/formerly underlined area.
[self updateTrackingAreas]; // Cause mouseMoved to be (not) called on movement if cmd is down (up).
[self removeUnderlines];
}
 
- (void)flagsChanged:(NSEvent *)theEvent
Loading
Loading
@@ -5820,44 +5801,6 @@ static double EuclideanDistance(NSPoint p1, NSPoint p2) {
return memoizedContrastingColor_;
}
 
- (NSRange)underlinedRangeOnLine:(int)row {
if (_underlineRange.coordRange.start.x < 0) {
return NSMakeRange(0, 0);
}
if (row == _underlineRange.coordRange.start.y && row == _underlineRange.coordRange.end.y) {
// Whole underline is on one line.
const int start = VT100GridWindowedRangeStart(_underlineRange).x;
const int end = VT100GridWindowedRangeEnd(_underlineRange).x;
return NSMakeRange(start, end - start);
} else if (row == _underlineRange.coordRange.start.y) {
// Underline spans multiple lines, starting at this one.
const int start = VT100GridWindowedRangeStart(_underlineRange).x;
const int end =
_underlineRange.columnWindow.length > 0 ? VT100GridRangeMax(_underlineRange.columnWindow) + 1
: [_dataSource width];
return NSMakeRange(start, end - start);
} else if (row == _underlineRange.coordRange.end.y) {
// Underline spans multiple lines, ending at this one.
const int start =
_underlineRange.columnWindow.length > 0 ? _underlineRange.columnWindow.location : 0;
const int end = VT100GridWindowedRangeEnd(_underlineRange).x;
return NSMakeRange(start, end - start);
} else if (row > _underlineRange.coordRange.start.y && row < _underlineRange.coordRange.end.y) {
// Underline spans multiple lines. This is not the first or last line, so all chars
// in it are underlined.
const int start =
_underlineRange.columnWindow.length > 0 ? _underlineRange.columnWindow.location : 0;
const int end =
_underlineRange.columnWindow.length > 0 ? VT100GridRangeMax(_underlineRange.columnWindow) + 1
: [_dataSource width];
return NSMakeRange(start, end - start);
} else {
// No selection on this line.
return NSMakeRange(0, 0);
}
}
- (CRun *)_constructRuns:(NSPoint)initialPoint
theLine:(screen_char_t *)theLine
row:(int)row
Loading
Loading
@@ -5881,12 +5824,25 @@ static double EuclideanDistance(NSPoint p1, NSPoint p2) {
int lastBold = 2; // Bold is a one-bit field so it can never equal 2.
NSColor *lastColor = nil;
CGFloat curX = 0;
NSRange underlinedRange = [self underlinedRangeOnLine:row];
const int underlineStartsAt = underlinedRange.location;
const int underlineEndsAt = NSMaxRange(underlinedRange);
NSIndexSet *underlinedIndices = [_underlinedLinks selectedIndexesOnLine:row];
NSMutableIndexSet *resolvedUnderlinedIndices = nil;
if ([underlinedIndices count]) {
resolvedUnderlinedIndices = [NSMutableIndexSet indexSet];
VT100GridCoordRange rowRange = VT100GridRangeMake(indexRange.location,
row,
indexRange.location + indexRange.length,
row);
[_underlinedLinks enumerateSubSelectionsInRange:rowRange
block:^(iTermSubSelection *sub,
VT100GridCoordRange intersectingCoordRange) {
NSRange intersectingRange = NSMakeRange(intersectingCoordRange.start.x,
intersectingCoordRange.end.x);
[resolvedUnderlinedIndices addIndexesInRange:intersectingRange];
}];
}
const BOOL dimOnlyText = _colorMap.dimOnlyText;
for (int i = indexRange.location; i < indexRange.location + indexRange.length; i++) {
inUnderlinedRange = (i >= underlineStartsAt && i < underlineEndsAt);
inUnderlinedRange = [underlinedIndices containsIndex:i];
if (theLine[i].code == DWC_RIGHT) {
continue;
}
Loading
Loading
@@ -6031,8 +5987,13 @@ static double EuclideanDistance(NSPoint p1, NSPoint p2) {
if (theLine[i].image) {
thisCharString = @"I";
}
if (inUnderlinedRange && !self.currentUnderlineHostname) {
attrs.color = [NSColor colorWithCalibratedRed:0.023 green:0.270 blue:0.678 alpha:1];
if (inUnderlinedRange && [resolvedUnderlinedIndices containsIndex:i]) {
// Underlined URLs with valid resolved hostnames get a blue underline, otherwise
// they get the same color as text.
attrs.color = [NSColor colorWithCalibratedRed:0.023
green:0.270
blue:0.678
alpha:1];
}
if (!currentRun) {
firstRun = currentRun = malloc(sizeof(CRun));
Loading
Loading
@@ -7818,17 +7779,37 @@ static double EuclideanDistance(NSPoint p1, NSPoint p2) {
}
 
- (void)hostnameLookupFailed:(NSNotification *)notification {
if ([[notification object] isEqualToString:self.currentUnderlineHostname]) {
self.currentUnderlineHostname = nil;
[self removeUnderline];
_underlineRange = VT100GridWindowedRangeMake(VT100GridCoordRangeMake(-1, -1, -1, -1), 0, 0);
NSString *hostname = [notification object];
NSMutableArray *subsToRemove = [NSMutableArray array];
for (iTermSubSelection *sub in [_underlinedLinks allSubSelections]) {
NSString *subHostname = (NSString *)sub.object;
if ([subHostname isEqualToString:hostname]) {
[subsToRemove addObject:sub];
}
}
for (iTermSubSelection *sub in subsToRemove) {
[_underlinedLinks removeSubSelection:sub];
}
if ([subsToRemove count]) {
[self setNeedsDisplay:YES];
}
}
 
- (void)hostnameLookupSucceeded:(NSNotification *)notification {
if ([[notification object] isEqualToString:self.currentUnderlineHostname]) {
self.currentUnderlineHostname = nil;
NSString *hostname = [notification object];
// If any hostname in the current set of underlined links matches the lookup, redraw ourselves.
BOOL found = NO;
for (iTermSubSelection *sub in [_underlinedLinks allSubSelections]) {
NSString *subHostname = (NSString *)sub.object;
if ([subHostname isEqualToString:hostname]) {
found = YES;
sub.object = nil;
}
}
if (found) {
[self setNeedsDisplay:YES];
}
}
Loading
Loading
Loading
Loading
@@ -141,6 +141,13 @@ NS_INLINE VT100GridCoord VT100GridWindowedRangeEnd(VT100GridWindowedRange range)
return coord;
}
 
NS_INLINE BOOL VT100GridWindowedRangeEquals(VT100GridWindowedRange a, VT100GridWindowedRange b) {
return (VT100GridCoordEquals(a.coordRange.start, b.coordRange.start) &&
VT100GridCoordEquals(a.coordRange.end, b.coordRange.end) &&
a.columnWindow.location == b.columnWindow.location &&
a.columnWindow.length == b.columnWindow.length);
}
// Ascending: a < b
// Descending: a > b
// Same: a == b
Loading
Loading
@@ -227,6 +234,53 @@ NS_INLINE long long VT100GridCoordRangeLength(VT100GridCoordRange range, int gri
return VT100GridCoordDistance(range.start, range.end, gridWidth);
}
 
// Returns a range where start is before end.
NS_INLINE VT100GridCoordRange VT100GridCoordRangeAscending(VT100GridCoordRange range) {
if (VT100GridCoordOrder(range.start, range.end) == NSOrderedDescending) {
return VT100GridCoordRangeMake(range.end.x, range.end.y, range.start.x, range.start.y);
} else {
return range;
}
}
NS_INLINE VT100GridCoordRange VT100GridCoordRangeIntersection(VT100GridCoordRange a, VT100GridCoordRange b) {
a = VT100GridCoordRangeAscending(a);
b = VT100GridCoordRangeAscending(b);
VT100GridCorod maxOfMins;
if (VT100GridCoordOrder(a.start, b.start) == NSOrderedAscending) { // a < b
maxOfMins = b.start;
} else {
maxOfMins = a.start;
}
VT100GridCoord minOfMaxes;
if (VT100GridCoordOrder(a.end, b.end) == NSOrderedDescending) { // a < b
minOfMaxes = a.end;
} else {
minOfMaxes = b.end;
}
// If maxOfMins (X) < minOfMaxes (Y) then there is an intersection.
// X Y X,Y* X Y X Y Y X*
/ | | | | | | | | |
// aaaaa aaaa aaaaaaaa aaaaa aaaa
// bbbbbb bbbb bbb bbbb bb
if (VT100GridCoordOrder(maxOfMins, minOfMaxes) == NSOrderedAscending) {
return VT100GridCoordRangeMake(maxOfMins.x, maxOfMins.y, minOfMaxes.x, minOfMaxes.y);
} else {
return VT100GridCoordRangeMake(-1, -1, -1, -1);
}
}
NS_INLINE BOOL VT100GridCoordRangesIntersect(VT100GridCoordRange a, VT100GridCoordRange b) {
VT100GridCoordRange intersection = VT100GridCoordRangeIntersection(a, b);
return !(intersection.start.x == -1 &&
intersection.start.y == -1 &&
intersection.end.x == -1 &&
intersection.end.y == -1);
}
// Returns the coord of the last char inside the run.
NS_INLINE VT100GridCoord VT100GridRunMax(VT100GridRun run, int width) {
VT100GridCoord coord = run.origin;
Loading
Loading
Loading
Loading
@@ -54,6 +54,7 @@ typedef enum {
@property(nonatomic, assign) VT100GridWindowedRange range;
@property(nonatomic, assign) iTermSelectionMode selectionMode;
@property(nonatomic, assign) BOOL connected; // If connected, no newline occurs before the next sub
@property(nonatomic, retain) NSObject *object; // User-supplied object.
 
+ (instancetype)subSelectionWithRange:(VT100GridWindowedRange)range
mode:(iTermSelectionMode)mode;
Loading
Loading
@@ -145,12 +146,24 @@ typedef enum {
// Add a range to the set of selections.
- (void)addSubSelection:(iTermSubSelection *)sub;
 
// Remove a subselection.
- (void)removeSubSelection:(iTermSubSelection *)sub;
// Contains an equal subselection?
- (BOOL)containsSubSelection:(iTermSubSelection *)sub;
// Returns the indexes of characters selected on a given line.
- (NSIndexSet *)selectedIndexesOnLine:(int)line;
 
// Calls the block for each selected range.
- (void)enumerateSelectedRanges:(void (^)(VT100GridWindowedRange range, BOOL *stop, BOOL eol))block;
 
// Calls the block for all subselections that intersect |range|. If a subselection has multiple
// intersecting ranges (due to a window) then the block will be called more than once for the same
// subselection.
- (void)enumerateSubSelectionsInRange:(VT100GridRange)range
block:(void (^)(iTermSubSelection *, VT100GridCoordRange))block;
// Changes the first/last range.
- (void)setFirstRange:(VT100GridWindowedRange)firstRange mode:(iTermSelectionMode)mode;
- (void)setLastRange:(VT100GridWindowedRange)lastRange mode:(iTermSelectionMode)mode;
Loading
Loading
Loading
Loading
@@ -19,6 +19,18 @@
return sub;
}
 
- (void)dealloc {
[_object release];
[super dealloc];
}
- (BOOL)isEqual:(id)object {
return (VT100GridWindowedRangeEquals(self.range, other.range) &&
self.selectionMode == other.selectionMode &&
self.connected == other.connected &&
[self.object isEqual:other.object]);
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p range=%@ mode=%@>",
[self class], self, VT100GridWindowedRangeDescription(_range),
Loading
Loading
@@ -64,7 +76,7 @@
return @[ self ];
}
NSMutableArray *result = [NSMutableArray array];
[[self class] enumerateRangesInWindowedRange:self.range block:^(VT100GridCoordRange theRange) {
[[self class] enumerateRangesInWindowedRange:self.range block:^(VT100GridCoordRange theRange, BOOL *stop) {
iTermSubSelection *sub =
[iTermSubSelection subSelectionWithRange:VT100GridWindowedRangeMake(theRange, 0, 0)
mode:_selectionMode];
Loading
Loading
@@ -76,20 +88,25 @@
}
 
+ (void)enumerateRangesInWindowedRange:(VT100GridWindowedRange)windowedRange
block:(void (^)(VT100GridCoordRange))block {
block:(void (^)(VT100GridCoordRange, BOOL *))block {
if (windowedRange.columnWindow.length) {
BOOL stop = NO;
int right = windowedRange.columnWindow.location + windowedRange.columnWindow.length;
int startX = VT100GridWindowedRangeStart(windowedRange).x;
for (int y = windowedRange.coordRange.start.y; y < windowedRange.coordRange.end.y; y++) {
block(VT100GridCoordRangeMake(startX, y, right, y));
block(VT100GridCoordRangeMake(startX, y, right, y), &stop);
if (stop) {
return;
}
startX = windowedRange.columnWindow.location;
}
block(VT100GridCoordRangeMake(startX,
windowedRange.coordRange.end.y,
VT100GridWindowedRangeEnd(windowedRange).x,
windowedRange.coordRange.end.y));
windowedRange.coordRange.end.y),
&stop);
} else {
block(windowedRange.coordRange);
block(windowedRange.coordRange, &stop);
}
}
 
Loading
Loading
@@ -684,6 +701,15 @@
[_delegate selectionDidChange:self];
}
 
- (void)removeSubSelection:(iTermSubSelection *)sub {
[_subSelections removeObject:sub];
[_delegate selectionDidChange:self];
}
- (BOOL)containsSubSelection:(iTermSubSelection *)sub {
return [_subSelections containsObject:sub];
}
- (void)removeWindowsWithWidth:(int)width {
_initialRange = VT100GridWindowedRangeMake(VT100GridCoordRangeMake(0, 0, 0, 0), 0, 0);
if (_live) {
Loading
Loading
@@ -803,6 +829,40 @@
return indexes;
}
 
- (void)enumerateSubSelectionsInRange:(VT100GridRange)range
block:(void (^)(iTermSubSelection *, VT100GridCoordRange))block {
if (_live) {
// Live ranges can have box subs, which is just a pain to deal with, so make a copy,
// end live selection in the copy (which converts boxes to individual selections), and
// then try again on the copy.
iTermSelection *temp = [[self copy] autorelease];
[temp endLiveSelection];
[temp enumerateSubSelectionsInRange:range block:block];
return;
}
for (iTermSubSelection *sub in [self allSubSelections]) {
VT100GridCoordRange intersection = VT100GridCoordRangeIntersection(range, sub.range.coordRange);
if (intersection.start.x != -1) {
VT100GridWindowedRange windowedRange;
windowedRange.coordRange = intersection;
windowedRange.columnWindow = sub.range.columnWindow;
[iTermSubSelection enumerateRangesInWindowedRange:windowedRange
block:^(VT100GridCoordRange subRange, BOOL *stop) {
if (VT100GridCoordRangesIntersect(range, subRange)) {
block(sub, subRange);
} else if (VT100GridCoordOrder(range.end, subRange.start) == NSOrderedAscending) {
// We're called sequentially from top to bottom in the sub's range.
// Once we're called with a range that starts after the end of |range| we can
// stop enumerating because we'll never re-enter |range|.
*stop = YES;
}
}];
}
}
}
- (void)enumerateSelectedRanges:(void (^)(VT100GridWindowedRange, BOOL *, BOOL))block {
if (_live) {
// Live ranges can have box subs, which is just a pain to deal with, so make a copy,
Loading
Loading
@@ -840,7 +900,7 @@
}
__block NSRange theRange = NSMakeRange(0, 0);
[iTermSubSelection enumerateRangesInWindowedRange:outer.range
block:^(VT100GridCoordRange outerRange) {
block:^(VT100GridCoordRange outerRange, BOOL *outerStop) {
theRange = NSMakeRange(outerRange.start.x + outerRange.start.y * width,
VT100GridCoordRangeLength(outerRange, width));
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment