source: trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEWebKit.m@ 678

Last change on this file since 678 was 495, checked in by Nicholas Riley, 15 years ago

English.proj/APEInfo.rtfd: Updated for 1.5b5.

English.lproj/InfoPlist.strings: Updated copyright date.

English.lproj/Localizable.strings: Don't report an error in WebKit if
there's nothing to launch ("Sorry, ICeCoffEE was unable to find
anything to select").

ICeCoffEE/ICeCoffEE.m: Don't trigger exception with Command-click
outside text range (introduced in 10.5.x?).

ICeCoffEE/ICeCoffEEWebKit.m: Update for WebFrame changes (r39145).

VERSION.xcconfig: Updated for 1.5b5.

File size: 9.0 KB
RevLine 
[66]1//
2// ICeCoffEEWebKit.m
3// ICeCoffEE APE
4//
5// Created by Nicholas Riley on Sun Jan 19 2003.
6// Copyright (c) 2003 Nicholas Riley. All rights reserved.
7//
8
9#import "ICeCoffEEWebKit.h"
[443]10#import "ICeCoffEEWebPolicyDelegate.h"
11#import "ICeCoffEEParser.h"
12#import "ICeCoffEETrigger.h"
13#import <WebKit/WebKit.h>
[66]14#import <unistd.h>
15
[455]16// Safari 3.1 and earlier (pre-r31014)
17// eliminated in http://bugs.webkit.org/show_bug.cgi?id=17640
[495]18// but convertNSRangeToDOMRange: (temporarily) restored in r39145
[449]19@interface WebCoreFrameBridge : NSObject
[443]20- (DOMRange *)convertNSRangeToDOMRange:(NSRange)range;
21- (NSString *)stringForRange:(DOMRange *)range;
[66]22@end
23
[456]24// r31014 and later; likely to move again
25@interface WebFrame (WebFrameInternal)
26- (DOMRange *)_convertNSRangeToDOMRange:(NSRange)range;
27- (NSString *)_stringForRange:(DOMRange *)range;
28@end
29
[443]30// XXX WebHTMLView is going away
[167]31@interface WebHTMLView : NSObject
[66]32
[320]33- (NSRect)selectionRect;
[443]34- (DOMRange *)_documentRange;
[66]35
[443]36- (WebView *)_webView;
[495]37- (WebCoreFrameBridge *)_bridge; // moved from WebNSViewExtras in r14032; removed in r31014 then (temporarily) restored in r39145
[456]38- (WebFrame *)_frame; // moved from WebNSViewExtras in r14032
[167]39
[66]40@end
41
[448]42// from WebViewPrivate.h (will become public, part of WebViewEditing)
43@interface WebView (webViewGrammarChecking)
44- (BOOL)isGrammarCheckingEnabled;
45- (void)setGrammarCheckingEnabled:(BOOL)flag;
46@end
47
[66]48@implementation ICeCoffEEWebKit
49
50- (NSMenu *)menuForEvent:(NSEvent *)e;
51{
52 NSMenu *myMenu = [super menuForEvent: e];
53 return ICCF_MenuForEvent(self, myMenu, e);
54}
55
56static NSEvent *downEvent = nil;
[443]57static id /* (WebPolicyDelegate) */ policyDelegate;
58static id /* DOMRange */ selectedRange;
[167]59
[467]60static BOOL ICCF_DOMParentOfClass(DOMNode *node, Class cls) {
61 do {
62 if ([node isKindOfClass: cls])
63 return YES;
64 } while ( (node = [node parentNode]) != nil);
65
66 return NO;
67}
68#define ICCF_NodeIs(elt) [clickedNode isKindOfClass: NSClassFromString(@"DOMHTML"elt"Element")]
69#define ICCF_DOMParentIs(elt) ICCF_DOMParentOfClass(clickedNode, NSClassFromString(@"DOMHTML"elt"Element"))
70
[66]71- (void)mouseDown:(NSEvent *)e;
72{
[182]73 [downEvent release]; downEvent = nil;
[74]74 if (ICCF_enabled && ICCF_prefs.commandClickEnabled && ICCF_EventIsCommandMouseDown(e)) {
[443]75 if ([self respondsToSelector: @selector(_webView)]) {
76 WebView *webView = [(WebHTMLView *)self _webView];
77
[467]78 // check that the Command-click isn't being used for discontiguous selection
79 NSPoint viewClickPt = [webView convertPoint: [e locationInWindow] fromView: nil];
80 NSDictionary *elementDict = [webView elementAtPoint: viewClickPt];
81 DOMNode *clickedNode = [elementDict objectForKey: @"WebElementDOMNode"];
82 if (clickedNode != nil && ICCF_NodeIs("Select")) {
83 [super mouseDown: e];
84 return;
85 }
86
[182]87 // save selection: it may be deselected on super mouseDown
[443]88 selectedRange = [[webView selectedDOMRange] retain];
89
90 // stop any URL launching from happening
91 if ([webView isEditable]) {
92 policyDelegate = [[webView policyDelegate] retain];
93 [webView setPolicyDelegate: [ICeCoffEEWebPolicyDelegate sharedDelegate]];
94 }
95
96 downEvent = [e retain];
[182]97 }
[66]98 }
99 [super mouseDown: e];
100}
101
102- (void)mouseUp:(NSEvent *)e;
103{
104 [super mouseUp: e];
105
[443]106 if (downEvent == nil)
107 return;
108
109 WebView *webView = [(WebHTMLView *)self _webView];
[455]110 BOOL webViewIsEditable = [webView isEditable];
111 BOOL elementIsEditable = YES; // don't restore
[448]112 BOOL isContinuousSpellCheckingEnabled = NO, isGrammarCheckingEnabled = NO;
[443]113
[455]114 if (webViewIsEditable) {
[443]115 [webView setPolicyDelegate: policyDelegate];
116 [policyDelegate release]; policyDelegate = nil;
117 }
118
119 NSPoint downPt = [downEvent locationInWindow];
120 NSPoint upPt = [e locationInWindow];
121 if (abs(downPt.x - upPt.x) > kICHysteresisPixels && abs(downPt.y - upPt.y) > kICHysteresisPixels)
122 return;
123
124 @try {
[455]125 NSPoint viewClickPt = [webView convertPoint: downPt fromView: nil];
126 NSDictionary *elementDict = [webView elementAtPoint: viewClickPt];
127 ICLog(@"elementDict: %@", elementDict);
128
[495]129 if ([elementDict count] == 0)
130 return;
[455]131
132 elementIsEditable = [[elementDict objectForKey: @"WebElementIsContentEditableKey"] boolValue];
133 if (!elementIsEditable) {
134 NSAssert(!webViewIsEditable, @"Internal error: uneditable element inside editable WebView");
[443]135 [webView setEditable: YES];
[448]136
137 // don't want spelling/grammar marks to persist when view is made uneditable again
138 if ([webView respondsToSelector: @selector(isContinuousSpellCheckingEnabled)] &&
139 (isContinuousSpellCheckingEnabled = [webView isContinuousSpellCheckingEnabled])) {
140 if ([webView respondsToSelector: @selector(setContinuousSpellCheckingEnabled:)])
141 [webView setContinuousSpellCheckingEnabled: NO];
142 else
143 isContinuousSpellCheckingEnabled = NO; // don't restore
144 }
[443]145
[448]146 if ([webView respondsToSelector: @selector(isGrammarCheckingEnabled)] &&
147 (isGrammarCheckingEnabled = [webView isGrammarCheckingEnabled])) {
148 if ([webView respondsToSelector: @selector(setGrammarCheckingEnabled:)])
149 [webView setGrammarCheckingEnabled: NO];
150 else
151 isGrammarCheckingEnabled = NO; // don't restore
152 }
153 }
154
[455]155 DOMNode *clickedNode = [elementDict objectForKey: @"WebElementDOMNode"];
[457]156 BOOL elementIsHTMLInput = ICCF_NodeIs("Input");
[455]157
[457]158 if (!elementIsEditable && (elementIsHTMLInput || ICCF_DOMParentIs("Button") || ICCF_NodeIs("FieldSet"))) {
[455]159 ICLog(@"got an uneditable form field (e.g., button)");
160 return;
161 }
[443]162
163 id link = [elementDict objectForKey: @"WebElementLinkURL"];
164 NSString *url = [link isKindOfClass: [NSURL class]] ? [link absoluteString] : nil;
[455]165
[443]166 ICCF_StartIC();
167
168 id /* DOMRange */ domRange = nil;
169
170 if (url != nil) {
171 ICLog(@"got a link");
[455]172 if (!elementIsEditable) {
[436]173 return;
[182]174 }
[443]175 // XXX handle existing selection
176 domRange = [webView selectedDOMRange];
[455]177 [domRange selectNode: clickedNode];
[443]178 [webView setSelectedDOMRange: domRange affinity: NSSelectionAffinityDownstream];
179 } else {
[455]180 if (!elementIsEditable) // may have become deselected in mouseDown
181 [webView setSelectedDOMRange: selectedRange affinity: NSSelectionAffinityDownstream];
[443]182
183 NSRange range = [ICeCoffEETrigger rangeForEvent: downEvent onTarget: (NSView<NSTextInput> *)self];
[495]184 if (range.location == NSNotFound)
185 return;
[443]186
[456]187 WebFrame *frame = [(WebHTMLView *)self _frame];
188 WebCoreFrameBridge *bridge = nil;
189 if ([self respondsToSelector: @selector(_bridge)])
190 bridge = [(WebHTMLView *)self _bridge];
[455]191
[443]192 // XXX limit to a reasonable size
[455]193 NSString *s;
[457]194 if (elementIsHTMLInput || ICCF_NodeIs("TextArea") || (elementIsEditable && !webViewIsEditable)) {
195 // for form fields, range will be field-relative rather than document-relative
196 ICLog(@"Using attributedSubstringFromRange:");
[455]197 s = [[(NSView<NSTextInput> *)self attributedSubstringFromRange: NSMakeRange(0, UINT_MAX)] string];
[457]198 } else { // sometimes attributedSubstringFromRange: returns nil
199 ICLog(@"Using stringForRange:");
[456]200 domRange = [(WebHTMLView *)self _documentRange];
[495]201 if ([frame respondsToSelector: @selector(_stringForRange:)])
202 s = [frame _stringForRange: domRange];
203 else if (bridge != nil && [bridge respondsToSelector: @selector(stringForRange:)])
[456]204 s = [bridge stringForRange: domRange];
[495]205 else {
206 ICLog(@"WebKit has neither -[WebFrame _stringForRange:] nor -[WebCoreFrameBridge stringForRange:]");
[456]207 return; // WebKit too new?
[495]208 }
[456]209 }
[455]210
[456]211 if (bridge == nil && ![frame respondsToSelector: @selector(_convertNSRangeToDOMRange:)]) {
[495]212 ICLog(@"WebKit has no bridge and no -[WebFrame _convertNSRangeToDOMRange:]");
[456]213 return; // WebKit too new?
214 }
215
[443]216 if (range.length == 0) {
217 range.length = 1;
218 range = ICCF_URLEnclosingRange(s, range);
[456]219 domRange = bridge ? [bridge convertNSRangeToDOMRange: range] :
220 [frame _convertNSRangeToDOMRange: range];
[443]221 [webView setSelectedDOMRange: domRange affinity: NSSelectionAffinityDownstream];
222 } else {
[456]223 domRange = bridge ? [bridge convertNSRangeToDOMRange: range] :
224 [frame _convertNSRangeToDOMRange: range];
[183]225 }
[443]226
227 url = [s substringWithRange: range];
228 }
229
230 if (ICCF_LaunchURL(url, ICCF_KeyboardAction(downEvent)) && ICCF_prefs.textBlinkEnabled && domRange != nil) {
231 NSRect selectionRect = [(WebHTMLView *)self selectionRect];
232 for (int i = 0 ; i < ICCF_prefs.textBlinkCount ; i++) {
233 [webView setSelectedDOMRange: nil affinity: NSSelectionAffinityDownstream];
234 [self setNeedsDisplayInRect: selectionRect];
235 [self display];
236 usleep(kICBlinkDelayUsecs);
237 [webView setSelectedDOMRange: domRange affinity: NSSelectionAffinityDownstream];
238 [self setNeedsDisplayInRect: selectionRect];
239 [self display];
240 usleep(kICBlinkDelayUsecs);
[182]241 }
[436]242 }
[443]243 } @catch (NSException *e) {
244 ICCF_HandleException(e, downEvent);
245 } @finally {
246 [selectedRange release]; selectedRange = nil;
247 [downEvent release]; downEvent = nil;
[455]248 if (!elementIsEditable && !webViewIsEditable) {
[443]249 [webView setEditable: NO];
[448]250 if (isContinuousSpellCheckingEnabled)
251 [webView setContinuousSpellCheckingEnabled: YES];
252 if (isGrammarCheckingEnabled)
253 [webView setGrammarCheckingEnabled: YES];
254 }
[456]255 ICCF_StopIC();
[443]256 }
[66]257}
258
259@end
Note: See TracBrowser for help on using the repository browser.