[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 |
|
---|
[436] | 42 | @try {
|
---|
[388] | 43 | NSString *s = [self string];
|
---|
| 44 | unsigned length = [s length];
|
---|
| 45 | NSCAssert(s != nil && length != 0, ICCF_LocalizedString(@"No text was found"));
|
---|
| 46 |
|
---|
| 47 | ICCF_StartIC();
|
---|
| 48 |
|
---|
| 49 | // XXX handle discontiguous/rectangular selection with [self valueForKey: @"textSelectionRanges"] => NSIndexSet
|
---|
| 50 | NSCAssert(range.location < length, ICCF_LocalizedString(@"Sorry, ICeCoffEE was unable to find anything to select"));
|
---|
| 51 | if (range.length == 0) {
|
---|
| 52 | range.length = 1;
|
---|
| 53 | range = ICCF_URLEnclosingRange(s, range);
|
---|
| 54 | [self setSelectedRange: range];
|
---|
| 55 | }
|
---|
| 56 |
|
---|
| 57 | iccfURLAction keyboardAction = ICCF_KeyboardAction(triggeringEvent);
|
---|
| 58 | if (ICCF_LaunchURL([s substringWithRange: range], keyboardAction)) {
|
---|
| 59 | if (ICCF_prefs.textBlinkEnabled) {
|
---|
| 60 | for (unsigned i = 0 ; i < ICCF_prefs.textBlinkCount ; i++) {
|
---|
| 61 | [self clearTextSelection];
|
---|
| 62 | [self display];
|
---|
| 63 | usleep(kICBlinkDelayUsecs);
|
---|
| 64 | [self setSelectedRange: range];
|
---|
| 65 | [self display];
|
---|
| 66 | usleep(kICBlinkDelayUsecs);
|
---|
| 67 | }
|
---|
| 68 | }
|
---|
| 69 | } else if (keyboardAction.presentMenu) {
|
---|
| 70 | // mouse up used to cancel menu?
|
---|
| 71 | // XXX next click lost if Esc used; next drag behaves like Command-drag
|
---|
| 72 | ICCF_discardNextMouseUp = YES;
|
---|
| 73 | }
|
---|
[436] | 74 | } @catch (NSException *e) {
|
---|
| 75 | ICCF_HandleException(e, triggeringEvent);
|
---|
| 76 | }
|
---|
[388] | 77 |
|
---|
| 78 | ICCF_StopIC();
|
---|
| 79 | }
|
---|
| 80 |
|
---|
| 81 |
|
---|
| 82 | @implementation ICeCoffEETTView
|
---|
| 83 |
|
---|
| 84 | static NSEvent *ICCF_downEvent;
|
---|
| 85 |
|
---|
| 86 | - (void)mouseUp:(NSEvent *)upEvent;
|
---|
| 87 | {
|
---|
| 88 | if (ICCF_downEvent != nil) {
|
---|
| 89 | NSPoint downPt = [ICCF_downEvent locationInWindow];
|
---|
| 90 | NSPoint upPt = [upEvent locationInWindow];
|
---|
| 91 | if (abs(downPt.x - upPt.x) <= kICHysteresisPixels && abs(downPt.y - upPt.y) <= kICHysteresisPixels) {
|
---|
| 92 | [ICeCoffEETTViewTrigger setTriggerForEvent: ICCF_downEvent onTarget: self];
|
---|
| 93 | ICCF_discardNextMouseUp = YES; // otherwise Command-Option-click => cursor repositioning Option-click
|
---|
| 94 | }
|
---|
| 95 | [ICCF_downEvent release];
|
---|
| 96 | ICCF_downEvent = nil;
|
---|
| 97 | }
|
---|
| 98 | ICLog(@"ICeCoffEETTView up: %@", upEvent);
|
---|
| 99 | if (ICCF_discardNextMouseUp) {
|
---|
| 100 | ICCF_discardNextMouseUp = NO;
|
---|
| 101 | return;
|
---|
| 102 | }
|
---|
| 103 | [super mouseUp: upEvent];
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | - (void)mouseDown:(NSEvent *)downEvent;
|
---|
| 107 | {
|
---|
| 108 | [ICeCoffEETrigger cancel];
|
---|
| 109 |
|
---|
| 110 | if (ICCF_enabled && ICCF_prefs.commandClickEnabled && ICCF_EventIsCommandMouseDown(downEvent)) {
|
---|
| 111 | [ICCF_downEvent release];
|
---|
| 112 | ICCF_downEvent = [downEvent retain];
|
---|
| 113 | }
|
---|
| 114 | ICLog(@"ICeCoffEETTView down: %@", downEvent);
|
---|
| 115 | [super mouseDown: downEvent];
|
---|
| 116 | }
|
---|
| 117 |
|
---|
| 118 | - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
|
---|
| 119 | {
|
---|
| 120 | if (!ICCF_prefs.terminalRequireOptionForSelfDrag || [sender draggingSource] != self || ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)) {
|
---|
| 121 | [super draggingEntered: sender];
|
---|
| 122 | // 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.
|
---|
| 123 | return NSDragOperationCopy;
|
---|
| 124 | }
|
---|
| 125 | return NSDragOperationNone;
|
---|
| 126 | }
|
---|
| 127 |
|
---|
| 128 | @end
|
---|