| /* |
| * Copyright (C) 2004, 2005, 2006 Apple Computer, 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" |
| |
| #import "Cursor.h" |
| #import "Font.h" |
| #import "FoundationExtras.h" |
| #import "GraphicsContext.h" |
| #import "BlockExceptions.h" |
| #import "FrameMac.h" |
| #import "WebCoreFrameBridge.h" |
| #import "WebCoreFrameView.h" |
| #import "WebCoreView.h" |
| #import "WebCoreWidgetHolder.h" |
| #import "WidgetClient.h" |
| |
| namespace WebCore { |
| |
| static bool deferFirstResponderChanges; |
| static Widget *deferredFirstResponder; |
| |
| class WidgetPrivate |
| { |
| public: |
| Font font; |
| NSView* view; |
| WidgetClient* client; |
| bool visible; |
| bool mustStayInWindow; |
| bool removeFromSuperviewSoon; |
| }; |
| |
| Widget::Widget() : data(new WidgetPrivate) |
| { |
| data->view = nil; |
| data->client = 0; |
| data->visible = true; |
| data->mustStayInWindow = false; |
| data->removeFromSuperviewSoon = false; |
| } |
| |
| Widget::Widget(NSView* view) : data(new WidgetPrivate) |
| { |
| data->view = KWQRetain(view); |
| data->client = 0; |
| data->visible = true; |
| data->mustStayInWindow = false; |
| data->removeFromSuperviewSoon = false; |
| } |
| |
| Widget::~Widget() |
| { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| KWQRelease(data->view); |
| END_BLOCK_OBJC_EXCEPTIONS; |
| |
| if (deferredFirstResponder == this) |
| deferredFirstResponder = 0; |
| |
| delete data; |
| } |
| |
| void Widget::setActiveWindow() |
| { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| [FrameMac::bridgeForWidget(this) focusWindow]; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::setEnabled(bool enabled) |
| { |
| id view = data->view; |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| if ([view respondsToSelector:@selector(setEnabled:)]) { |
| [view setEnabled:enabled]; |
| } |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| bool Widget::isEnabled() const |
| { |
| id view = data->view; |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| if ([view respondsToSelector:@selector(isEnabled)]) { |
| return [view isEnabled]; |
| } |
| END_BLOCK_OBJC_EXCEPTIONS; |
| |
| return true; |
| } |
| |
| IntRect Widget::frameGeometry() const |
| { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| return enclosingIntRect([getOuterView() frame]); |
| END_BLOCK_OBJC_EXCEPTIONS; |
| return IntRect(); |
| } |
| |
| bool Widget::hasFocus() const |
| { |
| if (deferFirstResponderChanges && deferredFirstResponder) { |
| return this == deferredFirstResponder; |
| } |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| |
| NSView *view = [getView() _webcore_effectiveFirstResponder]; |
| |
| id firstResponder = [FrameMac::bridgeForWidget(this) firstResponder]; |
| |
| if (!firstResponder) { |
| return false; |
| } |
| if (firstResponder == view) { |
| return true; |
| } |
| |
| // Some widgets, like text fields, secure text fields, text areas, and selects |
| // (when displayed using a list box) may have a descendent widget that is |
| // first responder. This checksDescendantsForFocus() check, turned on for the |
| // four widget types listed, enables the additional check which makes this |
| // function work correctly for the above-mentioned widget types. |
| if (checksDescendantsForFocus() && |
| [firstResponder isKindOfClass:[NSView class]] && |
| [(NSView *)firstResponder isDescendantOf:view]) { |
| // Return true when the first responder is a subview of this widget's view |
| return true; |
| } |
| |
| END_BLOCK_OBJC_EXCEPTIONS; |
| |
| return false; |
| } |
| |
| void Widget::setFocus() |
| { |
| if (hasFocus()) |
| return; |
| |
| if (deferFirstResponderChanges) { |
| deferredFirstResponder = this; |
| return; |
| } |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| NSView *view = [getView() _webcore_effectiveFirstResponder]; |
| if ([view superview] && [view acceptsFirstResponder]) { |
| WebCoreFrameBridge *bridge = FrameMac::bridgeForWidget(this); |
| NSResponder *oldFirstResponder = [bridge firstResponder]; |
| |
| [bridge makeFirstResponder:view]; |
| |
| // Setting focus can actually cause a style change which might |
| // remove the view from its superview while it's being made |
| // first responder. This confuses AppKit so we must restore |
| // the old first responder. |
| |
| if (![view superview]) |
| [bridge makeFirstResponder:oldFirstResponder]; |
| } |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::clearFocus() |
| { |
| if (!hasFocus()) |
| return; |
| FrameMac::clearDocumentFocus(this); |
| } |
| |
| Widget::FocusPolicy Widget::focusPolicy() const |
| { |
| // This provides support for controlling the widgets that take |
| // part in tab navigation. Widgets must: |
| // 1. not be hidden by css |
| // 2. be enabled |
| // 3. accept first responder |
| |
| if (!client()) |
| return NoFocus; |
| if (!client()->isVisible(const_cast<Widget*>(this))) |
| return NoFocus; |
| if (!isEnabled()) |
| return NoFocus; |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| if (![getView() acceptsFirstResponder]) |
| return NoFocus; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| |
| return TabFocus; |
| } |
| |
| const Font& Widget::font() const |
| { |
| return data->font; |
| } |
| |
| void Widget::setFont(const Font& font) |
| { |
| data->font = font; |
| } |
| |
| void Widget::setCursor(const Cursor& cursor) |
| { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| for (id view = data->view; view; view = [view superview]) { |
| if ([view respondsToSelector:@selector(setDocumentCursor:)]) { |
| if ([view respondsToSelector:@selector(documentCursor)] && cursor.impl() == [view documentCursor]) |
| break; |
| [view setDocumentCursor:cursor.impl()]; |
| break; |
| } |
| } |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::show() |
| { |
| if (!data || data->visible) |
| return; |
| |
| data->visible = true; |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| [getOuterView() setHidden: NO]; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::hide() |
| { |
| if (!data || !data->visible) |
| return; |
| |
| data->visible = false; |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| [getOuterView() setHidden: YES]; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::setFrameGeometry(const IntRect &rect) |
| { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| NSView *v = getOuterView(); |
| NSRect f = rect; |
| if (!NSEqualRects(f, [v frame])) { |
| [v setFrame:f]; |
| [v setNeedsDisplay: NO]; |
| } |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| IntPoint Widget::mapFromGlobal(const IntPoint &p) const |
| { |
| NSPoint bp = {0,0}; |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| bp = [[FrameMac::bridgeForWidget(this) window] convertScreenToBase:[data->view convertPoint:p toView:nil]]; |
| return IntPoint(bp); |
| END_BLOCK_OBJC_EXCEPTIONS; |
| return IntPoint(); |
| } |
| |
| NSView* Widget::getView() const |
| { |
| return data->view; |
| } |
| |
| void Widget::setView(NSView* view) |
| { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| KWQRetain(view); |
| KWQRelease(data->view); |
| data->view = view; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| NSView* Widget::getOuterView() const |
| { |
| // If this widget's view is a WebCoreFrameView the we resize its containing view, a WebFrameView. |
| |
| NSView* view = data->view; |
| if ([view conformsToProtocol:@protocol(WebCoreFrameView)]) { |
| view = [view superview]; |
| ASSERT(view); |
| } |
| |
| return view; |
| } |
| |
| void Widget::lockDrawingFocus() |
| { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| [getView() lockFocus]; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::unlockDrawingFocus() |
| { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| [getView() unlockFocus]; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::disableFlushDrawing() |
| { |
| // It's OK to use the real window here, because if the view's not |
| // in the view hierarchy, then we don't actually want to affect |
| // flushing. |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| [[getView() window] disableFlushWindow]; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::enableFlushDrawing() |
| { |
| // It's OK to use the real window here, because if the view's not |
| // in the view hierarchy, then we don't actually want to affect |
| // flushing. |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| NSWindow* window = [getView() window]; |
| [window enableFlushWindow]; |
| [window flushWindow]; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::setDrawingAlpha(float alpha) |
| { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| CGContextSetAlpha((CGContextRef)[[NSGraphicsContext currentContext] graphicsPort], alpha); |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::paint(GraphicsContext* p, const IntRect& r) |
| { |
| if (p->paintingDisabled()) |
| return; |
| NSView *view = getOuterView(); |
| // WebCoreTextArea and WebCoreTextField both rely on the fact that we use this particular |
| // NSView display method. If you change this, be sure to update them as well. |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| [view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]]]; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::sendConsumedMouseUp() |
| { |
| if (client()) |
| client()->sendConsumedMouseUp(this); |
| } |
| |
| void Widget::setIsSelected(bool isSelected) |
| { |
| [FrameMac::bridgeForWidget(this) setIsSelected:isSelected forView:getView()]; |
| } |
| |
| void Widget::addToSuperview(NSView *superview) |
| { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| |
| ASSERT(superview); |
| NSView *subview = getOuterView(); |
| ASSERT(![superview isDescendantOf:subview]); |
| if ([subview superview] != superview) |
| [superview addSubview:subview]; |
| data->removeFromSuperviewSoon = false; |
| |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| |
| void Widget::removeFromSuperview() |
| { |
| if (data->mustStayInWindow) |
| data->removeFromSuperviewSoon = true; |
| else { |
| data->removeFromSuperviewSoon = false; |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| [getOuterView() removeFromSuperview]; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } |
| } |
| |
| void Widget::beforeMouseDown(NSView *view) |
| { |
| ASSERT([view conformsToProtocol:@protocol(WebCoreWidgetHolder)]); |
| Widget* widget = [(NSView <WebCoreWidgetHolder> *)view widget]; |
| if (widget) { |
| ASSERT(view == widget->getOuterView()); |
| ASSERT(!widget->data->mustStayInWindow); |
| widget->data->mustStayInWindow = true; |
| } |
| } |
| |
| void Widget::afterMouseDown(NSView *view) |
| { |
| ASSERT([view conformsToProtocol:@protocol(WebCoreWidgetHolder)]); |
| Widget* widget = [(NSView <WebCoreWidgetHolder>*)view widget]; |
| if (!widget) { |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| [view removeFromSuperview]; |
| END_BLOCK_OBJC_EXCEPTIONS; |
| } else { |
| ASSERT(widget->data->mustStayInWindow); |
| widget->data->mustStayInWindow = false; |
| if (widget->data->removeFromSuperviewSoon) |
| widget->removeFromSuperview(); |
| } |
| } |
| |
| void Widget::setDeferFirstResponderChanges(bool defer) |
| { |
| deferFirstResponderChanges = defer; |
| if (!defer) { |
| Widget* r = deferredFirstResponder; |
| deferredFirstResponder = 0; |
| if (r) { |
| r->setFocus(); |
| } |
| } |
| } |
| |
| void Widget::setClient(WidgetClient* c) |
| { |
| data->client = c; |
| } |
| |
| WidgetClient* Widget::client() const |
| { |
| return data->client; |
| } |
| |
| } |