/* | |
* Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. | |
* | |
* 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. | |
* | |
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR | |
* 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 "config.h" | |
#import "Widget.h" | |
#ifdef BUILDING_ON_TIGER | |
#import "AutodrainedPool.h" | |
#endif | |
#import "BlockExceptions.h" | |
#import "Chrome.h" | |
#import "Cursor.h" | |
#import "Document.h" | |
#import "Font.h" | |
#import "FoundationExtras.h" | |
#import "Frame.h" | |
#import "GraphicsContext.h" | |
#import "NotImplemented.h" | |
#import "Page.h" | |
#import "PlatformMouseEvent.h" | |
#import "ScrollView.h" | |
#import "WebCoreFrameView.h" | |
#import "WebCoreView.h" | |
#import <wtf/RetainPtr.h> | |
@interface NSWindow (WebWindowDetails) | |
- (BOOL)_needsToResetDragMargins; | |
- (void)_setNeedsToResetDragMargins:(BOOL)needs; | |
@end | |
@interface NSView (WebSetSelectedMethods) | |
- (void)setIsSelected:(BOOL)isSelected; | |
- (void)webPlugInSetIsSelected:(BOOL)isSelected; | |
@end | |
@interface NSView (Widget) | |
- (void)visibleRectDidChange; | |
@end | |
namespace WebCore { | |
class WidgetPrivate { | |
public: | |
WidgetPrivate() | |
: previousVisibleRect(NSZeroRect) | |
{ | |
} | |
bool mustStayInWindow; | |
bool removeFromSuperviewSoon; | |
NSRect previousVisibleRect; | |
}; | |
static void safeRemoveFromSuperview(NSView *view) | |
{ | |
// If the the view is the first responder, then set the window's first responder to nil so | |
// we don't leave the window pointing to a view that's no longer in it. | |
NSWindow *window = [view window]; | |
NSResponder *firstResponder = [window firstResponder]; | |
if ([firstResponder isKindOfClass:[NSView class]] && [(NSView *)firstResponder isDescendantOf:view]) | |
[window makeFirstResponder:nil]; | |
// Suppress the resetting of drag margins since we know we can't affect them. | |
BOOL resetDragMargins = [window _needsToResetDragMargins]; | |
[window _setNeedsToResetDragMargins:NO]; | |
[view removeFromSuperview]; | |
[window _setNeedsToResetDragMargins:resetDragMargins]; | |
} | |
Widget::Widget(NSView *view) | |
: m_data(new WidgetPrivate) | |
{ | |
init(view); | |
m_data->mustStayInWindow = false; | |
m_data->removeFromSuperviewSoon = false; | |
} | |
Widget::~Widget() | |
{ | |
releasePlatformWidget(); | |
delete m_data; | |
} | |
// FIXME: Should move this to Chrome; bad layering that this knows about Frame. | |
void Widget::setFocus() | |
{ | |
Frame* frame = Frame::frameForWidget(this); | |
if (!frame) | |
return; | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
NSView *view = [platformWidget() _webcore_effectiveFirstResponder]; | |
if (Page* page = frame->page()) | |
page->chrome()->focusNSView(view); | |
END_BLOCK_OBJC_EXCEPTIONS; | |
} | |
void Widget::setCursor(const Cursor& cursor) | |
{ | |
if ([NSCursor currentCursor] == cursor.impl()) | |
return; | |
[cursor.impl() set]; | |
} | |
void Widget::show() | |
{ | |
if (isSelfVisible()) | |
return; | |
setSelfVisible(true); | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
[getOuterView() setHidden:NO]; | |
END_BLOCK_OBJC_EXCEPTIONS; | |
} | |
void Widget::hide() | |
{ | |
if (!isSelfVisible()) | |
return; | |
setSelfVisible(false); | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
[getOuterView() setHidden:YES]; | |
END_BLOCK_OBJC_EXCEPTIONS; | |
} | |
IntRect Widget::frameRect() const | |
{ | |
if (!platformWidget()) | |
return m_frame; | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
return enclosingIntRect([getOuterView() frame]); | |
END_BLOCK_OBJC_EXCEPTIONS; | |
return m_frame; | |
} | |
void Widget::setFrameRect(const IntRect& rect) | |
{ | |
m_frame = rect; | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
NSView *v = getOuterView(); | |
if (!v) | |
return; | |
NSRect visibleRect = [v visibleRect]; | |
NSRect f = rect; | |
if (!NSEqualRects(f, [v frame])) { | |
[v setFrame:f]; | |
[v setNeedsDisplay:NO]; | |
} else if (!NSEqualRects(visibleRect, m_data->previousVisibleRect) && [v respondsToSelector:@selector(visibleRectDidChange)]) | |
[v visibleRectDidChange]; | |
m_data->previousVisibleRect = visibleRect; | |
END_BLOCK_OBJC_EXCEPTIONS; | |
} | |
NSView* Widget::getOuterView() const | |
{ | |
NSView* view = platformWidget(); | |
// If this widget's view is a WebCoreFrameScrollView then we | |
// resize its containing view, a WebFrameView. | |
if ([view conformsToProtocol:@protocol(WebCoreFrameScrollView)]) { | |
view = [view superview]; | |
ASSERT(view); | |
} | |
return view; | |
} | |
void Widget::paint(GraphicsContext* p, const IntRect& r) | |
{ | |
if (p->paintingDisabled()) | |
return; | |
NSView *view = getOuterView(); | |
NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; | |
if (currentContext == [[view window] graphicsContext] || ![currentContext isDrawingToScreen]) { | |
// This is the common case of drawing into a window or printing. | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
[view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]]]; | |
END_BLOCK_OBJC_EXCEPTIONS; | |
} else { | |
// This is the case of drawing into a bitmap context other than a window backing store. It gets hit beneath | |
// -cacheDisplayInRect:toBitmapImageRep:, and when painting into compositing layers. | |
// Transparent subframes are in fact implemented with scroll views that return YES from -drawsBackground (whenever the WebView | |
// itself is in drawsBackground mode). In the normal drawing code path, the scroll views are never asked to draw the background, | |
// so this is not an issue, but in this code path they are, so the following code temporarily turns background drwaing off. | |
NSView *innerView = platformWidget(); | |
NSScrollView *scrollView = 0; | |
if ([innerView conformsToProtocol:@protocol(WebCoreFrameScrollView)]) { | |
ASSERT([innerView isKindOfClass:[NSScrollView class]]); | |
NSScrollView *scrollView = static_cast<NSScrollView *>(innerView); | |
// -copiesOnScroll will return NO whenever the content view is not fully opaque. | |
if ([scrollView drawsBackground] && ![[scrollView contentView] copiesOnScroll]) | |
[scrollView setDrawsBackground:NO]; | |
else | |
scrollView = 0; | |
} | |
CGContextRef cgContext = p->platformContext(); | |
ASSERT(cgContext == [currentContext graphicsPort]); | |
CGContextSaveGState(cgContext); | |
NSRect viewFrame = [view frame]; | |
NSRect viewBounds = [view bounds]; | |
// Set up the translation and (flipped) orientation of the graphics context. In normal drawing, AppKit does it as it descends down | |
// the view hierarchy. | |
CGContextTranslateCTM(cgContext, viewFrame.origin.x - viewBounds.origin.x, viewFrame.origin.y + viewFrame.size.height + viewBounds.origin.y); | |
CGContextScaleCTM(cgContext, 1, -1); | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
{ | |
#ifdef BUILDING_ON_TIGER | |
AutodrainedPool pool; | |
#endif | |
NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]; | |
[view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]] inContext:nsContext]; | |
} | |
END_BLOCK_OBJC_EXCEPTIONS; | |
CGContextRestoreGState(cgContext); | |
if (scrollView) | |
[scrollView setDrawsBackground:YES]; | |
} | |
} | |
void Widget::setIsSelected(bool isSelected) | |
{ | |
NSView *view = platformWidget(); | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
if ([view respondsToSelector:@selector(webPlugInSetIsSelected:)]) | |
[view webPlugInSetIsSelected:isSelected]; | |
else if ([view respondsToSelector:@selector(setIsSelected:)]) | |
[view setIsSelected:isSelected]; | |
END_BLOCK_OBJC_EXCEPTIONS; | |
} | |
void Widget::removeFromSuperview() | |
{ | |
if (m_data->mustStayInWindow) | |
m_data->removeFromSuperviewSoon = true; | |
else { | |
m_data->removeFromSuperviewSoon = false; | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
safeRemoveFromSuperview(getOuterView()); | |
END_BLOCK_OBJC_EXCEPTIONS; | |
} | |
} | |
void Widget::beforeMouseDown(NSView *unusedView, Widget* widget) | |
{ | |
if (widget) { | |
ASSERT_UNUSED(unusedView, unusedView == widget->getOuterView()); | |
ASSERT(!widget->m_data->mustStayInWindow); | |
widget->m_data->mustStayInWindow = true; | |
} | |
} | |
void Widget::afterMouseDown(NSView *view, Widget* widget) | |
{ | |
if (!widget) { | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
safeRemoveFromSuperview(view); | |
END_BLOCK_OBJC_EXCEPTIONS; | |
} else { | |
ASSERT(widget->m_data->mustStayInWindow); | |
widget->m_data->mustStayInWindow = false; | |
if (widget->m_data->removeFromSuperviewSoon) | |
widget->removeFromSuperview(); | |
} | |
} | |
// These are here to deal with flipped coords on Mac. | |
IntRect Widget::convertFromRootToContainingWindow(const Widget* rootWidget, const IntRect& rect) | |
{ | |
if (!rootWidget->platformWidget()) | |
return rect; | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
return enclosingIntRect([rootWidget->platformWidget() convertRect:rect toView:nil]); | |
END_BLOCK_OBJC_EXCEPTIONS; | |
return rect; | |
} | |
IntRect Widget::convertFromContainingWindowToRoot(const Widget* rootWidget, const IntRect& rect) | |
{ | |
if (!rootWidget->platformWidget()) | |
return rect; | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
return enclosingIntRect([rootWidget->platformWidget() convertRect:rect fromView:nil]); | |
END_BLOCK_OBJC_EXCEPTIONS; | |
return rect; | |
} | |
IntPoint Widget::convertFromRootToContainingWindow(const Widget* rootWidget, const IntPoint& point) | |
{ | |
if (!rootWidget->platformWidget()) | |
return point; | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
return IntPoint([rootWidget->platformWidget() convertPoint:point toView:nil]); | |
END_BLOCK_OBJC_EXCEPTIONS; | |
return point; | |
} | |
IntPoint Widget::convertFromContainingWindowToRoot(const Widget* rootWidget, const IntPoint& point) | |
{ | |
if (!rootWidget->platformWidget()) | |
return point; | |
BEGIN_BLOCK_OBJC_EXCEPTIONS; | |
return IntPoint([rootWidget->platformWidget() convertPoint:point fromView:nil]); | |
END_BLOCK_OBJC_EXCEPTIONS; | |
return point; | |
} | |
void Widget::releasePlatformWidget() | |
{ | |
HardRelease(m_widget); | |
m_data->previousVisibleRect = NSZeroRect; | |
} | |
void Widget::retainPlatformWidget() | |
{ | |
HardRetain(m_widget); | |
} | |
} // namespace WebCore | |