Add Atom and RSS MIME types to set of supported XML types.
[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/rss+xml",
302             [WebHTMLView class], @"application/atom+xml",
303             [WebHTMLView class], @"application/x-webarchive",
304             [WebTextView class], @"text/",
305             [WebTextView class], @"application/x-javascript",
306 // Assume we'll only ever compile this on Panther or greater, so 
307 // MAC_OS_X_VERSION_10_3 is guranateed to be defined.
308 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_3
309             [WebPDFView class], @"application/pdf",
310 #endif
311             nil];
312     }
313
314     if (!addedImageTypes && !allowImageTypeOmission) {
315         NSEnumerator *enumerator = [[WebImageView supportedImageMIMETypes] objectEnumerator];
316         ASSERT(enumerator != nil);
317         NSString *mime;
318         while ((mime = [enumerator nextObject]) != nil) {
319             // Don't clobber previously-registered view classes.
320             if ([viewTypes objectForKey:mime] == nil) {
321                 [viewTypes setObject:[WebImageView class] forKey:mime];
322             }
323         }
324         addedImageTypes = YES;
325     }
326     
327     return viewTypes;
328 }
329
330 + (BOOL)_canShowMIMETypeAsHTML:(NSString *)MIMEType
331 {
332     return ([viewTypes objectForKey:MIMEType] == [WebHTMLView class]);
333 }
334
335 + (Class)_viewClassForMIMEType:(NSString *)MIMEType
336 {
337     Class viewClass;
338     return [WebView _viewClass:&viewClass andRepresentationClass:nil forMIMEType:MIMEType] ? viewClass : nil;
339 }
340
341 - (void)_goBack
342 {
343     [_private->webView goBack];
344 }
345
346 - (void)_goForward
347 {
348     [_private->webView goForward];
349 }
350
351 - (BOOL)_isMainFrame
352 {
353     return [_private->webView mainFrame] == [self webFrame];
354 }
355
356 - (void)_tile
357 {
358     NSRect scrollViewFrame = [self bounds];
359     // The border drawn by WebFrameView is 1 pixel on the left and right,
360     // two pixels on top and bottom.  Shrink the scroll view to accomodate
361     // the border.
362     if ([self _shouldDrawBorder]){
363         scrollViewFrame = NSInsetRect (scrollViewFrame, 1, 2);
364     }
365     [_private->frameScrollView setFrame:scrollViewFrame];
366 }
367
368 - (void)_drawBorder
369 {
370     if ([self _shouldDrawBorder]){
371         NSRect vRect = [self frame];
372             
373         // Left, black
374         [[NSColor blackColor] set];
375         NSRectFill(NSMakeRect(0,0,1,vRect.size.height));
376         
377         // Top, light gray, black
378         [[NSColor lightGrayColor] set];
379         NSRectFill(NSMakeRect(0,0,vRect.size.width,1));
380         [[NSColor whiteColor] set];
381         NSRectFill(NSMakeRect(1,1,vRect.size.width-2,1));
382         
383         // Right, light gray
384         [[NSColor lightGrayColor] set];
385         NSRectFill(NSMakeRect(vRect.size.width-1,1,1,vRect.size.height-2));
386         
387         // Bottom, light gray, white
388         [[NSColor blackColor] set];
389         NSRectFill(NSMakeRect(0,vRect.size.height-1,vRect.size.width,1));
390         [[NSColor lightGrayColor] set];
391         NSRectFill(NSMakeRect(1,vRect.size.height-2,vRect.size.width-2,1));
392     }
393 }
394
395 - (BOOL)_shouldDrawBorder
396 {
397     if (!_private->hasBorder)
398         return NO;
399         
400     // Only draw a border for frames that request a border and the frame does
401     // not contain a frameset.  Additionally we should (post-panther) not draw
402     // a border (left, right, top or bottom) if the frame edge abutts the window frame.
403     NSView *docV = [self documentView];
404     if ([docV isKindOfClass:[WebHTMLView class]]){
405         if ([[(WebHTMLView *)docV _bridge] isFrameSet]){
406             return NO;
407         }
408     }
409     return YES;
410 }
411
412 - (void)_setHasBorder:(BOOL)hasBorder
413 {
414     if (_private->hasBorder == hasBorder) {
415         return;
416     }
417     _private->hasBorder = hasBorder;
418     [self _tile];
419 }
420
421 - (BOOL)_firstResponderIsControl
422 {
423     return [[[self window] firstResponder] isKindOfClass:[NSControl class]];
424 }
425
426 @end
427
428 @implementation WebFrameView
429
430 - initWithFrame: (NSRect) frame
431 {
432     [super initWithFrame: frame];
433  
434     [WebViewFactory createSharedFactory];
435     [WebTextRendererFactory createSharedFactory];
436     [WebImageRendererFactory createSharedFactory];
437     [WebCookieAdapter createSharedAdapter];
438     [WebGraphicsBridge createSharedBridge];
439     [WebKeyGenerator createSharedGenerator];
440     
441     _private = [[WebFrameViewPrivate alloc] init];
442
443     WebDynamicScrollBarsView *scrollView  = [[WebDynamicScrollBarsView alloc] initWithFrame: NSMakeRect(0,0,frame.size.width,frame.size.height)];
444     _private->frameScrollView = scrollView;
445     [scrollView setContentView: [[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]];
446     [scrollView setDrawsBackground: NO];
447     [scrollView setHasVerticalScroller: NO];
448     [scrollView setHasHorizontalScroller: NO];
449     [scrollView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
450     [self addSubview: scrollView];
451     // don't call our overridden version here; we need to make the standard NSView link between us
452     // and our subview so that previousKeyView and previousValidKeyView work as expected. This works
453     // together with our becomeFirstResponder and setNextKeyView overrides.
454     [super setNextKeyView:scrollView];
455     
456     ++WebFrameViewCount;
457     
458     return self;
459 }
460
461 - (void)dealloc 
462 {
463     --WebFrameViewCount;
464     
465     [_private release];
466     _private = nil;
467     
468     [super dealloc];
469 }
470
471 - (void)finalize 
472 {
473     --WebFrameViewCount;
474
475     _private = nil;
476
477     [super finalize];
478 }
479
480 - (WebFrame *)webFrame
481 {
482     return [[self _webView] _frameForView: self]; 
483 }
484
485
486 - (void)setAllowsScrolling: (BOOL)flag
487 {
488     [(WebDynamicScrollBarsView *)[self _scrollView] setAllowsScrolling: flag];
489 }
490
491 - (BOOL)allowsScrolling
492 {
493     return [(WebDynamicScrollBarsView *)[self _scrollView] allowsScrolling];
494 }
495
496 - documentView
497 {
498     return [[self _scrollView] documentView];
499 }
500
501 - (BOOL)acceptsFirstResponder
502 {
503     // We always accept first responder; this matches OS X 10.2 WebKit
504     // behavior (see 3469791).
505     return YES;
506 }
507
508 - (BOOL)becomeFirstResponder
509 {
510     // This works together with setNextKeyView to splice the WebFrameView into
511     // the key loop similar to the way NSScrollView does this. Note that
512     // WebView has similar code.
513     
514     // If the scrollView won't accept first-responderness now, then we just become
515     // the first responder ourself like a normal view. This lets us be the first 
516     // responder in cases where no page has yet been loaded (see 3469791).
517     if ([[self _scrollView] acceptsFirstResponder]) {
518         NSWindow *window = [self window];
519         if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
520             NSView *previousValidKeyView = [self previousValidKeyView];
521             if ((previousValidKeyView != self) && (previousValidKeyView != [self _scrollView])) {
522                 [window makeFirstResponder:previousValidKeyView];
523             }
524         } else {
525             [window makeFirstResponder:[self _scrollView]];
526         }
527     }    
528     
529     return YES;
530 }
531
532 - (void)setNextKeyView:(NSView *)aView
533 {
534     // This works together with becomeFirstResponder to splice the WebFrameView into
535     // the key loop similar to the way NSScrollView does this. Note that
536     // WebView has very similar code.
537     if ([self _scrollView] != nil) {
538         [[self _scrollView] setNextKeyView:aView];
539     } else {
540         [super setNextKeyView:aView];
541     }
542 }
543
544 - (BOOL)isOpaque
545 {
546     return [[self _webView] drawsBackground];
547 }
548
549 - (void)drawRect:(NSRect)rect
550 {
551     if ([self documentView] == nil) {
552         // Need to paint ourselves if there's no documentView to do it instead.
553         [[NSColor whiteColor] set];
554         NSRectFill(rect);
555     } else {
556 #ifndef NDEBUG
557         if ([[self _scrollView] drawsBackground]) {
558             [[NSColor cyanColor] set];
559             NSRectFill(rect);
560         }
561 #endif
562     }
563     
564     [self _drawBorder];
565 }
566
567 - (void)setFrameSize:(NSSize)size
568 {
569     if (!NSEqualSizes(size, [self frame].size) && [[[self webFrame] webView] drawsBackground]) {
570         [[self _scrollView] setDrawsBackground:YES];
571     }
572     [super setFrameSize:size];
573     [self _tile];
574 }
575
576 - (void)keyDown:(NSEvent *)event
577 {
578     NSString *characters = [event characters];
579     int index, count;
580     BOOL callSuper = YES;
581     BOOL maintainsBackForwardList = [[[self webFrame] webView] backForwardList] == nil ? NO : YES;
582
583     count = [characters length];
584     for (index = 0; index < count; ++index) {
585         switch ([characters characterAtIndex:index]) {
586             case NSDeleteCharacter:
587                 if (!maintainsBackForwardList) {
588                     callSuper = YES;
589                     break;
590                 }
591                 // This odd behavior matches some existing browsers,
592                 // including Windows IE
593                 if ([event modifierFlags] & NSShiftKeyMask) {
594                     [self _goForward];
595                 } else {
596                     [self _goBack];
597                 }
598                 callSuper = NO;
599                 break;
600             case SpaceKey:
601                 // Checking for a control will allow events to percolate 
602                 // correctly when the focus is on a form control and we
603                 // are in full keyboard access mode.
604                 if (![self allowsScrolling] || [self _firstResponderIsControl]) {
605                     callSuper = YES;
606                     break;
607                 }
608                 if ([event modifierFlags] & NSShiftKeyMask) {
609                     [self scrollPageUp:nil];
610                 } else {
611                     [self scrollPageDown:nil];
612                 }
613                 callSuper = NO;
614                 break;
615             case NSPageUpFunctionKey:
616                 if (![self allowsScrolling]) {
617                     callSuper = YES;
618                     break;
619                 }
620                 [self scrollPageUp:nil];
621                 callSuper = NO;
622                 break;
623             case NSPageDownFunctionKey:
624                 if (![self allowsScrolling]) {
625                     callSuper = YES;
626                     break;
627                 }
628                 [self scrollPageDown:nil];
629                 callSuper = NO;
630                 break;
631             case NSHomeFunctionKey:
632                 if (![self allowsScrolling]) {
633                     callSuper = YES;
634                     break;
635                 }
636                 [self _scrollToTopLeft];
637                 callSuper = NO;
638                 break;
639             case NSEndFunctionKey:
640                 if (![self allowsScrolling]) {
641                     callSuper = YES;
642                     break;
643                 }
644                 [self _scrollToBottomLeft];
645                 callSuper = NO;
646                 break;
647             case NSUpArrowFunctionKey:
648                 // We don't handle shifted arrow keys here, so let super have a chance.
649                 if ([event modifierFlags] & NSShiftKeyMask) {
650                     callSuper = YES;
651                     break;
652                 }
653                 if (![self allowsScrolling] ||
654                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
655                     // Let arrow keys go through to pop up buttons
656                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
657                     // pop-up menu should pop the menu
658                     callSuper = YES;
659                     break;
660                 }
661                 if ([event modifierFlags] & NSCommandKeyMask) {
662                     [self _scrollToTopLeft];
663                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
664                     [self scrollPageUp:nil];
665                 } else {
666                     [self scrollLineUp:nil];
667                 }
668                 callSuper = NO;
669                 break;
670             case NSDownArrowFunctionKey:
671                 // We don't handle shifted arrow keys here, so let super have a chance.
672                 if ([event modifierFlags] & NSShiftKeyMask) {
673                     callSuper = YES;
674                     break;
675                 }
676                 if (![self allowsScrolling] ||
677                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
678                     // Let arrow keys go through to pop up buttons
679                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
680                     // pop-up menu should pop the menu
681                     callSuper = YES;
682                     break;
683                 }
684                 if ([event modifierFlags] & NSCommandKeyMask) {
685                     [self _scrollToBottomLeft];
686                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
687                     [self scrollPageDown:nil];
688                 } else {
689                     [self scrollLineDown:nil];
690                 }
691                 callSuper = NO;
692                 break;
693             case NSLeftArrowFunctionKey:
694                 // We don't handle shifted arrow keys here, so let super have a chance.
695                 if ([event modifierFlags] & NSShiftKeyMask) {
696                     callSuper = YES;
697                     break;
698                 }
699                 // Check back/forward related keys.
700                 if ([event modifierFlags] & NSCommandKeyMask) {
701                     if (!maintainsBackForwardList) {
702                         callSuper = YES;
703                         break;
704                     }
705                     [self _goBack];
706                 } else {
707                     // Now check scrolling related keys.
708                     if (![self allowsScrolling]) {
709                         callSuper = YES;
710                         break;
711                     }
712
713                     if ([event modifierFlags] & NSAlternateKeyMask) {
714                         [self _pageLeft];
715                     } else {
716                         [self _lineLeft];
717                     }
718                 }
719                 callSuper = NO;
720                 break;
721             case NSRightArrowFunctionKey:
722                 // We don't handle shifted arrow keys here, so let super have a chance.
723                 if ([event modifierFlags] & NSShiftKeyMask) {
724                     callSuper = YES;
725                     break;
726                 }
727                 // Check back/forward related keys.
728                 if ([event modifierFlags] & NSCommandKeyMask) {
729                     if (!maintainsBackForwardList) {
730                         callSuper = YES;
731                         break;
732                     }
733                     [self _goForward];
734                 } else {
735                     // Now check scrolling related keys.
736                     if (![self allowsScrolling]) {
737                         callSuper = YES;
738                         break;
739                     }
740
741                     if ([event modifierFlags] & NSAlternateKeyMask) {
742                         [self _pageRight];
743                     } else {
744                         [self _lineRight];
745                     }
746                 }
747                 callSuper = NO;
748                 break;
749         }
750     }
751     
752     if (callSuper) {
753         [super keyDown:event];
754     } else {
755         // if we did something useful, get the cursor out of the way
756         [NSCursor setHiddenUntilMouseMoves:YES];
757     }
758 }
759
760 @end