[74] | 1 | //
|
---|
| 2 | // ICeCoffEETerminal.m
|
---|
| 3 | // ICeCoffEE
|
---|
| 4 | //
|
---|
| 5 | // Created by Nicholas Riley on Mon Jan 28 2002.
|
---|
| 6 | // Copyright (c) 2002 Nicholas Riley. All rights reserved.
|
---|
| 7 | //
|
---|
| 8 |
|
---|
| 9 | #import "ICeCoffEETerminal.h"
|
---|
| 10 | #import "ICeCoffEE.h"
|
---|
[322] | 11 | #import "ICeCoffEEParser.h"
|
---|
[74] | 12 | #import <Carbon/Carbon.h>
|
---|
| 13 | #include <unistd.h>
|
---|
| 14 |
|
---|
| 15 | static NSRange ICCF_zeroRange = { NSNotFound, 5 };
|
---|
| 16 |
|
---|
| 17 | #define min(a,b) (a < b ? a : b)
|
---|
| 18 | #define max(a,b) (a > b ? a : b)
|
---|
| 19 |
|
---|
| 20 | @interface TermStorage:NSObject
|
---|
| 21 | - (struct _FSelPt)selPt0;
|
---|
| 22 | - (struct _FSelPt)selPt1;
|
---|
| 23 | - (BOOL)hasSelection;
|
---|
| 24 | - (void)startSelectionAtLine:(unsigned int)row column:(unsigned short)column;
|
---|
| 25 | - (void)startSelectionAtLine:(unsigned int)row offset:(unsigned short)offset;
|
---|
| 26 | - (void)endSelectionAtLine:(unsigned int)row offset:(unsigned short)offset;
|
---|
| 27 | - (void)selectWhitespaceDelimitedTextAtLine:(unsigned int)line offset:(unsigned short)offset;
|
---|
| 28 | - (void)selectWordAtLine:(unsigned int)line offset:(unsigned short)offset;
|
---|
| 29 | - (NSString *)selectedString;
|
---|
| 30 | - (unsigned int)numberOfLines;
|
---|
| 31 | - (unsigned int)effectiveColumnsForLine:(unsigned int)line;
|
---|
| 32 | - (void)clearSelection;
|
---|
| 33 | - (BOOL)isSelected:(unsigned int)line :(unsigned int)column;
|
---|
| 34 | @end
|
---|
| 35 |
|
---|
| 36 | @interface TermController:NSObject
|
---|
| 37 | - (TermStorage *)storage;
|
---|
| 38 | @end
|
---|
| 39 |
|
---|
[388] | 40 | @implementation ICeCoffEETermSubviewSuper
|
---|
[74] | 41 | // NSTextInput implementation
|
---|
| 42 | - (void)insertText:(id)aString {}
|
---|
| 43 | - (void)doCommandBySelector:(SEL)aSelector {}
|
---|
| 44 | - (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange {}
|
---|
| 45 | - (void)unmarkText {}
|
---|
| 46 | - (BOOL)hasMarkedText { return NO; }
|
---|
| 47 | - (long)conversationIdentifier { return 0; }
|
---|
| 48 | - (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange { return nil; }
|
---|
| 49 | - (NSRange)markedRange { return ICCF_zeroRange; }
|
---|
| 50 | - (NSRange)selectedRange { return ICCF_zeroRange; }
|
---|
| 51 | - (NSRect)firstRectForCharacterRange:(NSRange)theRange { return NSZeroRect; }
|
---|
| 52 | - (unsigned int)characterIndexForPoint:(NSPoint)thePoint { return 0; }
|
---|
| 53 | - (NSArray*)validAttributesForMarkedText { return nil; }
|
---|
[139] | 54 | // NSDraggingDestination
|
---|
| 55 | - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender { return NSDragOperationNone; }
|
---|
[142] | 56 | // misc. other stuff
|
---|
[74] | 57 | - (void)_optionClickEvent:(NSEvent *)event:(unsigned int)row:(unsigned short)column {}
|
---|
[216] | 58 | - (void)setNeedsDisplay {}
|
---|
| 59 | // NSView methods (needed to avoid crash in super invocation)
|
---|
| 60 | - (void)mouseUp:(NSEvent *)e { [super mouseUp: e]; }
|
---|
| 61 | - (void)mouseDown:(NSEvent *)e { [super mouseDown: e]; }
|
---|
[74] | 62 | @end
|
---|
| 63 |
|
---|
| 64 | @interface ICeCoffEETerminalRange : NSObject
|
---|
| 65 | {
|
---|
| 66 | TermStorage *storage;
|
---|
| 67 | struct _FSelPt pt0;
|
---|
| 68 | struct _FSelPt pt1;
|
---|
| 69 | unsigned int width;
|
---|
| 70 | unsigned int height;
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | + (ICeCoffEETerminalRange *)rangeWithTerminal:(ICeCoffEETerminal *)terminal;
|
---|
| 74 | + (ICeCoffEETerminalRange *)rangeWithTerminal:(ICeCoffEETerminal *)terminal pt0:(struct _FSelPt)selPt0 pt1:(struct _FSelPt)selPt1;
|
---|
| 75 |
|
---|
| 76 | - (id)initWithTerminal:(ICeCoffEETerminal *)terminal;
|
---|
| 77 | - (id)initWithTerminal:(ICeCoffEETerminal *)terminal pt0:(struct _FSelPt)selPt0 pt1:(struct _FSelPt)selPt1;
|
---|
| 78 |
|
---|
| 79 | - (struct _FSelPt)pt0;
|
---|
| 80 | - (struct _FSelPt)pt1;
|
---|
| 81 |
|
---|
| 82 | - (NSString *)stringFromRange;
|
---|
| 83 |
|
---|
| 84 | - (void)growBackwardByLength:(unsigned long)length;
|
---|
| 85 | - (void)growForwardByLength:(unsigned long)length;
|
---|
| 86 |
|
---|
| 87 | - (void)shrinkBackByLength:(unsigned long)length;
|
---|
| 88 | - (void)shrinkFrontByLength:(unsigned long)length;
|
---|
| 89 |
|
---|
| 90 | - (BOOL)rangeIsEmpty;
|
---|
| 91 |
|
---|
| 92 | - (void)select;
|
---|
| 93 |
|
---|
| 94 | @end
|
---|
| 95 |
|
---|
| 96 | @implementation ICeCoffEETerminalRange
|
---|
| 97 |
|
---|
| 98 | + (ICeCoffEETerminalRange *)rangeWithTerminal:(ICeCoffEETerminal *)terminal;
|
---|
| 99 | {
|
---|
| 100 | return [[[self alloc] initWithTerminal: terminal] autorelease];
|
---|
| 101 | }
|
---|
| 102 |
|
---|
| 103 | + (ICeCoffEETerminalRange *)rangeWithTerminal:(ICeCoffEETerminal *)terminal pt0:(struct _FSelPt)selPt0 pt1:(struct _FSelPt)selPt1;
|
---|
| 104 | {
|
---|
| 105 | return [[[self alloc] initWithTerminal: terminal pt0: selPt0 pt1: selPt1] autorelease];
|
---|
| 106 | }
|
---|
| 107 |
|
---|
| 108 | - (id)initWithTerminal:(ICeCoffEETerminal *)terminal;
|
---|
| 109 | {
|
---|
| 110 | if ( (self = [self init]) != nil) {
|
---|
| 111 | storage = [(TermController *)[terminal valueForKey: @"controller"] storage];
|
---|
| 112 | pt0 = [storage selPt0];
|
---|
| 113 | pt1 = [storage selPt1];
|
---|
| 114 | width = [storage effectiveColumnsForLine: pt0.line];
|
---|
| 115 | height = [storage numberOfLines];
|
---|
| 116 | }
|
---|
| 117 | return self;
|
---|
| 118 | }
|
---|
| 119 |
|
---|
| 120 | - (id)initWithTerminal:(ICeCoffEETerminal *)terminal pt0:(struct _FSelPt)selPt0 pt1:(struct _FSelPt)selPt1;
|
---|
| 121 | {
|
---|
| 122 | if ( (self = [self initWithTerminal: terminal]) != nil) {
|
---|
| 123 | pt0 = selPt0;
|
---|
| 124 | pt1 = selPt1;
|
---|
| 125 | }
|
---|
| 126 | return self;
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | - (struct _FSelPt)pt0;
|
---|
| 130 | {
|
---|
| 131 | return pt0;
|
---|
| 132 | }
|
---|
| 133 |
|
---|
| 134 | - (struct _FSelPt)pt1;
|
---|
| 135 | {
|
---|
| 136 | return pt1;
|
---|
| 137 | }
|
---|
| 138 |
|
---|
| 139 | - (NSString *)stringFromRange;
|
---|
| 140 | {
|
---|
| 141 | struct _FSelPt oldPt0 = [storage selPt0], oldPt1 = [storage selPt1];
|
---|
| 142 | NSString *str;
|
---|
| 143 |
|
---|
| 144 | [storage startSelectionAtLine: pt0.line offset: pt0.col];
|
---|
| 145 | [storage endSelectionAtLine: pt1.line offset: pt1.col];
|
---|
| 146 |
|
---|
| 147 | str = [storage selectedString];
|
---|
| 148 |
|
---|
| 149 | [storage startSelectionAtLine: oldPt0.line offset: oldPt0.col];
|
---|
| 150 | [storage endSelectionAtLine: oldPt1.line offset: oldPt1.col];
|
---|
| 151 |
|
---|
| 152 | return str;
|
---|
| 153 | }
|
---|
| 154 |
|
---|
| 155 | - (struct _FSelPt)_moveBack:(struct _FSelPt)pt byLength:(unsigned long)length;
|
---|
| 156 | {
|
---|
| 157 | unsigned int extraLines = length / width;
|
---|
| 158 | if ((long)(pt.line - extraLines) < 0) {
|
---|
| 159 | pt.line = 0;
|
---|
| 160 | pt.col = 0;
|
---|
| 161 | } else if (pt.line - extraLines == 0) {
|
---|
| 162 | pt.line = 0;
|
---|
| 163 | pt.col += width - (length % height);
|
---|
| 164 | if (pt.col < width) pt.col = 0;
|
---|
| 165 | else pt.col -= width;
|
---|
| 166 | } else {
|
---|
| 167 | pt.line -= extraLines;
|
---|
| 168 | pt.col += width - (length % height);
|
---|
| 169 | if (pt.col < width) pt.line--;
|
---|
| 170 | else pt.col -= width;
|
---|
| 171 | }
|
---|
| 172 | return pt;
|
---|
| 173 | }
|
---|
| 174 |
|
---|
| 175 | - (struct _FSelPt)_moveForward:(struct _FSelPt)pt byLength:(unsigned long)length;
|
---|
| 176 | {
|
---|
| 177 | unsigned int extraLines = length / width;
|
---|
| 178 | if (pt.line + extraLines >= height) {
|
---|
| 179 | pt.line = height - 1;
|
---|
| 180 | pt.col = width - 1;
|
---|
| 181 | } else if (pt.line + extraLines == height - 1) {
|
---|
| 182 | pt.line = height - 1;
|
---|
| 183 | pt.col += length % height;
|
---|
| 184 | if (pt.col >= width) pt.col = width - 1;
|
---|
| 185 | } else {
|
---|
| 186 | pt.line += extraLines;
|
---|
| 187 | pt.col = pt.col + (length % height);
|
---|
| 188 | if (pt.col > width) {
|
---|
| 189 | pt.line++;
|
---|
| 190 | pt.col -= width;
|
---|
| 191 | }
|
---|
| 192 | }
|
---|
| 193 | return pt;
|
---|
| 194 | }
|
---|
| 195 |
|
---|
| 196 | - (BOOL)rangeIsEmpty;
|
---|
| 197 | {
|
---|
| 198 | return (pt1.line < pt0.line) || (pt1.line == pt0.line && pt1.col <= pt0.col);
|
---|
| 199 | }
|
---|
| 200 |
|
---|
| 201 | - (void)growBackwardByLength:(unsigned long)length;
|
---|
| 202 | {
|
---|
| 203 | pt0 = [self _moveBack: pt0 byLength: length];
|
---|
| 204 | }
|
---|
| 205 |
|
---|
| 206 | - (void)growForwardByLength:(unsigned long)length;
|
---|
| 207 | {
|
---|
| 208 | pt1 = [self _moveForward: pt1 byLength: length];
|
---|
| 209 | }
|
---|
| 210 |
|
---|
| 211 | - (void)shrinkFrontByLength:(unsigned long)length;
|
---|
| 212 | {
|
---|
| 213 | pt0 = [self _moveForward: pt0 byLength: length];
|
---|
| 214 | }
|
---|
| 215 |
|
---|
| 216 | - (void)shrinkBackByLength:(unsigned long)length;
|
---|
| 217 | {
|
---|
| 218 | pt1 = [self _moveBack: pt1 byLength: length];
|
---|
| 219 | }
|
---|
| 220 |
|
---|
| 221 | - (void)select;
|
---|
| 222 | {
|
---|
| 223 | [storage startSelectionAtLine: pt0.line offset: pt0.col];
|
---|
| 224 | [storage endSelectionAtLine: pt1.line offset: pt1.col];
|
---|
| 225 | }
|
---|
| 226 |
|
---|
| 227 | - (NSString *)description;
|
---|
| 228 | {
|
---|
| 229 | return [NSString stringWithFormat: @"L%uC%hu - L%uC%hu: |%@|", pt0.line, pt0.col, pt1.line, pt1.col, [[self stringFromRange] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]];
|
---|
| 230 | }
|
---|
| 231 |
|
---|
| 232 | @end
|
---|
| 233 |
|
---|
| 234 | // TermSubview's NSTextInput implementation is essentially a noop: make one that does something
|
---|
| 235 | @implementation ICeCoffEETerminal // XXX normally in category (NSTextInput), but APE doesnÕt work if I do that
|
---|
| 236 |
|
---|
| 237 | - (NSRange)selectedRange {
|
---|
| 238 | TermStorage *storage = [(TermController *)[self valueForKey: @"controller"] storage];
|
---|
| 239 | struct _FSelPt selPt0 = [storage selPt0], selPt1 = [storage selPt1];
|
---|
| 240 | unsigned width = [storage effectiveColumnsForLine: selPt0.line];
|
---|
| 241 | unsigned pt0 = selPt0.line * width + selPt0.col;
|
---|
| 242 | ICLog(@"selPt0 %d x %d selPt1 %d x %d", selPt0.line, selPt0.col, selPt1.line, selPt1.col);
|
---|
| 243 | return NSMakeRange(pt0, selPt1.line * width + selPt1.col - pt0);
|
---|
| 244 | }
|
---|
| 245 |
|
---|
| 246 | // XXX string isn't padded: what we need is something like the ICeCoffEE 1.2.x method, adapted to Terminal 1.2, but we've chosen another way
|
---|
| 247 | - (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange;
|
---|
| 248 | {
|
---|
| 249 | TermStorage *storage = [(TermController *)[self valueForKey: @"controller"] storage];
|
---|
| 250 | struct _FSelPt oldPt0 = [storage selPt0], oldPt1 = [storage selPt1], realPt0, realPt1;
|
---|
| 251 | unsigned pt1 = theRange.location + theRange.length;
|
---|
| 252 | unsigned width = [storage effectiveColumnsForLine: 0];
|
---|
| 253 | NSAttributedString *str;
|
---|
| 254 |
|
---|
| 255 | realPt0.line = theRange.location / width;
|
---|
| 256 | realPt0.col = theRange.location % width;
|
---|
| 257 | realPt1.line = pt1 / width;
|
---|
| 258 | realPt1.col = pt1 % width;
|
---|
| 259 |
|
---|
| 260 | [storage startSelectionAtLine: realPt0.line offset: realPt0.col];
|
---|
| 261 | [storage endSelectionAtLine: realPt1.line offset: realPt1.col];
|
---|
| 262 |
|
---|
| 263 | str = [[NSAttributedString alloc] initWithString: [storage selectedString]];
|
---|
| 264 |
|
---|
| 265 | NSAssert2([str length] == theRange.length, @"Substring has length %lu when we expected %lu", [str length], theRange.length);
|
---|
| 266 |
|
---|
| 267 | [storage startSelectionAtLine: oldPt0.line offset: oldPt0.col];
|
---|
| 268 | [storage endSelectionAtLine: oldPt1.line offset: oldPt1.col];
|
---|
| 269 |
|
---|
| 270 | return [str autorelease];
|
---|
| 271 | }
|
---|
| 272 |
|
---|
| 273 | static NSEvent *ICCF_downEvent;
|
---|
| 274 | static unsigned int ICCF_line;
|
---|
| 275 | static unsigned short ICCF_col;
|
---|
| 276 | static BOOL ICCF_optionClickRegistered;
|
---|
| 277 |
|
---|
| 278 | - (void)setSelectedRange:(NSRange)charRange affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag;
|
---|
| 279 | {
|
---|
| 280 | TermStorage *storage = [(TermController *)[self valueForKey: @"controller"] storage];
|
---|
| 281 | unsigned width = [storage effectiveColumnsForLine: 0];
|
---|
| 282 | unsigned pt1 = charRange.location + charRange.length;
|
---|
| 283 | [storage startSelectionAtLine: charRange.location / width offset: charRange.location % width];
|
---|
| 284 | [storage endSelectionAtLine: pt1 / width offset: pt1 % width];
|
---|
| 285 | // [self refresh];
|
---|
| 286 | }
|
---|
| 287 |
|
---|
| 288 | void ICCF_LaunchURLFromTerminal(ICeCoffEETerminal *self) {
|
---|
| 289 | NSCharacterSet *urlLeftDelimiters = nil, *urlRightDelimiters = nil;
|
---|
| 290 |
|
---|
| 291 | ICeCoffEETerminalRange *termRange = nil, *selRange = nil;
|
---|
| 292 | NSString *s;
|
---|
| 293 | NSRange range, delimiterRange;
|
---|
| 294 |
|
---|
| 295 | NS_DURING
|
---|
| 296 |
|
---|
| 297 | TermStorage *storage = [(TermController *)[self valueForKey: @"controller"] storage];
|
---|
| 298 |
|
---|
| 299 | if ([storage hasSelection] && [storage isSelected: ICCF_line : ICCF_col]) {
|
---|
| 300 | selRange = [ICeCoffEETerminalRange rangeWithTerminal: self];
|
---|
| 301 | } else { // select something
|
---|
[319] | 302 | // XXX test this next line, it may be what's causing a Terminal bug to exhibit itself
|
---|
[74] | 303 | [storage selectWordAtLine: ICCF_line offset: ICCF_col];
|
---|
| 304 | selRange = [ICeCoffEETerminalRange rangeWithTerminal: self];
|
---|
| 305 | NSCAssert(![selRange rangeIsEmpty], ICCF_LocalizedString(@"Sorry, ICeCoffEE was unable to find anything to select"));
|
---|
| 306 | }
|
---|
| 307 |
|
---|
| 308 | // However, word selection does not capture even the approximate boundaries of a URL
|
---|
| 309 | // (text to a space/line ending character); it'll stop at a period in the middle of a hostname.
|
---|
| 310 | // So, we expand it as follows:
|
---|
| 311 |
|
---|
| 312 | ICCF_Delimiters(&urlLeftDelimiters, &urlRightDelimiters);
|
---|
| 313 |
|
---|
| 314 | termRange = [ICeCoffEETerminalRange rangeWithTerminal: self pt0: [selRange pt0] pt1: [selRange pt0]];
|
---|
| 315 | [termRange growBackwardByLength: ICCF_MAX_URL_LEN]; // potentially too big
|
---|
[319] | 316 |
|
---|
| 317 | expandFront:
|
---|
| 318 | s = [termRange stringFromRange];
|
---|
[74] | 319 | ICLog(@"front %@", termRange);
|
---|
| 320 | delimiterRange = [s rangeOfCharacterFromSet: urlLeftDelimiters
|
---|
| 321 | options: NSLiteralSearch | NSBackwardsSearch];
|
---|
| 322 | if (delimiterRange.location == NSNotFound) {
|
---|
| 323 | // extend to beginning of string (as much as possible)
|
---|
[319] | 324 | [selRange growBackwardByLength: [s length]];
|
---|
[74] | 325 | } else {
|
---|
| 326 | NSCAssert(delimiterRange.length == 1, @"Internal error: delimiter matched range is not of length 1");
|
---|
[319] | 327 | [selRange growBackwardByLength: [s length] - delimiterRange.location - 1];
|
---|
[320] | 328 |
|
---|
| 329 | // in url/(parens)stuff, handle clicking inside or after (parens).
|
---|
[319] | 330 | if ([s characterAtIndex: delimiterRange.location] == '(') {
|
---|
| 331 | s = [selRange stringFromRange];
|
---|
| 332 | if ([s rangeOfString: @")"].location != NSNotFound ||
|
---|
| 333 | [s rangeOfCharacterFromSet: [NSCharacterSet characterSetWithCharactersInString: @"/."]].location == NSNotFound) {
|
---|
| 334 | [selRange growBackwardByLength: 1];
|
---|
[320] | 335 | ICLog(@"expanding past (, now |%@|", selRange);
|
---|
[319] | 336 | [termRange shrinkBackByLength: [[termRange stringFromRange] length] - delimiterRange.location];
|
---|
| 337 | goto expandFront;
|
---|
| 338 | }
|
---|
| 339 | }
|
---|
[74] | 340 | }
|
---|
| 341 |
|
---|
| 342 | ICLog(@"parsed front %@", selRange);
|
---|
| 343 |
|
---|
| 344 | termRange = [ICeCoffEETerminalRange rangeWithTerminal: self pt0: [selRange pt1] pt1: [selRange pt1]];
|
---|
| 345 | [termRange growForwardByLength: ICCF_MAX_URL_LEN]; // potentially too big
|
---|
[319] | 346 |
|
---|
| 347 | expandBack:
|
---|
| 348 | s = [termRange stringFromRange];
|
---|
[74] | 349 | ICLog(@"back %@", termRange);
|
---|
| 350 | delimiterRange = [s rangeOfCharacterFromSet: urlRightDelimiters
|
---|
| 351 | options: NSLiteralSearch];
|
---|
| 352 | if (delimiterRange.location == NSNotFound) {
|
---|
| 353 | // extend to end of string
|
---|
[319] | 354 | [selRange growForwardByLength: [s length]];
|
---|
[74] | 355 | } else {
|
---|
| 356 | NSCAssert(delimiterRange.length == 1, @"Internal error: delimiter matched range is not of length 1");
|
---|
[319] | 357 | [selRange growForwardByLength: delimiterRange.location];
|
---|
[320] | 358 |
|
---|
| 359 | // grow URL past closing paren if we've seen an open paren
|
---|
[319] | 360 | if ([s characterAtIndex: delimiterRange.location] == ')' &&
|
---|
| 361 | [[selRange stringFromRange] rangeOfString: @"("].location != NSNotFound) {
|
---|
| 362 | [selRange growForwardByLength: 1];
|
---|
[320] | 363 | ICLog(@"expanding past ), now |%@|", selRange);
|
---|
[319] | 364 | [termRange shrinkFrontByLength: delimiterRange.location + 1];
|
---|
| 365 | goto expandBack;
|
---|
| 366 | }
|
---|
[74] | 367 | }
|
---|
| 368 |
|
---|
| 369 | ICCF_StartIC();
|
---|
| 370 |
|
---|
| 371 | s = [selRange stringFromRange];
|
---|
| 372 |
|
---|
| 373 | range = NSMakeRange(0, [s length]);
|
---|
| 374 |
|
---|
| 375 | ICCF_CheckRange(range);
|
---|
| 376 |
|
---|
| 377 | ICLog(@"parsed back %@", selRange);
|
---|
[139] | 378 | ICLog(@"range of string %@", NSStringFromRange(range));
|
---|
[74] | 379 | ICCF_ParseURL(s, &range);
|
---|
| 380 | ICLog(@"parsed range %@ |%@|", NSStringFromRange(range), [s substringWithRange: range]);
|
---|
| 381 |
|
---|
| 382 | [selRange shrinkFrontByLength: range.location];
|
---|
| 383 | [selRange shrinkBackByLength: [s length] - range.length - range.location];
|
---|
| 384 |
|
---|
| 385 | s = [selRange stringFromRange];
|
---|
| 386 | ICLog(@"reconstituted URL %@", selRange);
|
---|
| 387 |
|
---|
| 388 | [selRange select];
|
---|
| 389 | [self setNeedsDisplay];
|
---|
| 390 | [[self superview] display];
|
---|
| 391 |
|
---|
[183] | 392 | if (ICCF_LaunchURL(s, ICCF_KeyboardAction([NSApp currentEvent])) && ICCF_prefs.textBlinkEnabled) {
|
---|
[74] | 393 | int i;
|
---|
| 394 | // Terminal flashes the selection one more time, so blink one fewer
|
---|
| 395 | for (i = 1 ; i < ICCF_prefs.textBlinkCount ; i++) {
|
---|
| 396 | [storage clearSelection];
|
---|
| 397 | [self setNeedsDisplay];
|
---|
| 398 | [[self superview] display];
|
---|
| 399 | usleep(kICBlinkDelayUsecs);
|
---|
| 400 | [selRange select];
|
---|
| 401 | [self setNeedsDisplay];
|
---|
| 402 | [[self superview] display];
|
---|
| 403 | usleep(kICBlinkDelayUsecs);
|
---|
| 404 | }
|
---|
| 405 | }
|
---|
| 406 | NS_HANDLER
|
---|
[388] | 407 | ICCF_HandleException(localException, ICCF_downEvent);
|
---|
[74] | 408 | NS_ENDHANDLER
|
---|
| 409 |
|
---|
| 410 | ICCF_StopIC();
|
---|
| 411 | }
|
---|
| 412 |
|
---|
| 413 | - (void)_optionClickEvent:(NSEvent *)event:(unsigned int)row:(unsigned short)column;
|
---|
| 414 | {
|
---|
| 415 | if (ICCF_downEvent != nil) {
|
---|
| 416 | ICCF_line = row; // XXX are these lines or rows? check with wrapping
|
---|
| 417 | ICCF_col = column;
|
---|
| 418 | ICCF_optionClickRegistered = YES;
|
---|
| 419 | } else {
|
---|
| 420 | [super _optionClickEvent: event :row :column];
|
---|
| 421 | }
|
---|
| 422 | }
|
---|
| 423 |
|
---|
| 424 | - (void)mouseUp:(NSEvent *)upEvent;
|
---|
| 425 | {
|
---|
| 426 | ICLog(@"ICeCoffEE Terminal up: %@", upEvent);
|
---|
| 427 | [super mouseUp: upEvent];
|
---|
| 428 | if (ICCF_downEvent != nil) {
|
---|
| 429 | NSPoint downPt = [ICCF_downEvent locationInWindow];
|
---|
| 430 | NSPoint upPt = [upEvent locationInWindow];
|
---|
| 431 | if (abs(downPt.x - upPt.x) <= kICHysteresisPixels && abs(downPt.y - upPt.y) <= kICHysteresisPixels) {
|
---|
| 432 | if (ICCF_optionClickRegistered) {
|
---|
| 433 | ICCF_optionClickRegistered = NO;
|
---|
| 434 | ICLog(@"========= launching... %d x %d", ICCF_line, ICCF_col);
|
---|
| 435 | ICCF_LaunchURLFromTerminal(self);
|
---|
| 436 | }
|
---|
| 437 | }
|
---|
[388] | 438 | [ICCF_downEvent release];
|
---|
| 439 | ICCF_downEvent = nil;
|
---|
[74] | 440 | }
|
---|
| 441 | }
|
---|
| 442 |
|
---|
| 443 | - (void)mouseDown:(NSEvent *)downEvent;
|
---|
| 444 | {
|
---|
| 445 | if (ICCF_enabled && ICCF_prefs.commandClickEnabled && ICCF_EventIsCommandMouseDown(downEvent)) {
|
---|
| 446 | NSEvent *optionClickEvent = [NSEvent mouseEventWithType: NSLeftMouseDown location: [downEvent locationInWindow] modifierFlags: NSAlternateKeyMask timestamp: [downEvent timestamp] windowNumber: [downEvent windowNumber] context: [downEvent context] eventNumber: [downEvent eventNumber] clickCount: 1 pressure: 0];
|
---|
| 447 | [ICCF_downEvent release];
|
---|
| 448 | ICCF_downEvent = [downEvent retain];
|
---|
| 449 | ICLog(@"ICeCoffEE Terminal constructed: %@", optionClickEvent);
|
---|
| 450 | ICCF_optionClickRegistered = NO;
|
---|
| 451 | [super mouseDown: optionClickEvent];
|
---|
| 452 | } else {
|
---|
| 453 | [super mouseDown: downEvent];
|
---|
| 454 | }
|
---|
| 455 | }
|
---|
| 456 |
|
---|
[139] | 457 | // NSDraggingDestination
|
---|
| 458 | // -[TermSubview draggingUpdated:] just invokes draggingEntered...
|
---|
| 459 | // XXX Crashing on repeated drag snap-back can happen even without ICeCoffEE installed; don't bother to try to fix
|
---|
| 460 | - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
|
---|
| 461 | {
|
---|
[142] | 462 | if (!ICCF_prefs.terminalRequireOptionForSelfDrag || [sender draggingSource] != self || ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)) {
|
---|
[139] | 463 | [super draggingEntered: sender];
|
---|
| 464 | // When doing non-self drags, this works around one bug in Terminal wherein the option key acts as a toggle, and it shouldn't (see Aqua HIG). Unfortunately, this messes up drag feedback for self drags, but I don't know of any way to fix it. Not that most Cocoa apps get it remotely right, anyway.
|
---|
| 465 | return NSDragOperationCopy;
|
---|
| 466 | }
|
---|
| 467 | return NSDragOperationNone;
|
---|
| 468 | }
|
---|
| 469 |
|
---|
[74] | 470 | @end
|
---|