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

Last change on this file since 456 was 456, checked in by Nicholas Riley, 16 years ago

Support WebKit past r31014; patch PDFView in some places (not WebKit yet).

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