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

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

Disable on list boxes.

File size: 8.8 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
59static BOOL ICCF_DOMParentOfClass(DOMNode *node, Class cls) {
60 do {
61 if ([node isKindOfClass: cls])
62 return YES;
63 } while ( (node = [node parentNode]) != nil);
64
65 return NO;
66}
67#define ICCF_NodeIs(elt) [clickedNode isKindOfClass: NSClassFromString(@"DOMHTML"elt"Element")]
68#define ICCF_DOMParentIs(elt) ICCF_DOMParentOfClass(clickedNode, NSClassFromString(@"DOMHTML"elt"Element"))
69
70- (void)mouseDown:(NSEvent *)e;
71{
72 [downEvent release]; downEvent = nil;
73 if (ICCF_enabled && ICCF_prefs.commandClickEnabled && ICCF_EventIsCommandMouseDown(e)) {
74 if ([self respondsToSelector: @selector(_webView)]) {
75 WebView *webView = [(WebHTMLView *)self _webView];
76
77 // check that the Command-click isn't being used for discontiguous selection
78 NSPoint viewClickPt = [webView convertPoint: [e locationInWindow] fromView: nil];
79 NSDictionary *elementDict = [webView elementAtPoint: viewClickPt];
80 DOMNode *clickedNode = [elementDict objectForKey: @"WebElementDOMNode"];
81 if (clickedNode != nil && ICCF_NodeIs("Select")) {
82 [super mouseDown: e];
83 return;
84 }
85
86 // save selection: it may be deselected on super mouseDown
87 selectedRange = [[webView selectedDOMRange] retain];
88
89 // stop any URL launching from happening
90 if ([webView isEditable]) {
91 policyDelegate = [[webView policyDelegate] retain];
92 [webView setPolicyDelegate: [ICeCoffEEWebPolicyDelegate sharedDelegate]];
93 }
94
95 downEvent = [e retain];
96 }
97 }
98 [super mouseDown: e];
99}
100
101- (void)mouseUp:(NSEvent *)e;
102{
103 [super mouseUp: e];
104
105 if (downEvent == nil)
106 return;
107
108 WebView *webView = [(WebHTMLView *)self _webView];
109 BOOL webViewIsEditable = [webView isEditable];
110 BOOL elementIsEditable = YES; // don't restore
111 BOOL isContinuousSpellCheckingEnabled = NO, isGrammarCheckingEnabled = NO;
112
113 if (webViewIsEditable) {
114 [webView setPolicyDelegate: policyDelegate];
115 [policyDelegate release]; policyDelegate = nil;
116 }
117
118 NSPoint downPt = [downEvent locationInWindow];
119 NSPoint upPt = [e locationInWindow];
120 if (abs(downPt.x - upPt.x) > kICHysteresisPixels && abs(downPt.y - upPt.y) > kICHysteresisPixels)
121 return;
122
123 @try {
124 NSPoint viewClickPt = [webView convertPoint: downPt fromView: nil];
125 NSDictionary *elementDict = [webView elementAtPoint: viewClickPt];
126 ICLog(@"elementDict: %@", elementDict);
127
128 NSAssert([elementDict count] != 0, ICCF_LocalizedString(@"Sorry, ICeCoffEE was unable to find anything to select"));
129
130 elementIsEditable = [[elementDict objectForKey: @"WebElementIsContentEditableKey"] boolValue];
131 if (!elementIsEditable) {
132 NSAssert(!webViewIsEditable, @"Internal error: uneditable element inside editable WebView");
133 [webView setEditable: YES];
134
135 // don't want spelling/grammar marks to persist when view is made uneditable again
136 if ([webView respondsToSelector: @selector(isContinuousSpellCheckingEnabled)] &&
137 (isContinuousSpellCheckingEnabled = [webView isContinuousSpellCheckingEnabled])) {
138 if ([webView respondsToSelector: @selector(setContinuousSpellCheckingEnabled:)])
139 [webView setContinuousSpellCheckingEnabled: NO];
140 else
141 isContinuousSpellCheckingEnabled = NO; // don't restore
142 }
143
144 if ([webView respondsToSelector: @selector(isGrammarCheckingEnabled)] &&
145 (isGrammarCheckingEnabled = [webView isGrammarCheckingEnabled])) {
146 if ([webView respondsToSelector: @selector(setGrammarCheckingEnabled:)])
147 [webView setGrammarCheckingEnabled: NO];
148 else
149 isGrammarCheckingEnabled = NO; // don't restore
150 }
151 }
152
153 DOMNode *clickedNode = [elementDict objectForKey: @"WebElementDOMNode"];
154 BOOL elementIsHTMLInput = ICCF_NodeIs("Input");
155
156 if (!elementIsEditable && (elementIsHTMLInput || ICCF_DOMParentIs("Button") || ICCF_NodeIs("FieldSet"))) {
157 ICLog(@"got an uneditable form field (e.g., button)");
158 return;
159 }
160
161 id link = [elementDict objectForKey: @"WebElementLinkURL"];
162 NSString *url = [link isKindOfClass: [NSURL class]] ? [link absoluteString] : nil;
163
164 ICCF_StartIC();
165
166 id /* DOMRange */ domRange = nil;
167
168 if (url != nil) {
169 ICLog(@"got a link");
170 if (!elementIsEditable) {
171 return;
172 }
173 // XXX handle existing selection
174 domRange = [webView selectedDOMRange];
175 [domRange selectNode: clickedNode];
176 [webView setSelectedDOMRange: domRange affinity: NSSelectionAffinityDownstream];
177 } else {
178 if (!elementIsEditable) // may have become deselected in mouseDown
179 [webView setSelectedDOMRange: selectedRange affinity: NSSelectionAffinityDownstream];
180
181 NSRange range = [ICeCoffEETrigger rangeForEvent: downEvent onTarget: (NSView<NSTextInput> *)self];
182 NSAssert(range.location != NSNotFound, ICCF_LocalizedString(@"Sorry, ICeCoffEE was unable to find anything to select"));
183
184 WebFrame *frame = [(WebHTMLView *)self _frame];
185 WebCoreFrameBridge *bridge = nil;
186 if ([self respondsToSelector: @selector(_bridge)])
187 bridge = [(WebHTMLView *)self _bridge];
188
189 // XXX limit to a reasonable size
190 NSString *s;
191 if (elementIsHTMLInput || ICCF_NodeIs("TextArea") || (elementIsEditable && !webViewIsEditable)) {
192 // for form fields, range will be field-relative rather than document-relative
193 ICLog(@"Using attributedSubstringFromRange:");
194 s = [[(NSView<NSTextInput> *)self attributedSubstringFromRange: NSMakeRange(0, UINT_MAX)] string];
195 } else { // sometimes attributedSubstringFromRange: returns nil
196 ICLog(@"Using stringForRange:");
197 domRange = [(WebHTMLView *)self _documentRange];
198 if (bridge != nil)
199 s = [bridge stringForRange: domRange];
200 else if (![frame respondsToSelector: @selector(_stringForRange:)])
201 return; // WebKit too new?
202 else
203 s = [frame _stringForRange: domRange];
204 }
205
206 if (bridge == nil && ![frame respondsToSelector: @selector(_convertNSRangeToDOMRange:)]) {
207 return; // WebKit too new?
208 }
209
210 if (range.length == 0) {
211 range.length = 1;
212 range = ICCF_URLEnclosingRange(s, range);
213 domRange = bridge ? [bridge convertNSRangeToDOMRange: range] :
214 [frame _convertNSRangeToDOMRange: range];
215 [webView setSelectedDOMRange: domRange affinity: NSSelectionAffinityDownstream];
216 } else {
217 domRange = bridge ? [bridge convertNSRangeToDOMRange: range] :
218 [frame _convertNSRangeToDOMRange: range];
219 }
220
221 url = [s substringWithRange: range];
222 }
223
224 if (ICCF_LaunchURL(url, ICCF_KeyboardAction(downEvent)) && ICCF_prefs.textBlinkEnabled && domRange != nil) {
225 NSRect selectionRect = [(WebHTMLView *)self selectionRect];
226 for (int i = 0 ; i < ICCF_prefs.textBlinkCount ; i++) {
227 [webView setSelectedDOMRange: nil affinity: NSSelectionAffinityDownstream];
228 [self setNeedsDisplayInRect: selectionRect];
229 [self display];
230 usleep(kICBlinkDelayUsecs);
231 [webView setSelectedDOMRange: domRange affinity: NSSelectionAffinityDownstream];
232 [self setNeedsDisplayInRect: selectionRect];
233 [self display];
234 usleep(kICBlinkDelayUsecs);
235 }
236 }
237 } @catch (NSException *e) {
238 ICCF_HandleException(e, downEvent);
239 } @finally {
240 [selectedRange release]; selectedRange = nil;
241 [downEvent release]; downEvent = nil;
242 if (!elementIsEditable && !webViewIsEditable) {
243 [webView setEditable: NO];
244 if (isContinuousSpellCheckingEnabled)
245 [webView setContinuousSpellCheckingEnabled: YES];
246 if (isGrammarCheckingEnabled)
247 [webView setGrammarCheckingEnabled: YES];
248 }
249 ICCF_StopIC();
250 }
251}
252
253@end
Note: See TracBrowser for help on using the repository browser.