[388] | 1 | //
|
---|
| 2 | // ICeCoffEETTView.m
|
---|
| 3 | // ICeCoffEE
|
---|
| 4 | //
|
---|
| 5 | // Created by Nicholas Riley on 2/21/08.
|
---|
| 6 | // Copyright 2008 Nicholas Riley. All rights reserved.
|
---|
| 7 | //
|
---|
| 8 |
|
---|
| 9 | #import "ICeCoffEETTView.h"
|
---|
| 10 | #import "ICeCoffEE.h"
|
---|
| 11 | #import "ICeCoffEEParser.h"
|
---|
| 12 | #import "ICeCoffEETTViewTrigger.h"
|
---|
| 13 | #include <unistd.h>
|
---|
| 14 |
|
---|
| 15 | static NSRange ICCF_zeroRange = { NSNotFound, 5 };
|
---|
| 16 |
|
---|
| 17 | @implementation ICeCoffEETTViewSuper
|
---|
| 18 | // NSTextInput implementation
|
---|
| 19 | - (void)insertText:(id)aString {}
|
---|
| 20 | - (void)doCommandBySelector:(SEL)aSelector {}
|
---|
| 21 | - (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange {}
|
---|
| 22 | - (void)unmarkText {}
|
---|
| 23 | - (BOOL)hasMarkedText { return NO; }
|
---|
| 24 | - (long)conversationIdentifier { return 0; }
|
---|
| 25 | - (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange { return nil; }
|
---|
| 26 | - (NSRange)markedRange { return ICCF_zeroRange; }
|
---|
| 27 | - (NSRange)selectedRange { return ICCF_zeroRange; }
|
---|
| 28 | - (NSRect)firstRectForCharacterRange:(NSRange)theRange { return NSZeroRect; }
|
---|
| 29 | - (unsigned int)characterIndexForPoint:(NSPoint)thePoint { return 0; }
|
---|
| 30 | - (NSArray*)validAttributesForMarkedText { return nil; }
|
---|
| 31 | // Selection
|
---|
| 32 | - (void)clearTextSelection {}
|
---|
| 33 | - (void)setSelectedRange:(NSRange)charRange {}
|
---|
| 34 | // misc. other stuff
|
---|
| 35 | - (NSString *)string { return nil; }
|
---|
| 36 | @end
|
---|
| 37 |
|
---|
| 38 | static BOOL ICCF_discardNextMouseUp = NO;
|
---|
| 39 |
|
---|
| 40 | void ICCF_LaunchURLFromTTView(ICeCoffEETTView *self, NSEvent *triggeringEvent, NSRange range) {
|
---|
| 41 |
|
---|
| 42 | NS_DURING
|
---|
| 43 |
|
---|
| 44 | NSString *s = [self string];
|
---|
| 45 | unsigned length = [s length];
|
---|
| 46 | NSCAssert(s != nil && length != 0, ICCF_LocalizedString(@"No text was found"));
|
---|
| 47 |
|
---|
| 48 | ICCF_StartIC();
|
---|
| 49 |
|
---|
| 50 | // XXX handle discontiguous/rectangular selection with [self valueForKey: @"textSelectionRanges"] => NSIndexSet
|
---|
| 51 | NSCAssert(range.location < length, ICCF_LocalizedString(@"Sorry, ICeCoffEE was unable to find anything to select"));
|
---|
| 52 | if (range.length == 0) {
|
---|
| 53 | range.length = 1;
|
---|
| 54 | range = ICCF_URLEnclosingRange(s, range);
|
---|
| 55 | [self setSelectedRange: range];
|
---|
| 56 | }
|
---|
| 57 |
|
---|
| 58 | iccfURLAction keyboardAction = ICCF_KeyboardAction(triggeringEvent);
|
---|
| 59 | if (ICCF_LaunchURL([s substringWithRange: range], keyboardAction)) {
|
---|
| 60 | if (ICCF_prefs.textBlinkEnabled) {
|
---|
| 61 | for (unsigned i = 0 ; i < ICCF_prefs.textBlinkCount ; i++) {
|
---|
| 62 | [self clearTextSelection];
|
---|
| 63 | [self display];
|
---|
| 64 | usleep(kICBlinkDelayUsecs);
|
---|
| 65 | [self setSelectedRange: range];
|
---|
| 66 | [self display];
|
---|
| 67 | usleep(kICBlinkDelayUsecs);
|
---|
| 68 | }
|
---|
| 69 | }
|
---|
| 70 | } else if (keyboardAction.presentMenu) {
|
---|
| 71 | // mouse up used to cancel menu?
|
---|
| 72 | // XXX next click lost if Esc used; next drag behaves like Command-drag
|
---|
| 73 | ICCF_discardNextMouseUp = YES;
|
---|
| 74 | }
|
---|
| 75 |
|
---|
| 76 | NS_HANDLER
|
---|
| 77 | ICCF_HandleException(localException, triggeringEvent);
|
---|
| 78 | NS_ENDHANDLER
|
---|
| 79 |
|
---|
| 80 | ICCF_StopIC();
|
---|
| 81 | }
|
---|
| 82 |
|
---|
| 83 |
|
---|
| 84 | @implementation ICeCoffEETTView
|
---|
| 85 |
|
---|
| 86 | static NSEvent *ICCF_downEvent;
|
---|
| 87 |
|
---|
| 88 | - (void)mouseUp:(NSEvent *)upEvent;
|
---|
| 89 | {
|
---|
| 90 | if (ICCF_downEvent != nil) {
|
---|
| 91 | NSPoint downPt = [ICCF_downEvent locationInWindow];
|
---|
| 92 | NSPoint upPt = [upEvent locationInWindow];
|
---|
| 93 | if (abs(downPt.x - upPt.x) <= kICHysteresisPixels && abs(downPt.y - upPt.y) <= kICHysteresisPixels) {
|
---|
| 94 | [ICeCoffEETTViewTrigger setTriggerForEvent: ICCF_downEvent onTarget: self];
|
---|
| 95 | ICCF_discardNextMouseUp = YES; // otherwise Command-Option-click => cursor repositioning Option-click
|
---|
| 96 | }
|
---|
| 97 | [ICCF_downEvent release];
|
---|
| 98 | ICCF_downEvent = nil;
|
---|
| 99 | }
|
---|
| 100 | ICLog(@"ICeCoffEETTView up: %@", upEvent);
|
---|
| 101 | if (ICCF_discardNextMouseUp) {
|
---|
| 102 | ICCF_discardNextMouseUp = NO;
|
---|
| 103 | return;
|
---|
| 104 | }
|
---|
| 105 | [super mouseUp: upEvent];
|
---|
| 106 | }
|
---|
| 107 |
|
---|
| 108 | - (void)mouseDown:(NSEvent *)downEvent;
|
---|
| 109 | {
|
---|
| 110 | [ICeCoffEETrigger cancel];
|
---|
| 111 |
|
---|
| 112 | if (ICCF_enabled && ICCF_prefs.commandClickEnabled && ICCF_EventIsCommandMouseDown(downEvent)) {
|
---|
| 113 | [ICCF_downEvent release];
|
---|
| 114 | ICCF_downEvent = [downEvent retain];
|
---|
| 115 | }
|
---|
| 116 | ICLog(@"ICeCoffEETTView down: %@", downEvent);
|
---|
| 117 | [super mouseDown: downEvent];
|
---|
| 118 | }
|
---|
| 119 |
|
---|
| 120 | - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
|
---|
| 121 | {
|
---|
| 122 | if (!ICCF_prefs.terminalRequireOptionForSelfDrag || [sender draggingSource] != self || ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)) {
|
---|
| 123 | [super draggingEntered: sender];
|
---|
| 124 | // 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.
|
---|
| 125 | return NSDragOperationCopy;
|
---|
| 126 | }
|
---|
| 127 | return NSDragOperationNone;
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 | @end
|
---|