blob: a185667f8f1b455850876c5e342353ecf91644e3 [file] [log] [blame]
/*
* Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2006 David Smith (catfish.man@gmail.com)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "WebViewInternal.h"
#import "WebFrameInternal.h"
#import "WebHTMLView.h"
#import "WebTextCompletionController.h"
#import "WebViewData.h"
#import <WebCore/Frame.h>
using namespace WebCore;
@class NSTextInputContext;
@interface NSResponder (WebNSResponderDetails)
- (NSTextInputContext *)inputContext;
@end
#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
@interface NSObject (NSTextInputContextDetails)
- (BOOL)wantsToHandleMouseEvents;
- (BOOL)handleMouseEvent:(NSEvent *)event;
@end
#endif
@implementation WebView (WebViewEventHandling)
static WebView *lastMouseoverView;
- (void)_closingEventHandling
{
if (lastMouseoverView == self)
lastMouseoverView = nil;
}
- (void)_setMouseDownEvent:(NSEvent *)event
{
ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
if (event == _private->mouseDownEvent)
return;
[event retain];
[_private->mouseDownEvent release];
_private->mouseDownEvent = event;
}
- (void)mouseDown:(NSEvent *)event
{
// FIXME (Viewless): This method should be shared with WebHTMLView, which needs to
// do the same work in the usesDocumentViews case. We don't want to maintain two
// duplicate copies of this method.
if (_private->usesDocumentViews) {
[super mouseDown:event];
return;
}
// There's a chance that responding to this event will run a nested event loop, and
// fetching a new event might release the old one. Retaining and then autoreleasing
// the current event prevents that from causing a problem inside WebKit or AppKit code.
[[event retain] autorelease];
RetainPtr<WebView> protector = self;
if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event])
return;
_private->handlingMouseDownEvent = YES;
// Record the mouse down position so we can determine drag hysteresis.
[self _setMouseDownEvent:event];
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
goto done;
[_private->completionController endRevertingChange:NO moveLeft:NO];
// If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
// We don't want to pass them along to KHTML a second time.
if (!([event modifierFlags] & NSControlKeyMask)) {
_private->ignoringMouseDraggedEvents = NO;
// Don't do any mouseover while the mouse is down.
[self _cancelUpdateMouseoverTimer];
// Let WebCore get a chance to deal with the event. This will call back to us
// to start the autoscroll timer if appropriate.
if (Frame* frame = [self _mainCoreFrame])
frame->eventHandler()->mouseDown(event);
}
done:
_private->handlingMouseDownEvent = NO;
}
- (void)mouseUp:(NSEvent *)event
{
// FIXME (Viewless): This method should be shared with WebHTMLView, which needs to
// do the same work in the usesDocumentViews case. We don't want to maintain two
// duplicate copies of this method.
if (_private->usesDocumentViews) {
[super mouseUp:event];
return;
}
// There's a chance that responding to this event will run a nested event loop, and
// fetching a new event might release the old one. Retaining and then autoreleasing
// the current event prevents that from causing a problem inside WebKit or AppKit code.
[[event retain] autorelease];
[self _setMouseDownEvent:nil];
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
return;
[self retain];
[self _stopAutoscrollTimer];
if (Frame* frame = [self _mainCoreFrame])
frame->eventHandler()->mouseUp(event);
[self _updateMouseoverWithFakeEvent];
[self release];
}
+ (void)_updateMouseoverWithEvent:(NSEvent *)event
{
WebView *oldView = lastMouseoverView;
lastMouseoverView = nil;
NSView *contentView = [[event window] contentView];
NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
for (NSView *hitView = [contentView hitTest:locationForHitTest]; hitView; hitView = [hitView superview]) {
if ([hitView isKindOfClass:[WebView class]]) {
lastMouseoverView = static_cast<WebView *>(hitView);
break;
}
}
if (lastMouseoverView && lastMouseoverView->_private->hoverFeedbackSuspended)
lastMouseoverView = nil;
if (lastMouseoverView != oldView) {
if (Frame* oldCoreFrame = [oldView _mainCoreFrame]) {
NSEvent *oldViewEvent = [NSEvent mouseEventWithType:NSMouseMoved
location:NSMakePoint(-1, -1)
modifierFlags:[[NSApp currentEvent] modifierFlags]
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:[[oldView window] windowNumber]
context:[[NSApp currentEvent] context]
eventNumber:0 clickCount:0 pressure:0];
oldCoreFrame->eventHandler()->mouseMoved(oldViewEvent);
}
}
if (!lastMouseoverView)
return;
if (Frame* coreFrame = core([lastMouseoverView mainFrame]))
coreFrame->eventHandler()->mouseMoved(event);
}
- (void)_updateMouseoverWithFakeEvent
{
[self _cancelUpdateMouseoverTimer];
NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
modifierFlags:[[NSApp currentEvent] modifierFlags]
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:[[self window] windowNumber]
context:[[NSApp currentEvent] context]
eventNumber:0 clickCount:0 pressure:0];
[[self class] _updateMouseoverWithEvent:fakeEvent];
}
- (void)_cancelUpdateMouseoverTimer
{
if (_private->updateMouseoverTimer) {
CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
CFRelease(_private->updateMouseoverTimer);
_private->updateMouseoverTimer = NULL;
}
}
- (void)_stopAutoscrollTimer
{
NSTimer *timer = _private->autoscrollTimer;
_private->autoscrollTimer = nil;
[_private->autoscrollTriggerEvent release];
_private->autoscrollTriggerEvent = nil;
[timer invalidate];
[timer release];
}
- (void)_setToolTip:(NSString *)toolTip
{
if (_private->usesDocumentViews) {
id documentView = [[[self _selectedOrMainFrame] frameView] documentView];
if ([documentView isKindOfClass:[WebHTMLView class]])
[documentView _setToolTip:toolTip];
return;
}
// FIXME (Viewless): Code to handle tooltips needs to move into WebView.
}
@end