318d6e92c2333339734ddd3ccd9761a966898a56
[WebKit-https.git] / WebKit / WebView.subproj / WebFrameView.m
1 /*      WebFrameView.m
2         Copyright 2001, 2002, Apple Computer, Inc. All rights reserved.
3 */
4
5 #import <WebKit/WebFrameView.h>
6
7 #import <WebKit/WebBridge.h>
8 #import <WebKit/WebClipView.h>
9 #import <WebKit/WebCookieAdapter.h>
10 #import <WebKit/WebDataSource.h>
11 #import <WebKit/WebDocument.h>
12 #import <WebKit/WebDynamicScrollBarsView.h>
13 #import <WebKit/WebFrame.h>
14 #import <WebKit/WebFrameViewPrivate.h>
15 #import <WebKit/WebHTMLViewPrivate.h>
16 #import <WebKit/WebGraphicsBridge.h>
17 #import <WebKit/WebImageRenderer.h>
18 #import <WebKit/WebImageRendererFactory.h>
19 #import <WebKit/WebImageView.h>
20 #import <WebKit/WebKeyGenerator.h>
21 #import <WebKit/WebKitErrorsPrivate.h>
22 #import <WebKit/WebKitStatisticsPrivate.h>
23 #import <WebKit/WebNSObjectExtras.h>
24 #import <WebKit/WebNSPasteboardExtras.h>
25 #import <WebKit/WebNSViewExtras.h>
26 // Assume we'll only ever compile this on Panther or greater, so 
27 // MAC_OS_X_VERSION_10_3 is guranateed to be defined.
28 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_3
29 #import <WebKit/WebPDFView.h>
30 #endif
31 #import <WebKit/WebTextRendererFactory.h>
32 #import <WebKit/WebTextView.h>
33 #import <WebKit/WebViewFactory.h>
34 #import <WebKit/WebViewInternal.h>
35 #import <WebKit/WebViewPrivate.h>
36 #import <WebKit/WebAssertions.h>
37
38 #import <Foundation/NSDictionary_NSURLExtras.h>
39 #import <Foundation/NSURLRequest.h>
40
41 @interface NSClipView (AppKitSecretsIKnow)
42 - (BOOL)_scrollTo:(const NSPoint *)newOrigin; // need the boolean result from this method
43 @end
44
45 enum {
46     SpaceKey = 0x0020
47 };
48
49 @implementation WebFrameViewPrivate
50
51 - init
52 {
53     [super init];
54     
55     marginWidth = -1;
56     marginHeight = -1;
57     
58     return self;
59 }
60
61 - (void)dealloc
62 {
63     [frameScrollView release];
64     [draggingTypes release];
65     [super dealloc];
66 }
67
68 @end
69
70 @implementation WebFrameView (WebPrivate)
71
72 // Note that the WebVew is not retained.
73 - (WebView *)_webView
74 {
75     return _private->webView;
76 }
77
78
79 - (void)_setMarginWidth: (int)w
80 {
81     _private->marginWidth = w;
82 }
83
84 - (int)_marginWidth
85 {
86     return _private->marginWidth;
87 }
88
89 - (void)_setMarginHeight: (int)h
90 {
91     _private->marginHeight = h;
92 }
93
94 - (int)_marginHeight
95 {
96     return _private->marginHeight;
97 }
98
99 - (void)_setDocumentView:(NSView *)view
100 {
101     WebDynamicScrollBarsView *sv = (WebDynamicScrollBarsView *)[self _scrollView];
102     
103     [sv setSuppressLayout: YES];
104     
105     // Always start out with arrow.  New docView can then change as needed, but doesn't have to
106     // clean up after the previous docView.  Also TextView will set cursor when added to view
107     // tree, so must do this before setDocumentView:.
108     [sv setDocumentCursor:[NSCursor arrowCursor]];
109
110     [sv setDocumentView: view];
111     [sv setSuppressLayout: NO];
112 }
113
114 -(NSView <WebDocumentView> *)_makeDocumentViewForDataSource:(WebDataSource *)dataSource
115 {    
116     Class viewClass = [[self class] _viewClassForMIMEType:[[dataSource response] MIMEType]];
117     NSView <WebDocumentView> *documentView = viewClass ? [[viewClass alloc] init] : nil;
118     [self _setDocumentView:documentView];
119     [documentView release];
120     
121     return documentView;
122 }
123
124 - (void)_setWebView:(WebView *)webView
125 {
126     // Not retained because the WebView owns the WebFrame, which owns the WebFrameView.
127     _private->webView = webView;    
128 }
129
130 - (NSScrollView *)_scrollView
131 {
132     // this can be called by [super dealloc] when cleaning up the keyview loop,
133     // after _private has been nilled out.
134     if (_private == nil) {
135         return nil;
136     }
137     return _private->frameScrollView;
138 }
139
140
141 - (NSClipView *)_contentView
142 {
143     return [[self _scrollView] contentView];
144 }
145
146 - (BOOL)_scrollVerticallyBy: (float)delta
147 {
148     // This method uses the secret method _scrollTo on NSClipView.
149     // It does that because it needs to know definitively whether scrolling was
150     // done or not to help implement the "scroll parent if we are at the limit" feature.
151     // In the presence of smooth scrolling, there's no easy way to tell if the method
152     // did any scrolling or not with the public API.
153     NSPoint point = [[self _contentView] bounds].origin;
154     point.y += delta;
155     return [[self _contentView] _scrollTo:&point];
156 }
157
158 - (void)_scrollHorizontallyBy: (float)delta
159 {
160     NSPoint point = [[self _contentView] bounds].origin;
161     point.x += delta;
162     [[self _contentView] scrollPoint: point];
163 }
164
165 - (float)_verticalKeyboardScrollAmount
166 {
167     // verticalLineScroll is quite small, to make scrolling from the scroll bar
168     // arrows relatively smooth. But this seemed too small for scrolling with
169     // the arrow keys, so we bump up the number here. Cheating? Perhaps.
170     return [[self _scrollView] verticalLineScroll] * 4;
171 }
172
173 - (float)_horizontalKeyboardScrollAmount
174 {
175     // verticalLineScroll is quite small, to make scrolling from the scroll bar
176     // arrows relatively smooth. But this seemed too small for scrolling with
177     // the arrow keys, so we bump up the number here. Cheating? Perhaps.
178     return [[self _scrollView] horizontalLineScroll] * 4;
179 }
180
181 - (BOOL)_pageVertically:(BOOL)up
182 {
183     float pageOverlap = [self _verticalKeyboardScrollAmount];
184     float delta = [[self _contentView] bounds].size.height;
185     
186     delta = (delta < pageOverlap) ? delta / 2.0 : delta - pageOverlap;
187
188     if (up) {
189         delta = -delta;
190     }
191
192     return [self _scrollVerticallyBy: delta];
193 }
194
195 - (void)_pageHorizontally: (BOOL)left
196 {
197     float pageOverlap = [self _horizontalKeyboardScrollAmount];
198     float delta = [[self _contentView] bounds].size.width;
199     
200     delta = (delta < pageOverlap) ? delta / 2.0 : delta - pageOverlap;
201
202     if (left) {
203         delta = -delta;
204     }
205
206     [self _scrollHorizontallyBy: delta];
207 }
208
209 - (void)_scrollLineVertically: (BOOL)up
210 {
211     float delta = [self _verticalKeyboardScrollAmount];
212
213     if (up) {
214         delta = -delta;
215     }
216
217     [self _scrollVerticallyBy: delta];
218 }
219
220 - (void)_scrollLineHorizontally: (BOOL)left
221 {
222     float delta = [self _horizontalKeyboardScrollAmount];
223
224     if (left) {
225         delta = -delta;
226     }
227
228     [self _scrollHorizontallyBy: delta];
229 }
230
231 - (void)scrollPageUp:(id)sender
232 {
233     if (![self _pageVertically:YES]) {
234         // If we were already at the top, tell the next responder to scroll if it can.
235         [[self nextResponder] tryToPerform:@selector(scrollPageUp:) with:sender];
236     }
237 }
238
239 - (void)scrollPageDown:(id)sender
240 {
241     if (![self _pageVertically:NO]) {
242         // If we were already at the bottom, tell the next responder to scroll if it can.
243         [[self nextResponder] tryToPerform:@selector(scrollPageDown:) with:sender];
244     }
245 }
246
247 - (void)_pageLeft
248 {
249     [self _pageHorizontally:YES];
250 }
251
252 - (void)_pageRight
253 {
254     [self _pageHorizontally:NO];
255 }
256
257 - (void)_scrollToTopLeft
258 {
259     [[self _contentView] scrollPoint:[[[self _scrollView] documentView] frame].origin];
260 }
261
262 - (void)_scrollToBottomLeft
263 {
264     NSRect frame = [[[self _scrollView] documentView] frame];
265     [[self _contentView] scrollPoint:NSMakePoint(frame.origin.x, NSMaxY(frame))];
266 }
267
268 - (void)scrollLineUp:(id)sender
269 {
270     [self _scrollLineVertically: YES];
271 }
272
273 - (void)scrollLineDown:(id)sender
274 {
275     [self _scrollLineVertically: NO];
276 }
277
278 - (void)_lineLeft
279 {
280     [self _scrollLineHorizontally: YES];
281 }
282
283 - (void)_lineRight
284 {
285     [self _scrollLineHorizontally: NO];
286 }
287
288 static NSMutableDictionary *viewTypes;
289
290 + (NSMutableDictionary *)_viewTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission
291 {
292     static BOOL addedImageTypes;
293
294     if (!viewTypes) {
295         viewTypes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
296             [WebHTMLView class], @"text/html",
297             [WebHTMLView class], @"text/xml",
298             [WebHTMLView class], @"text/xsl",
299             [WebHTMLView class], @"application/xml",
300             [WebHTMLView class], @"application/xhtml+xml",
301             [WebHTMLView class], @"application/x-webarchive",
302             [WebTextView class], @"text/",
303             [WebTextView class], @"application/x-javascript",
304 // Assume we'll only ever compile this on Panther or greater, so 
305 // MAC_OS_X_VERSION_10_3 is guranateed to be defined.
306 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_3
307             [WebPDFView class], @"application/pdf",
308 #endif
309             nil];
310     }
311
312     if (!addedImageTypes && !allowImageTypeOmission) {
313         NSEnumerator *enumerator = [[WebImageView supportedImageMIMETypes] objectEnumerator];
314         ASSERT(enumerator != nil);
315         NSString *mime;
316         while ((mime = [enumerator nextObject]) != nil) {
317             // Don't clobber previously-registered view classes.
318             if ([viewTypes objectForKey:mime] == nil) {
319                 [viewTypes setObject:[WebImageView class] forKey:mime];
320             }
321         }
322         addedImageTypes = YES;
323     }
324     
325     return viewTypes;
326 }
327
328 + (BOOL)_canShowMIMETypeAsHTML:(NSString *)MIMEType
329 {
330     return ([viewTypes objectForKey:MIMEType] == [WebHTMLView class]);
331 }
332
333 + (Class)_viewClassForMIMEType:(NSString *)MIMEType
334 {
335     Class viewClass;
336     return [WebView _viewClass:&viewClass andRepresentationClass:nil forMIMEType:MIMEType] ? viewClass : nil;
337 }
338
339 - (void)_goBack
340 {
341     [_private->webView goBack];
342 }
343
344 - (void)_goForward
345 {
346     [_private->webView goForward];
347 }
348
349 - (BOOL)_isMainFrame
350 {
351     return [_private->webView mainFrame] == [self webFrame];
352 }
353
354 - (void)_tile
355 {
356     NSRect scrollViewFrame = [self bounds];
357     // The border drawn by WebFrameView is 1 pixel on the left and right,
358     // two pixels on top and bottom.  Shrink the scroll view to accomodate
359     // the border.
360     if ([self _shouldDrawBorder]){
361         scrollViewFrame = NSInsetRect (scrollViewFrame, 1, 2);
362     }
363     [_private->frameScrollView setFrame:scrollViewFrame];
364 }
365
366 - (void)_drawBorder
367 {
368     if ([self _shouldDrawBorder]){
369         NSRect vRect = [self frame];
370             
371         // Left, black
372         [[NSColor blackColor] set];
373         NSRectFill(NSMakeRect(0,0,1,vRect.size.height));
374         
375         // Top, light gray, black
376         [[NSColor lightGrayColor] set];
377         NSRectFill(NSMakeRect(0,0,vRect.size.width,1));
378         [[NSColor whiteColor] set];
379         NSRectFill(NSMakeRect(1,1,vRect.size.width-2,1));
380         
381         // Right, light gray
382         [[NSColor lightGrayColor] set];
383         NSRectFill(NSMakeRect(vRect.size.width-1,1,1,vRect.size.height-2));
384         
385         // Bottom, light gray, white
386         [[NSColor blackColor] set];
387         NSRectFill(NSMakeRect(0,vRect.size.height-1,vRect.size.width,1));
388         [[NSColor lightGrayColor] set];
389         NSRectFill(NSMakeRect(1,vRect.size.height-2,vRect.size.width-2,1));
390     }
391 }
392
393 - (BOOL)_shouldDrawBorder
394 {
395     if (!_private->hasBorder)
396         return NO;
397         
398     // Only draw a border for frames that request a border and the frame does
399     // not contain a frameset.  Additionally we should (post-panther) not draw
400     // a border (left, right, top or bottom) if the frame edge abutts the window frame.
401     NSView *docV = [self documentView];
402     if ([docV isKindOfClass:[WebHTMLView class]]){
403         if ([[(WebHTMLView *)docV _bridge] isFrameSet]){
404             return NO;
405         }
406     }
407     return YES;
408 }
409
410 - (void)_setHasBorder:(BOOL)hasBorder
411 {
412     if (_private->hasBorder == hasBorder) {
413         return;
414     }
415     _private->hasBorder = hasBorder;
416     [self _tile];
417 }
418
419 - (BOOL)_firstResponderIsControl
420 {
421     return [[[self window] firstResponder] isKindOfClass:[NSControl class]];
422 }
423
424 @end
425
426 @implementation WebFrameView
427
428 - initWithFrame: (NSRect) frame
429 {
430     [super initWithFrame: frame];
431  
432     [WebViewFactory createSharedFactory];
433     [WebTextRendererFactory createSharedFactory];
434     [WebImageRendererFactory createSharedFactory];
435     [WebCookieAdapter createSharedAdapter];
436     [WebGraphicsBridge createSharedBridge];
437     [WebKeyGenerator createSharedGenerator];
438     
439     _private = [[WebFrameViewPrivate alloc] init];
440
441     WebDynamicScrollBarsView *scrollView  = [[WebDynamicScrollBarsView alloc] initWithFrame: NSMakeRect(0,0,frame.size.width,frame.size.height)];
442     _private->frameScrollView = scrollView;
443     [scrollView setContentView: [[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]];
444     [scrollView setDrawsBackground: NO];
445     [scrollView setHasVerticalScroller: NO];
446     [scrollView setHasHorizontalScroller: NO];
447     [scrollView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
448     [self addSubview: scrollView];
449     // don't call our overridden version here; we need to make the standard NSView link between us
450     // and our subview so that previousKeyView and previousValidKeyView work as expected. This works
451     // together with our becomeFirstResponder and setNextKeyView overrides.
452     [super setNextKeyView:scrollView];
453     
454     ++WebFrameViewCount;
455     
456     return self;
457 }
458
459 - (void)dealloc 
460 {
461     --WebFrameViewCount;
462     
463     [_private release];
464     _private = nil;
465     
466     [super dealloc];
467 }
468
469 - (void)finalize 
470 {
471     --WebFrameViewCount;
472
473     _private = nil;
474
475     [super finalize];
476 }
477
478 - (WebFrame *)webFrame
479 {
480     return [[self _webView] _frameForView: self]; 
481 }
482
483
484 - (void)setAllowsScrolling: (BOOL)flag
485 {
486     [(WebDynamicScrollBarsView *)[self _scrollView] setAllowsScrolling: flag];
487 }
488
489 - (BOOL)allowsScrolling
490 {
491     return [(WebDynamicScrollBarsView *)[self _scrollView] allowsScrolling];
492 }
493
494 - documentView
495 {
496     return [[self _scrollView] documentView];
497 }
498
499 - (BOOL)acceptsFirstResponder
500 {
501     // We always accept first responder; this matches OS X 10.2 WebKit
502     // behavior (see 3469791).
503     return YES;
504 }
505
506 - (BOOL)becomeFirstResponder
507 {
508     // This works together with setNextKeyView to splice the WebFrameView into
509     // the key loop similar to the way NSScrollView does this. Note that
510     // WebView has similar code.
511     
512     // If the scrollView won't accept first-responderness now, then we just become
513     // the first responder ourself like a normal view. This lets us be the first 
514     // responder in cases where no page has yet been loaded (see 3469791).
515     if ([[self _scrollView] acceptsFirstResponder]) {
516         NSWindow *window = [self window];
517         if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
518             NSView *previousValidKeyView = [self previousValidKeyView];
519             if ((previousValidKeyView != self) && (previousValidKeyView != [self _scrollView])) {
520                 [window makeFirstResponder:previousValidKeyView];
521             }
522         } else {
523             [window makeFirstResponder:[self _scrollView]];
524         }
525     }    
526     
527     return YES;
528 }
529
530 - (void)setNextKeyView:(NSView *)aView
531 {
532     // This works together with becomeFirstResponder to splice the WebFrameView into
533     // the key loop similar to the way NSScrollView does this. Note that
534     // WebView has very similar code.
535     if ([self _scrollView] != nil) {
536         [[self _scrollView] setNextKeyView:aView];
537     } else {
538         [super setNextKeyView:aView];
539     }
540 }
541
542 - (BOOL)isOpaque
543 {
544     return [[self _webView] drawsBackground];
545 }
546
547 - (void)drawRect:(NSRect)rect
548 {
549     if ([self documentView] == nil) {
550         // Need to paint ourselves if there's no documentView to do it instead.
551         [[NSColor whiteColor] set];
552         NSRectFill(rect);
553     } else {
554 #ifndef NDEBUG
555         if ([[self _scrollView] drawsBackground]) {
556             [[NSColor cyanColor] set];
557             NSRectFill(rect);
558         }
559 #endif
560     }
561     
562     [self _drawBorder];
563 }
564
565 - (void)setFrameSize:(NSSize)size
566 {
567     if (!NSEqualSizes(size, [self frame].size) && [[[self webFrame] webView] drawsBackground]) {
568         [[self _scrollView] setDrawsBackground:YES];
569     }
570     [super setFrameSize:size];
571     [self _tile];
572 }
573
574 - (void)keyDown:(NSEvent *)event
575 {
576     NSString *characters = [event characters];
577     int index, count;
578     BOOL callSuper = YES;
579     BOOL maintainsBackForwardList = [[[self webFrame] webView] backForwardList] == nil ? NO : YES;
580
581     count = [characters length];
582     for (index = 0; index < count; ++index) {
583         switch ([characters characterAtIndex:index]) {
584             case NSDeleteCharacter:
585                 if (!maintainsBackForwardList) {
586                     callSuper = YES;
587                     break;
588                 }
589                 // This odd behavior matches some existing browsers,
590                 // including Windows IE
591                 if ([event modifierFlags] & NSShiftKeyMask) {
592                     [self _goForward];
593                 } else {
594                     [self _goBack];
595                 }
596                 callSuper = NO;
597                 break;
598             case SpaceKey:
599                 // Checking for a control will allow events to percolate 
600                 // correctly when the focus is on a form control and we
601                 // are in full keyboard access mode.
602                 if (![self allowsScrolling] || [self _firstResponderIsControl]) {
603                     callSuper = YES;
604                     break;
605                 }
606                 if ([event modifierFlags] & NSShiftKeyMask) {
607                     [self scrollPageUp:nil];
608                 } else {
609                     [self scrollPageDown:nil];
610                 }
611                 callSuper = NO;
612                 break;
613             case NSPageUpFunctionKey:
614                 if (![self allowsScrolling]) {
615                     callSuper = YES;
616                     break;
617                 }
618                 [self scrollPageUp:nil];
619                 callSuper = NO;
620                 break;
621             case NSPageDownFunctionKey:
622                 if (![self allowsScrolling]) {
623                     callSuper = YES;
624                     break;
625                 }
626                 [self scrollPageDown:nil];
627                 callSuper = NO;
628                 break;
629             case NSHomeFunctionKey:
630                 if (![self allowsScrolling]) {
631                     callSuper = YES;
632                     break;
633                 }
634                 [self _scrollToTopLeft];
635                 callSuper = NO;
636                 break;
637             case NSEndFunctionKey:
638                 if (![self allowsScrolling]) {
639                     callSuper = YES;
640                     break;
641                 }
642                 [self _scrollToBottomLeft];
643                 callSuper = NO;
644                 break;
645             case NSUpArrowFunctionKey:
646                 // We don't handle shifted arrow keys here, so let super have a chance.
647                 if ([event modifierFlags] & NSShiftKeyMask) {
648                     callSuper = YES;
649                     break;
650                 }
651                 if (![self allowsScrolling] ||
652                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
653                     // Let arrow keys go through to pop up buttons
654                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
655                     // pop-up menu should pop the menu
656                     callSuper = YES;
657                     break;
658                 }
659                 if ([event modifierFlags] & NSCommandKeyMask) {
660                     [self _scrollToTopLeft];
661                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
662                     [self scrollPageUp:nil];
663                 } else {
664                     [self scrollLineUp:nil];
665                 }
666                 callSuper = NO;
667                 break;
668             case NSDownArrowFunctionKey:
669                 // We don't handle shifted arrow keys here, so let super have a chance.
670                 if ([event modifierFlags] & NSShiftKeyMask) {
671                     callSuper = YES;
672                     break;
673                 }
674                 if (![self allowsScrolling] ||
675                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
676                     // Let arrow keys go through to pop up buttons
677                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
678                     // pop-up menu should pop the menu
679                     callSuper = YES;
680                     break;
681                 }
682                 if ([event modifierFlags] & NSCommandKeyMask) {
683                     [self _scrollToBottomLeft];
684                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
685                     [self scrollPageDown:nil];
686                 } else {
687                     [self scrollLineDown:nil];
688                 }
689                 callSuper = NO;
690                 break;
691             case NSLeftArrowFunctionKey:
692                 // We don't handle shifted arrow keys here, so let super have a chance.
693                 if ([event modifierFlags] & NSShiftKeyMask) {
694                     callSuper = YES;
695                     break;
696                 }
697                 // Check back/forward related keys.
698                 if ([event modifierFlags] & NSCommandKeyMask) {
699                     if (!maintainsBackForwardList) {
700                         callSuper = YES;
701                         break;
702                     }
703                     [self _goBack];
704                 } else {
705                     // Now check scrolling related keys.
706                     if (![self allowsScrolling]) {
707                         callSuper = YES;
708                         break;
709                     }
710
711                     if ([event modifierFlags] & NSAlternateKeyMask) {
712                         [self _pageLeft];
713                     } else {
714                         [self _lineLeft];
715                     }
716                 }
717                 callSuper = NO;
718                 break;
719             case NSRightArrowFunctionKey:
720                 // We don't handle shifted arrow keys here, so let super have a chance.
721                 if ([event modifierFlags] & NSShiftKeyMask) {
722                     callSuper = YES;
723                     break;
724                 }
725                 // Check back/forward related keys.
726                 if ([event modifierFlags] & NSCommandKeyMask) {
727                     if (!maintainsBackForwardList) {
728                         callSuper = YES;
729                         break;
730                     }
731                     [self _goForward];
732                 } else {
733                     // Now check scrolling related keys.
734                     if (![self allowsScrolling]) {
735                         callSuper = YES;
736                         break;
737                     }
738
739                     if ([event modifierFlags] & NSAlternateKeyMask) {
740                         [self _pageRight];
741                     } else {
742                         [self _lineRight];
743                     }
744                 }
745                 callSuper = NO;
746                 break;
747         }
748     }
749     
750     if (callSuper) {
751         [super keyDown:event];
752     } else {
753         // if we did something useful, get the cursor out of the way
754         [NSCursor setHiddenUntilMouseMoves:YES];
755     }
756 }
757
758 @end