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 "ICeCoffEEScanner.h" |
---|
11 | #import "ICeCoffEE.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 | @end |
---|
60 | |
---|
61 | @interface ICeCoffEETerminalRange : NSObject |
---|
62 | { |
---|
63 | TermStorage *storage; |
---|
64 | struct _FSelPt pt0; |
---|
65 | struct _FSelPt pt1; |
---|
66 | unsigned int width; |
---|
67 | unsigned int height; |
---|
68 | } |
---|
69 | |
---|
70 | + (ICeCoffEETerminalRange *)rangeWithTerminal:(ICeCoffEETerminal *)terminal; |
---|
71 | + (ICeCoffEETerminalRange *)rangeWithTerminal:(ICeCoffEETerminal *)terminal pt0:(struct _FSelPt)selPt0 pt1:(struct _FSelPt)selPt1; |
---|
72 | |
---|
73 | - (id)initWithTerminal:(ICeCoffEETerminal *)terminal; |
---|
74 | - (id)initWithTerminal:(ICeCoffEETerminal *)terminal pt0:(struct _FSelPt)selPt0 pt1:(struct _FSelPt)selPt1; |
---|
75 | |
---|
76 | - (struct _FSelPt)pt0; |
---|
77 | - (struct _FSelPt)pt1; |
---|
78 | |
---|
79 | - (NSString *)stringFromRange; |
---|
80 | |
---|
81 | - (void)growBackwardByLength:(unsigned long)length; |
---|
82 | - (void)growForwardByLength:(unsigned long)length; |
---|
83 | |
---|
84 | - (void)shrinkBackByLength:(unsigned long)length; |
---|
85 | - (void)shrinkFrontByLength:(unsigned long)length; |
---|
86 | |
---|
87 | - (BOOL)rangeIsEmpty; |
---|
88 | |
---|
89 | - (void)select; |
---|
90 | |
---|
91 | @end |
---|
92 | |
---|
93 | @implementation ICeCoffEETerminalRange |
---|
94 | |
---|
95 | + (ICeCoffEETerminalRange *)rangeWithTerminal:(ICeCoffEETerminal *)terminal; |
---|
96 | { |
---|
97 | return [[[self alloc] initWithTerminal: terminal] autorelease]; |
---|
98 | } |
---|
99 | |
---|
100 | + (ICeCoffEETerminalRange *)rangeWithTerminal:(ICeCoffEETerminal *)terminal pt0:(struct _FSelPt)selPt0 pt1:(struct _FSelPt)selPt1; |
---|
101 | { |
---|
102 | return [[[self alloc] initWithTerminal: terminal pt0: selPt0 pt1: selPt1] autorelease]; |
---|
103 | } |
---|
104 | |
---|
105 | - (id)initWithTerminal:(ICeCoffEETerminal *)terminal; |
---|
106 | { |
---|
107 | if ( (self = [self init]) != nil) { |
---|
108 | storage = [(TermController *)[terminal valueForKey: @"controller"] storage]; |
---|
109 | pt0 = [storage selPt0]; |
---|
110 | pt1 = [storage selPt1]; |
---|
111 | width = [storage effectiveColumnsForLine: pt0.line]; |
---|
112 | height = [storage numberOfLines]; |
---|
113 | } |
---|
114 | return self; |
---|
115 | } |
---|
116 | |
---|
117 | - (id)initWithTerminal:(ICeCoffEETerminal *)terminal pt0:(struct _FSelPt)selPt0 pt1:(struct _FSelPt)selPt1; |
---|
118 | { |
---|
119 | if ( (self = [self initWithTerminal: terminal]) != nil) { |
---|
120 | pt0 = selPt0; |
---|
121 | pt1 = selPt1; |
---|
122 | } |
---|
123 | return self; |
---|
124 | } |
---|
125 | |
---|
126 | - (struct _FSelPt)pt0; |
---|
127 | { |
---|
128 | return pt0; |
---|
129 | } |
---|
130 | |
---|
131 | - (struct _FSelPt)pt1; |
---|
132 | { |
---|
133 | return pt1; |
---|
134 | } |
---|
135 | |
---|
136 | - (NSString *)stringFromRange; |
---|
137 | { |
---|
138 | struct _FSelPt oldPt0 = [storage selPt0], oldPt1 = [storage selPt1]; |
---|
139 | NSString *str; |
---|
140 | |
---|
141 | [storage startSelectionAtLine: pt0.line offset: pt0.col]; |
---|
142 | [storage endSelectionAtLine: pt1.line offset: pt1.col]; |
---|
143 | |
---|
144 | str = [storage selectedString]; |
---|
145 | |
---|
146 | [storage startSelectionAtLine: oldPt0.line offset: oldPt0.col]; |
---|
147 | [storage endSelectionAtLine: oldPt1.line offset: oldPt1.col]; |
---|
148 | |
---|
149 | return str; |
---|
150 | } |
---|
151 | |
---|
152 | - (struct _FSelPt)_moveBack:(struct _FSelPt)pt byLength:(unsigned long)length; |
---|
153 | { |
---|
154 | unsigned int extraLines = length / width; |
---|
155 | if ((long)(pt.line - extraLines) < 0) { |
---|
156 | pt.line = 0; |
---|
157 | pt.col = 0; |
---|
158 | } else if (pt.line - extraLines == 0) { |
---|
159 | pt.line = 0; |
---|
160 | pt.col += width - (length % height); |
---|
161 | if (pt.col < width) pt.col = 0; |
---|
162 | else pt.col -= width; |
---|
163 | } else { |
---|
164 | pt.line -= extraLines; |
---|
165 | pt.col += width - (length % height); |
---|
166 | if (pt.col < width) pt.line--; |
---|
167 | else pt.col -= width; |
---|
168 | } |
---|
169 | return pt; |
---|
170 | } |
---|
171 | |
---|
172 | - (struct _FSelPt)_moveForward:(struct _FSelPt)pt byLength:(unsigned long)length; |
---|
173 | { |
---|
174 | unsigned int extraLines = length / width; |
---|
175 | if (pt.line + extraLines >= height) { |
---|
176 | pt.line = height - 1; |
---|
177 | pt.col = width - 1; |
---|
178 | } else if (pt.line + extraLines == height - 1) { |
---|
179 | pt.line = height - 1; |
---|
180 | pt.col += length % height; |
---|
181 | if (pt.col >= width) pt.col = width - 1; |
---|
182 | } else { |
---|
183 | pt.line += extraLines; |
---|
184 | pt.col = pt.col + (length % height); |
---|
185 | if (pt.col > width) { |
---|
186 | pt.line++; |
---|
187 | pt.col -= width; |
---|
188 | } |
---|
189 | } |
---|
190 | return pt; |
---|
191 | } |
---|
192 | |
---|
193 | - (BOOL)rangeIsEmpty; |
---|
194 | { |
---|
195 | return (pt1.line < pt0.line) || (pt1.line == pt0.line && pt1.col <= pt0.col); |
---|
196 | } |
---|
197 | |
---|
198 | - (void)growBackwardByLength:(unsigned long)length; |
---|
199 | { |
---|
200 | pt0 = [self _moveBack: pt0 byLength: length]; |
---|
201 | } |
---|
202 | |
---|
203 | - (void)growForwardByLength:(unsigned long)length; |
---|
204 | { |
---|
205 | pt1 = [self _moveForward: pt1 byLength: length]; |
---|
206 | } |
---|
207 | |
---|
208 | - (void)shrinkFrontByLength:(unsigned long)length; |
---|
209 | { |
---|
210 | pt0 = [self _moveForward: pt0 byLength: length]; |
---|
211 | } |
---|
212 | |
---|
213 | - (void)shrinkBackByLength:(unsigned long)length; |
---|
214 | { |
---|
215 | pt1 = [self _moveBack: pt1 byLength: length]; |
---|
216 | } |
---|
217 | |
---|
218 | - (void)select; |
---|
219 | { |
---|
220 | [storage startSelectionAtLine: pt0.line offset: pt0.col]; |
---|
221 | [storage endSelectionAtLine: pt1.line offset: pt1.col]; |
---|
222 | } |
---|
223 | |
---|
224 | - (NSString *)description; |
---|
225 | { |
---|
226 | return [NSString stringWithFormat: @"L%uC%hu - L%uC%hu: |%@|", pt0.line, pt0.col, pt1.line, pt1.col, [[self stringFromRange] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]]; |
---|
227 | } |
---|
228 | |
---|
229 | @end |
---|
230 | |
---|
231 | // TermSubview's NSTextInput implementation is essentially a noop: make one that does something |
---|
232 | @implementation ICeCoffEETerminal // XXX normally in category (NSTextInput), but APE doesnÕt work if I do that |
---|
233 | |
---|
234 | - (NSRange)selectedRange { |
---|
235 | TermStorage *storage = [(TermController *)[self valueForKey: @"controller"] storage]; |
---|
236 | struct _FSelPt selPt0 = [storage selPt0], selPt1 = [storage selPt1]; |
---|
237 | unsigned width = [storage effectiveColumnsForLine: selPt0.line]; |
---|
238 | unsigned pt0 = selPt0.line * width + selPt0.col; |
---|
239 | ICLog(@"selPt0 %d x %d selPt1 %d x %d", selPt0.line, selPt0.col, selPt1.line, selPt1.col); |
---|
240 | return NSMakeRange(pt0, selPt1.line * width + selPt1.col - pt0); |
---|
241 | } |
---|
242 | |
---|
243 | // 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 |
---|
244 | - (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange; |
---|
245 | { |
---|
246 | TermStorage *storage = [(TermController *)[self valueForKey: @"controller"] storage]; |
---|
247 | struct _FSelPt oldPt0 = [storage selPt0], oldPt1 = [storage selPt1], realPt0, realPt1; |
---|
248 | unsigned pt1 = theRange.location + theRange.length; |
---|
249 | unsigned width = [storage effectiveColumnsForLine: 0]; |
---|
250 | NSAttributedString *str; |
---|
251 | |
---|
252 | realPt0.line = theRange.location / width; |
---|
253 | realPt0.col = theRange.location % width; |
---|
254 | realPt1.line = pt1 / width; |
---|
255 | realPt1.col = pt1 % width; |
---|
256 | |
---|
257 | [storage startSelectionAtLine: realPt0.line offset: realPt0.col]; |
---|
258 | [storage endSelectionAtLine: realPt1.line offset: realPt1.col]; |
---|
259 | |
---|
260 | str = [[NSAttributedString alloc] initWithString: [storage selectedString]]; |
---|
261 | |
---|
262 | NSAssert2([str length] == theRange.length, @"Substring has length %lu when we expected %lu", [str length], theRange.length); |
---|
263 | |
---|
264 | [storage startSelectionAtLine: oldPt0.line offset: oldPt0.col]; |
---|
265 | [storage endSelectionAtLine: oldPt1.line offset: oldPt1.col]; |
---|
266 | |
---|
267 | return [str autorelease]; |
---|
268 | } |
---|
269 | |
---|
270 | static NSEvent *ICCF_downEvent; |
---|
271 | static unsigned int ICCF_line; |
---|
272 | static unsigned short ICCF_col; |
---|
273 | static BOOL ICCF_optionClickRegistered; |
---|
274 | |
---|
275 | - (void)setSelectedRange:(NSRange)charRange affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag; |
---|
276 | { |
---|
277 | TermStorage *storage = [(TermController *)[self valueForKey: @"controller"] storage]; |
---|
278 | unsigned width = [storage effectiveColumnsForLine: 0]; |
---|
279 | unsigned pt1 = charRange.location + charRange.length; |
---|
280 | [storage startSelectionAtLine: charRange.location / width offset: charRange.location % width]; |
---|
281 | [storage endSelectionAtLine: pt1 / width offset: pt1 % width]; |
---|
282 | // [self refresh]; |
---|
283 | } |
---|
284 | |
---|
285 | void ICCF_LaunchURLFromTerminal(ICeCoffEETerminal *self) { |
---|
286 | NSCharacterSet *urlLeftDelimiters = nil, *urlRightDelimiters = nil; |
---|
287 | |
---|
288 | ICeCoffEETerminalRange *termRange = nil, *selRange = nil; |
---|
289 | NSString *s; |
---|
290 | NSRange range, delimiterRange; |
---|
291 | |
---|
292 | NS_DURING |
---|
293 | |
---|
294 | TermStorage *storage = [(TermController *)[self valueForKey: @"controller"] storage]; |
---|
295 | |
---|
296 | if ([storage hasSelection] && [storage isSelected: ICCF_line : ICCF_col]) { |
---|
297 | selRange = [ICeCoffEETerminalRange rangeWithTerminal: self]; |
---|
298 | } else { // select something |
---|
299 | [storage selectWordAtLine: ICCF_line offset: ICCF_col]; |
---|
300 | selRange = [ICeCoffEETerminalRange rangeWithTerminal: self]; |
---|
301 | NSCAssert(![selRange rangeIsEmpty], ICCF_LocalizedString(@"Sorry, ICeCoffEE was unable to find anything to select")); |
---|
302 | } |
---|
303 | |
---|
304 | // However, word selection does not capture even the approximate boundaries of a URL |
---|
305 | // (text to a space/line ending character); it'll stop at a period in the middle of a hostname. |
---|
306 | // So, we expand it as follows: |
---|
307 | |
---|
308 | ICCF_Delimiters(&urlLeftDelimiters, &urlRightDelimiters); |
---|
309 | |
---|
310 | termRange = [ICeCoffEETerminalRange rangeWithTerminal: self pt0: [selRange pt0] pt1: [selRange pt0]]; |
---|
311 | // add 1 to range to trap delimiters that are on the edge of the selection (i.e., <...) |
---|
312 | [termRange growForwardByLength: 1]; |
---|
313 | [termRange growBackwardByLength: ICCF_MAX_URL_LEN]; // potentially too big |
---|
314 | s = [termRange stringFromRange]; |
---|
315 | ICLog(@"front %@", termRange); |
---|
316 | delimiterRange = [s rangeOfCharacterFromSet: urlLeftDelimiters |
---|
317 | options: NSLiteralSearch | NSBackwardsSearch]; |
---|
318 | if (delimiterRange.location == NSNotFound) { |
---|
319 | // extend to beginning of string (as much as possible) |
---|
320 | [selRange growBackwardByLength: [s length] - 1]; |
---|
321 | } else { |
---|
322 | NSCAssert(delimiterRange.length == 1, @"Internal error: delimiter matched range is not of length 1"); |
---|
323 | [selRange growBackwardByLength: [s length] - delimiterRange.location - 2]; |
---|
324 | } |
---|
325 | |
---|
326 | ICLog(@"parsed front %@", selRange); |
---|
327 | |
---|
328 | termRange = [ICeCoffEETerminalRange rangeWithTerminal: self pt0: [selRange pt1] pt1: [selRange pt1]]; |
---|
329 | // subtract 1 from range to trap delimiters that are on the edge of the selection (i.e., ...>) |
---|
330 | [termRange growBackwardByLength: 1]; |
---|
331 | [termRange growForwardByLength: ICCF_MAX_URL_LEN]; // potentially too big |
---|
332 | s = [termRange stringFromRange]; |
---|
333 | ICLog(@"back %@", termRange); |
---|
334 | delimiterRange = [s rangeOfCharacterFromSet: urlRightDelimiters |
---|
335 | options: NSLiteralSearch]; |
---|
336 | if (delimiterRange.location == NSNotFound) { |
---|
337 | // extend to end of string |
---|
338 | [selRange growForwardByLength: [s length] - 1]; |
---|
339 | } else { |
---|
340 | NSCAssert(delimiterRange.length == 1, @"Internal error: delimiter matched range is not of length 1"); |
---|
341 | [selRange growForwardByLength: delimiterRange.location - 1]; |
---|
342 | } |
---|
343 | |
---|
344 | ICCF_StartIC(); |
---|
345 | |
---|
346 | s = [selRange stringFromRange]; |
---|
347 | |
---|
348 | range = NSMakeRange(0, [s length]); |
---|
349 | |
---|
350 | ICCF_CheckRange(range); |
---|
351 | |
---|
352 | ICLog(@"parsed back %@", selRange); |
---|
353 | ICLog(@"range of string %@", NSStringFromRange(range)); |
---|
354 | ICCF_ParseURL(s, &range); |
---|
355 | ICLog(@"parsed range %@ |%@|", NSStringFromRange(range), [s substringWithRange: range]); |
---|
356 | |
---|
357 | [selRange shrinkFrontByLength: range.location]; |
---|
358 | [selRange shrinkBackByLength: [s length] - range.length - range.location]; |
---|
359 | |
---|
360 | s = [selRange stringFromRange]; |
---|
361 | ICLog(@"reconstituted URL %@", selRange); |
---|
362 | |
---|
363 | [selRange select]; |
---|
364 | [self setNeedsDisplay]; |
---|
365 | [[self superview] display]; |
---|
366 | |
---|
367 | ICCF_LaunchURL(s, ICCF_KeyboardAction()); |
---|
368 | |
---|
369 | if (ICCF_prefs.textBlinkEnabled) { |
---|
370 | int i; |
---|
371 | // Terminal flashes the selection one more time, so blink one fewer |
---|
372 | for (i = 1 ; i < ICCF_prefs.textBlinkCount ; i++) { |
---|
373 | [storage clearSelection]; |
---|
374 | [self setNeedsDisplay]; |
---|
375 | [[self superview] display]; |
---|
376 | usleep(kICBlinkDelayUsecs); |
---|
377 | [selRange select]; |
---|
378 | [self setNeedsDisplay]; |
---|
379 | [[self superview] display]; |
---|
380 | usleep(kICBlinkDelayUsecs); |
---|
381 | } |
---|
382 | } |
---|
383 | NS_HANDLER |
---|
384 | ICCF_HandleException(localException); |
---|
385 | NS_ENDHANDLER |
---|
386 | |
---|
387 | ICCF_StopIC(); |
---|
388 | } |
---|
389 | |
---|
390 | - (void)_optionClickEvent:(NSEvent *)event:(unsigned int)row:(unsigned short)column; |
---|
391 | { |
---|
392 | if (ICCF_downEvent != nil) { |
---|
393 | ICCF_line = row; // XXX are these lines or rows? check with wrapping |
---|
394 | ICCF_col = column; |
---|
395 | ICCF_optionClickRegistered = YES; |
---|
396 | } else { |
---|
397 | [super _optionClickEvent: event :row :column]; |
---|
398 | } |
---|
399 | } |
---|
400 | |
---|
401 | - (void)mouseUp:(NSEvent *)upEvent; |
---|
402 | { |
---|
403 | ICLog(@"ICeCoffEE Terminal up: %@", upEvent); |
---|
404 | [super mouseUp: upEvent]; |
---|
405 | if (ICCF_downEvent != nil) { |
---|
406 | NSPoint downPt = [ICCF_downEvent locationInWindow]; |
---|
407 | NSPoint upPt = [upEvent locationInWindow]; |
---|
408 | [ICCF_downEvent release]; |
---|
409 | ICCF_downEvent = nil; |
---|
410 | if (abs(downPt.x - upPt.x) <= kICHysteresisPixels && abs(downPt.y - upPt.y) <= kICHysteresisPixels) { |
---|
411 | if (ICCF_optionClickRegistered) { |
---|
412 | ICCF_optionClickRegistered = NO; |
---|
413 | ICLog(@"========= launching... %d x %d", ICCF_line, ICCF_col); |
---|
414 | ICCF_LaunchURLFromTerminal(self); |
---|
415 | } |
---|
416 | } |
---|
417 | } |
---|
418 | } |
---|
419 | |
---|
420 | - (void)mouseDown:(NSEvent *)downEvent; |
---|
421 | { |
---|
422 | if (ICCF_enabled && ICCF_prefs.commandClickEnabled && ICCF_EventIsCommandMouseDown(downEvent)) { |
---|
423 | 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]; |
---|
424 | [ICCF_downEvent release]; |
---|
425 | ICCF_downEvent = [downEvent retain]; |
---|
426 | ICLog(@"ICeCoffEE Terminal constructed: %@", optionClickEvent); |
---|
427 | ICCF_optionClickRegistered = NO; |
---|
428 | [super mouseDown: optionClickEvent]; |
---|
429 | } else { |
---|
430 | [super mouseDown: downEvent]; |
---|
431 | } |
---|
432 | } |
---|
433 | |
---|
434 | // NSDraggingDestination |
---|
435 | // -[TermSubview draggingUpdated:] just invokes draggingEntered... |
---|
436 | // XXX Crashing on repeated drag snap-back can happen even without ICeCoffEE installed; don't bother to try to fix |
---|
437 | - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; |
---|
438 | { |
---|
439 | if (!ICCF_prefs.terminalRequireOptionForSelfDrag || [sender draggingSource] != self || ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)) { |
---|
440 | [super draggingEntered: sender]; |
---|
441 | // 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. |
---|
442 | return NSDragOperationCopy; |
---|
443 | } |
---|
444 | return NSDragOperationNone; |
---|
445 | } |
---|
446 | |
---|
447 | @end |
---|