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" |
---|
11 | #import "ICeCoffEEParser.h" |
---|
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 | |
---|
40 | @implementation ICeCoffEETermSubviewSuper |
---|
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; } |
---|
54 | // NSDraggingDestination |
---|
55 | - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender { return NSDragOperationNone; } |
---|
56 | // misc. other stuff |
---|
57 | - (void)_optionClickEvent:(NSEvent *)event:(unsigned int)row:(unsigned short)column {} |
---|
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]; } |
---|
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 | @try { |
---|
296 | TermStorage *storage = [(TermController *)[self valueForKey: @"controller"] storage]; |
---|
297 | |
---|
298 | if ([storage hasSelection] && [storage isSelected: ICCF_line : ICCF_col]) { |
---|
299 | selRange = [ICeCoffEETerminalRange rangeWithTerminal: self]; |
---|
300 | } else { // select something |
---|
301 | // XXX test this next line, it may be what's causing a Terminal bug to exhibit itself |
---|
302 | [storage selectWordAtLine: ICCF_line offset: ICCF_col]; |
---|
303 | selRange = [ICeCoffEETerminalRange rangeWithTerminal: self]; |
---|
304 | NSCAssert(![selRange rangeIsEmpty], ICCF_LocalizedString(@"Sorry, ICeCoffEE was unable to find anything to select")); |
---|
305 | } |
---|
306 | |
---|
307 | // However, word selection does not capture even the approximate boundaries of a URL |
---|
308 | // (text to a space/line ending character); it'll stop at a period in the middle of a hostname. |
---|
309 | // So, we expand it as follows: |
---|
310 | |
---|
311 | ICCF_Delimiters(&urlLeftDelimiters, &urlRightDelimiters); |
---|
312 | |
---|
313 | termRange = [ICeCoffEETerminalRange rangeWithTerminal: self pt0: [selRange pt0] pt1: [selRange pt0]]; |
---|
314 | [termRange growBackwardByLength: ICCF_MAX_URL_LEN]; // potentially too big |
---|
315 | |
---|
316 | expandFront: |
---|
317 | s = [termRange stringFromRange]; |
---|
318 | ICLog(@"front %@", termRange); |
---|
319 | delimiterRange = [s rangeOfCharacterFromSet: urlLeftDelimiters |
---|
320 | options: NSLiteralSearch | NSBackwardsSearch]; |
---|
321 | if (delimiterRange.location == NSNotFound) { |
---|
322 | // extend to beginning of string (as much as possible) |
---|
323 | [selRange growBackwardByLength: [s length]]; |
---|
324 | } else { |
---|
325 | NSCAssert(delimiterRange.length == 1, @"Internal error: delimiter matched range is not of length 1"); |
---|
326 | [selRange growBackwardByLength: [s length] - delimiterRange.location - 1]; |
---|
327 | |
---|
328 | // in url/(parens)stuff, handle clicking inside or after (parens). |
---|
329 | if ([s characterAtIndex: delimiterRange.location] == '(') { |
---|
330 | s = [selRange stringFromRange]; |
---|
331 | if ([s rangeOfString: @")"].location != NSNotFound || |
---|
332 | [s rangeOfCharacterFromSet: [NSCharacterSet characterSetWithCharactersInString: @"/."]].location == NSNotFound) { |
---|
333 | [selRange growBackwardByLength: 1]; |
---|
334 | ICLog(@"expanding past (, now |%@|", selRange); |
---|
335 | [termRange shrinkBackByLength: [[termRange stringFromRange] length] - delimiterRange.location]; |
---|
336 | goto expandFront; |
---|
337 | } |
---|
338 | } |
---|
339 | } |
---|
340 | |
---|
341 | ICLog(@"parsed front %@", selRange); |
---|
342 | |
---|
343 | termRange = [ICeCoffEETerminalRange rangeWithTerminal: self pt0: [selRange pt1] pt1: [selRange pt1]]; |
---|
344 | [termRange growForwardByLength: ICCF_MAX_URL_LEN]; // potentially too big |
---|
345 | |
---|
346 | expandBack: |
---|
347 | s = [termRange stringFromRange]; |
---|
348 | ICLog(@"back %@", termRange); |
---|
349 | delimiterRange = [s rangeOfCharacterFromSet: urlRightDelimiters |
---|
350 | options: NSLiteralSearch]; |
---|
351 | if (delimiterRange.location == NSNotFound) { |
---|
352 | // extend to end of string |
---|
353 | [selRange growForwardByLength: [s length]]; |
---|
354 | } else { |
---|
355 | NSCAssert(delimiterRange.length == 1, @"Internal error: delimiter matched range is not of length 1"); |
---|
356 | [selRange growForwardByLength: delimiterRange.location]; |
---|
357 | |
---|
358 | // grow URL past closing paren if we've seen an open paren |
---|
359 | if ([s characterAtIndex: delimiterRange.location] == ')' && |
---|
360 | [[selRange stringFromRange] rangeOfString: @"("].location != NSNotFound) { |
---|
361 | [selRange growForwardByLength: 1]; |
---|
362 | ICLog(@"expanding past ), now |%@|", selRange); |
---|
363 | [termRange shrinkFrontByLength: delimiterRange.location + 1]; |
---|
364 | goto expandBack; |
---|
365 | } |
---|
366 | } |
---|
367 | |
---|
368 | ICCF_StartIC(); |
---|
369 | |
---|
370 | s = [selRange stringFromRange]; |
---|
371 | |
---|
372 | range = NSMakeRange(0, [s length]); |
---|
373 | |
---|
374 | ICCF_CheckRange(range); |
---|
375 | |
---|
376 | ICLog(@"parsed back %@", selRange); |
---|
377 | ICLog(@"range of string %@", NSStringFromRange(range)); |
---|
378 | ICCF_ParseURL(s, &range); |
---|
379 | ICLog(@"parsed range %@ |%@|", NSStringFromRange(range), [s substringWithRange: range]); |
---|
380 | |
---|
381 | [selRange shrinkFrontByLength: range.location]; |
---|
382 | [selRange shrinkBackByLength: [s length] - range.length - range.location]; |
---|
383 | |
---|
384 | s = [selRange stringFromRange]; |
---|
385 | ICLog(@"reconstituted URL %@", selRange); |
---|
386 | |
---|
387 | [selRange select]; |
---|
388 | [self setNeedsDisplay]; |
---|
389 | [[self superview] display]; |
---|
390 | |
---|
391 | if (ICCF_LaunchURL(s, ICCF_KeyboardAction([NSApp currentEvent])) && ICCF_prefs.textBlinkEnabled) { |
---|
392 | int i; |
---|
393 | // Terminal flashes the selection one more time, so blink one fewer |
---|
394 | for (i = 1 ; i < ICCF_prefs.textBlinkCount ; i++) { |
---|
395 | [storage clearSelection]; |
---|
396 | [self setNeedsDisplay]; |
---|
397 | [[self superview] display]; |
---|
398 | usleep(kICBlinkDelayUsecs); |
---|
399 | [selRange select]; |
---|
400 | [self setNeedsDisplay]; |
---|
401 | [[self superview] display]; |
---|
402 | usleep(kICBlinkDelayUsecs); |
---|
403 | } |
---|
404 | } |
---|
405 | } @catch (NSException *e) { |
---|
406 | ICCF_HandleException(e, ICCF_downEvent); |
---|
407 | } |
---|
408 | |
---|
409 | ICCF_StopIC(); |
---|
410 | } |
---|
411 | |
---|
412 | - (void)_optionClickEvent:(NSEvent *)event:(unsigned int)row:(unsigned short)column; |
---|
413 | { |
---|
414 | if (ICCF_downEvent != nil) { |
---|
415 | ICCF_line = row; // XXX are these lines or rows? check with wrapping |
---|
416 | ICCF_col = column; |
---|
417 | ICCF_optionClickRegistered = YES; |
---|
418 | } else { |
---|
419 | [super _optionClickEvent: event :row :column]; |
---|
420 | } |
---|
421 | } |
---|
422 | |
---|
423 | - (void)mouseUp:(NSEvent *)upEvent; |
---|
424 | { |
---|
425 | ICLog(@"ICeCoffEE Terminal up: %@", upEvent); |
---|
426 | [super mouseUp: upEvent]; |
---|
427 | if (ICCF_downEvent != nil) { |
---|
428 | NSPoint downPt = [ICCF_downEvent locationInWindow]; |
---|
429 | NSPoint upPt = [upEvent locationInWindow]; |
---|
430 | if (abs(downPt.x - upPt.x) <= kICHysteresisPixels && abs(downPt.y - upPt.y) <= kICHysteresisPixels) { |
---|
431 | if (ICCF_optionClickRegistered) { |
---|
432 | ICCF_optionClickRegistered = NO; |
---|
433 | ICLog(@"========= launching... %d x %d", ICCF_line, ICCF_col); |
---|
434 | ICCF_LaunchURLFromTerminal(self); |
---|
435 | } |
---|
436 | } |
---|
437 | [ICCF_downEvent release]; |
---|
438 | ICCF_downEvent = nil; |
---|
439 | } |
---|
440 | } |
---|
441 | |
---|
442 | - (void)mouseDown:(NSEvent *)downEvent; |
---|
443 | { |
---|
444 | if (ICCF_enabled && ICCF_prefs.commandClickEnabled && ICCF_EventIsCommandMouseDown(downEvent)) { |
---|
445 | 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]; |
---|
446 | [ICCF_downEvent release]; |
---|
447 | ICCF_downEvent = [downEvent retain]; |
---|
448 | ICLog(@"ICeCoffEE Terminal constructed: %@", optionClickEvent); |
---|
449 | ICCF_optionClickRegistered = NO; |
---|
450 | [super mouseDown: optionClickEvent]; |
---|
451 | } else { |
---|
452 | [super mouseDown: downEvent]; |
---|
453 | } |
---|
454 | } |
---|
455 | |
---|
456 | // NSDraggingDestination |
---|
457 | // -[TermSubview draggingUpdated:] just invokes draggingEntered... |
---|
458 | // XXX Crashing on repeated drag snap-back can happen even without ICeCoffEE installed; don't bother to try to fix |
---|
459 | - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; |
---|
460 | { |
---|
461 | if (!ICCF_prefs.terminalRequireOptionForSelfDrag || [sender draggingSource] != self || ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)) { |
---|
462 | [super draggingEntered: sender]; |
---|
463 | // 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. |
---|
464 | return NSDragOperationCopy; |
---|
465 | } |
---|
466 | return NSDragOperationNone; |
---|
467 | } |
---|
468 | |
---|
469 | @end |
---|