WidgetMac.mm   [plain text]


/*
 * 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"
#import "WKGraphics.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 = HardRetain(view);
    data->client = 0;
    data->visible = true;
    data->mustStayInWindow = false;
    data->removeFromSuperviewSoon = false;
}

Widget::~Widget() 
{
    BEGIN_BLOCK_OBJC_EXCEPTIONS;
    HardRelease(data->view);
    END_BLOCK_OBJC_EXCEPTIONS;

    if (deferredFirstResponder == this)
        deferredFirstResponder = 0;

    delete data;
}

void Widget::setEnabled(bool enabled)
{
}

bool Widget::isEnabled() const
{
    return true;
}

IntRect Widget::frameGeometry() const
{
    BEGIN_BLOCK_OBJC_EXCEPTIONS;
    NSView *v = getOuterView();
    if (v != nil) return enclosingIntRect([v frame]);
    END_BLOCK_OBJC_EXCEPTIONS;
    return IntRect();
}

bool Widget::hasFocus() const
{
    return false;
}

void Widget::setFocus()
{
}

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)
{
}

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();
    if (v != nil) {
        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;
    HardRetain(view);
    HardRelease(data->view);
    data->view = view;
    END_BLOCK_OBJC_EXCEPTIONS;
}

float Widget::scaleFactor() const
{
	return 1.0;
}

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;
}

// FIXME: Get rid of the single use of these next two functions (frame resizing), and remove them.

GraphicsContext* Widget::lockDrawingFocus()
{
    PlatformGraphicsContext* platformContext = static_cast<PlatformGraphicsContext*>(WKGetCurrentGraphicsContext());
    return new GraphicsContext(platformContext);
}

void Widget::unlockDrawingFocus(GraphicsContext* context)
{
    BEGIN_BLOCK_OBJC_EXCEPTIONS;
    [getView() unlockFocus];
    END_BLOCK_OBJC_EXCEPTIONS;
    delete context;
}

void Widget::disableFlushDrawing()
{
}

void Widget::enableFlushDrawing()
{
}

void Widget::paint(GraphicsContext* p, const IntRect& r)
{
    if (p->paintingDisabled())
        return;
    // 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 setNeedsDisplayInRect:[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();
    if (!subview)
        return;
    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;
}

}