qdeclarativewebview.cpp [plain text]
#include "qdeclarativewebview_p.h"
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <QtCore/QDebug>
#include <QtCore/QEvent>
#include <QtCore/QFile>
#include <QtDeclarative/QDeclarativeContext>
#include <QtDeclarative/QDeclarativeEngine>
#include <QtDeclarative/qdeclarative.h>
#include <QtGui/QKeyEvent>
#include <QtGui/QMouseEvent>
#include <QtGui/QPen>
#include "qwebelement.h"
#include "qwebframe.h"
#include "qwebpage.h"
#include "qwebsettings.h"
QT_BEGIN_NAMESPACE
class QDeclarativeWebViewPrivate {
public:
QDeclarativeWebViewPrivate(QDeclarativeWebView* qq)
: q(qq)
, preferredwidth(0)
, preferredheight(0)
, progress(1.0)
, status(QDeclarativeWebView::Null)
, pending(PendingNone)
, newWindowComponent(0)
, newWindowParent(0)
, rendering(true)
{
}
QDeclarativeWebView* q;
QUrl url; GraphicsWebView* view;
int preferredwidth, preferredheight;
qreal progress;
QDeclarativeWebView::Status status;
QString statusText;
enum { PendingNone, PendingUrl, PendingHtml, PendingContent } pending;
QUrl pendingUrl;
QString pendingString;
QByteArray pendingData;
mutable QDeclarativeWebSettings settings;
QDeclarativeComponent* newWindowComponent;
QDeclarativeItem* newWindowParent;
static void windowObjectsAppend(QDeclarativeListProperty<QObject>* prop, QObject* o)
{
static_cast<QDeclarativeWebViewPrivate*>(prop->data)->windowObjects.append(o);
static_cast<QDeclarativeWebViewPrivate*>(prop->data)->updateWindowObjects();
}
void updateWindowObjects();
QObjectList windowObjects;
bool rendering;
};
GraphicsWebView::GraphicsWebView(QDeclarativeWebView* parent)
: QGraphicsWebView(parent)
, parent(parent)
, pressTime(400)
{
}
void GraphicsWebView::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
pressPoint = event->pos();
if (pressTime) {
pressTimer.start(pressTime, this);
parent->setKeepMouseGrab(false);
} else {
grabMouse();
parent->setKeepMouseGrab(true);
}
QGraphicsWebView::mousePressEvent(event);
QWebHitTestResult hit = page()->mainFrame()->hitTestContent(pressPoint.toPoint());
if (hit.isContentEditable())
parent->forceActiveFocus();
setFocus();
}
void GraphicsWebView::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
QGraphicsWebView::mouseReleaseEvent(event);
pressTimer.stop();
parent->setKeepMouseGrab(false);
ungrabMouse();
}
void GraphicsWebView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
{
QMouseEvent* me = new QMouseEvent(QEvent::MouseButtonDblClick, (event->pos() / parent->contentsScale()).toPoint(), event->button(), event->buttons(), 0);
emit doubleClick(event->pos().x(), event->pos().y());
delete me;
}
void GraphicsWebView::timerEvent(QTimerEvent* event)
{
if (event->timerId() == pressTimer.timerId()) {
pressTimer.stop();
grabMouse();
parent->setKeepMouseGrab(true);
}
}
void GraphicsWebView::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
if (pressTimer.isActive()) {
if ((event->pos() - pressPoint).manhattanLength() > QApplication::startDragDistance())
pressTimer.stop();
}
if (parent->keepMouseGrab())
QGraphicsWebView::mouseMoveEvent(event);
}
bool GraphicsWebView::sceneEvent(QEvent *event)
{
bool rv = QGraphicsWebView::sceneEvent(event);
if (event->type() == QEvent::UngrabMouse) {
pressTimer.stop();
parent->setKeepMouseGrab(false);
}
return rv;
}
QDeclarativeWebView::QDeclarativeWebView(QDeclarativeItem *parent) : QDeclarativeItem(parent)
{
init();
}
QDeclarativeWebView::~QDeclarativeWebView()
{
delete d;
}
void QDeclarativeWebView::init()
{
d = new QDeclarativeWebViewPrivate(this);
if (QWebSettings::iconDatabasePath().isNull() &&
QWebSettings::globalSettings()->localStoragePath().isNull() &&
QWebSettings::offlineStoragePath().isNull() &&
QWebSettings::offlineWebApplicationCachePath().isNull())
QWebSettings::enablePersistentStorage();
setAcceptedMouseButtons(Qt::LeftButton);
setFlag(QGraphicsItem::ItemHasNoContents, true);
setFlag(QGraphicsItem::ItemIsFocusScope, true);
setClip(true);
d->view = new GraphicsWebView(this);
d->view->setResizesToContents(true);
d->view->setFocus();
QWebPage* wp = new QDeclarativeWebPage(this);
setPage(wp);
if (!preferredWidth())
setPreferredWidth(d->view->preferredWidth());
if (!preferredHeight())
setPreferredHeight(d->view->preferredHeight());
connect(d->view, SIGNAL(geometryChanged()), this, SLOT(updateDeclarativeWebViewSize()));
connect(d->view, SIGNAL(doubleClick(int, int)), this, SIGNAL(doubleClick(int, int)));
connect(d->view, SIGNAL(scaleChanged()), this, SIGNAL(contentsScaleChanged()));
}
void QDeclarativeWebView::componentComplete()
{
QDeclarativeItem::componentComplete();
page()->setNetworkAccessManager(qmlEngine(this)->networkAccessManager());
switch (d->pending) {
case QDeclarativeWebViewPrivate::PendingUrl:
setUrl(d->pendingUrl);
break;
case QDeclarativeWebViewPrivate::PendingHtml:
setHtml(d->pendingString, d->pendingUrl);
break;
case QDeclarativeWebViewPrivate::PendingContent:
setContent(d->pendingData, d->pendingString, d->pendingUrl);
break;
default:
break;
}
d->pending = QDeclarativeWebViewPrivate::PendingNone;
d->updateWindowObjects();
}
QDeclarativeWebView::Status QDeclarativeWebView::status() const
{
return d->status;
}
qreal QDeclarativeWebView::progress() const
{
return d->progress;
}
void QDeclarativeWebView::doLoadStarted()
{
if (!d->url.isEmpty()) {
d->status = Loading;
emit statusChanged(d->status);
}
emit loadStarted();
}
void QDeclarativeWebView::doLoadProgress(int p)
{
if (d->progress == p / 100.0)
return;
d->progress = p / 100.0;
emit progressChanged();
}
void QDeclarativeWebView::pageUrlChanged()
{
updateContentsSize();
if ((d->url.isEmpty() && page()->mainFrame()->url() != QUrl(QLatin1String("about:blank")))
|| (d->url != page()->mainFrame()->url() && !page()->mainFrame()->url().isEmpty()))
{
d->url = page()->mainFrame()->url();
if (d->url == QUrl(QLatin1String("about:blank")))
d->url = QUrl();
emit urlChanged();
}
}
void QDeclarativeWebView::doLoadFinished(bool ok)
{
if (ok) {
d->status = d->url.isEmpty() ? Null : Ready;
emit loadFinished();
} else {
d->status = Error;
emit loadFailed();
}
emit statusChanged(d->status);
}
QUrl QDeclarativeWebView::url() const
{
return d->url;
}
void QDeclarativeWebView::setUrl(const QUrl& url)
{
if (url == d->url)
return;
if (isComponentComplete()) {
d->url = url;
updateContentsSize();
QUrl seturl = url;
if (seturl.isEmpty())
seturl = QUrl(QLatin1String("about:blank"));
Q_ASSERT(!seturl.isRelative());
page()->mainFrame()->load(seturl);
emit urlChanged();
} else {
d->pending = d->PendingUrl;
d->pendingUrl = url;
}
}
int QDeclarativeWebView::preferredWidth() const
{
return d->preferredwidth;
}
void QDeclarativeWebView::setPreferredWidth(int width)
{
if (d->preferredwidth == width)
return;
d->preferredwidth = width;
updateContentsSize();
emit preferredWidthChanged();
}
int QDeclarativeWebView::preferredHeight() const
{
return d->preferredheight;
}
void QDeclarativeWebView::setPreferredHeight(int height)
{
if (d->preferredheight == height)
return;
d->preferredheight = height;
updateContentsSize();
emit preferredHeightChanged();
}
QVariant QDeclarativeWebView::evaluateJavaScript(const QString& scriptSource)
{
return this->page()->mainFrame()->evaluateJavaScript(scriptSource);
}
void QDeclarativeWebView::updateDeclarativeWebViewSize()
{
QSizeF size = d->view->geometry().size() * contentsScale();
setImplicitWidth(size.width());
setImplicitHeight(size.height());
}
void QDeclarativeWebView::initialLayout()
{
}
void QDeclarativeWebView::updateContentsSize()
{
if (page()) {
page()->setPreferredContentsSize(QSize(
d->preferredwidth>0 ? d->preferredwidth : width(),
d->preferredheight>0 ? d->preferredheight : height()));
}
}
void QDeclarativeWebView::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
{
QWebPage* webPage = page();
if (newGeometry.size() != oldGeometry.size() && webPage) {
QSize contentSize = webPage->preferredContentsSize();
if (widthValid())
contentSize.setWidth(width());
if (heightValid())
contentSize.setHeight(height());
if (contentSize != webPage->preferredContentsSize())
webPage->setPreferredContentsSize(contentSize);
}
QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
}
QDeclarativeListProperty<QObject> QDeclarativeWebView::javaScriptWindowObjects()
{
return QDeclarativeListProperty<QObject>(this, d, &QDeclarativeWebViewPrivate::windowObjectsAppend);
}
QDeclarativeWebViewAttached* QDeclarativeWebView::qmlAttachedProperties(QObject* o)
{
return new QDeclarativeWebViewAttached(o);
}
void QDeclarativeWebViewPrivate::updateWindowObjects()
{
if (!q->isComponentCompletePublic() || !q->page())
return;
for (int i = 0; i < windowObjects.count(); ++i) {
QObject* object = windowObjects.at(i);
QDeclarativeWebViewAttached* attached = static_cast<QDeclarativeWebViewAttached *>(qmlAttachedPropertiesObject<QDeclarativeWebView>(object));
if (attached && !attached->windowObjectName().isEmpty())
q->page()->mainFrame()->addToJavaScriptWindowObject(attached->windowObjectName(), object);
}
}
bool QDeclarativeWebView::renderingEnabled() const
{
return d->rendering;
}
void QDeclarativeWebView::setRenderingEnabled(bool enabled)
{
if (d->rendering == enabled)
return;
d->rendering = enabled;
emit renderingEnabledChanged();
d->view->setTiledBackingStoreFrozen(!enabled);
}
bool QDeclarativeWebView::heuristicZoom(int clickX, int clickY, qreal maxZoom)
{
if (contentsScale() >= maxZoom / scale())
return false;
qreal ozf = contentsScale();
QRect showArea = elementAreaAt(clickX, clickY, d->preferredwidth / maxZoom, d->preferredheight / maxZoom);
qreal z = qMin(qreal(d->preferredwidth) / showArea.width(), qreal(d->preferredheight) / showArea.height());
if (z > maxZoom / scale())
z = maxZoom / scale();
if (z / ozf > 1.2) {
QRectF r(showArea.left() * z, showArea.top() * z, showArea.width() * z, showArea.height() * z);
emit zoomTo(z, r.x() + r.width() / 2, r.y() + r.height() / 2);
return true;
}
return false;
}
int QDeclarativeWebView::pressGrabTime() const
{
return d->view->pressTime;
}
void QDeclarativeWebView::setPressGrabTime(int millis)
{
if (d->view->pressTime == millis)
return;
d->view->pressTime = millis;
emit pressGrabTimeChanged();
}
#ifndef QT_NO_ACTION
QAction* QDeclarativeWebView::backAction() const
{
return page()->action(QWebPage::Back);
}
QAction* QDeclarativeWebView::forwardAction() const
{
return page()->action(QWebPage::Forward);
}
QAction* QDeclarativeWebView::reloadAction() const
{
return page()->action(QWebPage::Reload);
}
QAction* QDeclarativeWebView::stopAction() const
{
return page()->action(QWebPage::Stop);
}
#endif // QT_NO_ACTION
QString QDeclarativeWebView::title() const
{
return page()->mainFrame()->title();
}
QPixmap QDeclarativeWebView::icon() const
{
return page()->mainFrame()->icon().pixmap(QSize(256, 256));
}
void QDeclarativeWebView::setStatusText(const QString& text)
{
d->statusText = text;
emit statusTextChanged();
}
void QDeclarativeWebView::windowObjectCleared()
{
d->updateWindowObjects();
}
QString QDeclarativeWebView::statusText() const
{
return d->statusText;
}
QWebPage* QDeclarativeWebView::page() const
{
return d->view->page();
}
QDeclarativeWebSettings* QDeclarativeWebView::settingsObject() const
{
d->settings.s = page()->settings();
return &d->settings;
}
void QDeclarativeWebView::setPage(QWebPage* page)
{
if (d->view->page() == page)
return;
d->view->setPage(page);
updateContentsSize();
page->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
page->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
connect(page->mainFrame(), SIGNAL(urlChanged(QUrl)), this, SLOT(pageUrlChanged()));
connect(page->mainFrame(), SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged(QString)));
connect(page->mainFrame(), SIGNAL(iconChanged()), this, SIGNAL(iconChanged()));
connect(page->mainFrame(), SIGNAL(initialLayoutCompleted()), this, SLOT(initialLayout()));
connect(page->mainFrame(), SIGNAL(contentsSizeChanged(QSize)), this, SIGNAL(contentsSizeChanged(QSize)));
connect(page, SIGNAL(loadStarted()), this, SLOT(doLoadStarted()));
connect(page, SIGNAL(loadProgress(int)), this, SLOT(doLoadProgress(int)));
connect(page, SIGNAL(loadFinished(bool)), this, SLOT(doLoadFinished(bool)));
connect(page, SIGNAL(statusBarMessage(QString)), this, SLOT(setStatusText(QString)));
connect(page->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(windowObjectCleared()));
page->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, true);
}
void QDeclarativeWebView::load(const QNetworkRequest& request, QNetworkAccessManager::Operation operation, const QByteArray& body)
{
page()->mainFrame()->load(request, operation, body);
}
QString QDeclarativeWebView::html() const
{
return page()->mainFrame()->toHtml();
}
void QDeclarativeWebView::setHtml(const QString& html, const QUrl& baseUrl)
{
updateContentsSize();
if (isComponentComplete())
page()->mainFrame()->setHtml(html, baseUrl);
else {
d->pending = d->PendingHtml;
d->pendingUrl = baseUrl;
d->pendingString = html;
}
emit htmlChanged();
}
void QDeclarativeWebView::setContent(const QByteArray& data, const QString& mimeType, const QUrl& baseUrl)
{
updateContentsSize();
if (isComponentComplete())
page()->mainFrame()->setContent(data, mimeType, qmlContext(this)->resolvedUrl(baseUrl));
else {
d->pending = d->PendingContent;
d->pendingUrl = baseUrl;
d->pendingString = mimeType;
d->pendingData = data;
}
}
QWebHistory* QDeclarativeWebView::history() const
{
return page()->history();
}
QWebSettings* QDeclarativeWebView::settings() const
{
return page()->settings();
}
QDeclarativeWebView* QDeclarativeWebView::createWindow(QWebPage::WebWindowType type)
{
switch (type) {
case QWebPage::WebBrowserWindow: {
if (!d->newWindowComponent && d->newWindowParent)
qWarning("WebView::newWindowComponent not set - WebView::newWindowParent ignored");
else if (d->newWindowComponent && !d->newWindowParent)
qWarning("WebView::newWindowParent not set - WebView::newWindowComponent ignored");
else if (d->newWindowComponent && d->newWindowParent) {
QDeclarativeWebView* webview = 0;
QDeclarativeContext* windowContext = new QDeclarativeContext(qmlContext(this));
QObject* newObject = d->newWindowComponent->create(windowContext);
if (newObject) {
windowContext->setParent(newObject);
QDeclarativeItem* item = qobject_cast<QDeclarativeItem *>(newObject);
if (!item)
delete newObject;
else {
webview = item->findChild<QDeclarativeWebView*>();
if (!webview)
delete item;
else {
newObject->setParent(d->newWindowParent);
static_cast<QGraphicsObject*>(item)->setParentItem(d->newWindowParent);
}
}
} else
delete windowContext;
return webview;
}
}
break;
case QWebPage::WebModalDialog: {
}
}
return 0;
}
QDeclarativeComponent* QDeclarativeWebView::newWindowComponent() const
{
return d->newWindowComponent;
}
void QDeclarativeWebView::setNewWindowComponent(QDeclarativeComponent* newWindow)
{
if (newWindow == d->newWindowComponent)
return;
d->newWindowComponent = newWindow;
emit newWindowComponentChanged();
}
QDeclarativeItem* QDeclarativeWebView::newWindowParent() const
{
return d->newWindowParent;
}
void QDeclarativeWebView::setNewWindowParent(QDeclarativeItem* parent)
{
if (parent == d->newWindowParent)
return;
if (d->newWindowParent && parent) {
QList<QGraphicsItem *> children = d->newWindowParent->childItems();
for (int i = 0; i < children.count(); ++i)
children.at(i)->setParentItem(parent);
}
d->newWindowParent = parent;
emit newWindowParentChanged();
}
QSize QDeclarativeWebView::contentsSize() const
{
return page()->mainFrame()->contentsSize() * contentsScale();
}
qreal QDeclarativeWebView::contentsScale() const
{
return d->view->scale();
}
void QDeclarativeWebView::setContentsScale(qreal scale)
{
if (scale == d->view->scale())
return;
d->view->setScale(scale);
updateDeclarativeWebViewSize();
emit contentsScaleChanged();
}
#ifdef Q_REVISION
QColor QDeclarativeWebView::backgroundColor() const
{
return d->view->palette().base().color();
}
void QDeclarativeWebView::setBackgroundColor(const QColor& color)
{
QPalette palette = d->view->palette();
if (palette.base().color() == color)
return;
palette.setBrush(QPalette::Base, color);
d->view->setPalette(palette);
emit backgroundColorChanged();
}
#endif
QRect QDeclarativeWebView::elementAreaAt(int x, int y, int maxWidth, int maxHeight) const
{
QWebHitTestResult hit = page()->mainFrame()->hitTestContent(QPoint(x, y));
QRect hitRect = hit.boundingRect();
QWebElement element = hit.enclosingBlockElement();
if (maxWidth <= 0)
maxWidth = INT_MAX;
if (maxHeight <= 0)
maxHeight = INT_MAX;
while (!element.parent().isNull() && element.geometry().width() <= maxWidth && element.geometry().height() <= maxHeight) {
hitRect = element.geometry();
element = element.parent();
}
return hitRect;
}
QDeclarativeWebPage::QDeclarativeWebPage(QDeclarativeWebView* parent) :
QWebPage(parent)
{
}
QDeclarativeWebPage::~QDeclarativeWebPage()
{
}
QString QDeclarativeWebPage::chooseFile(QWebFrame* originatingFrame, const QString& oldFile)
{
Q_UNUSED(originatingFrame)
Q_UNUSED(oldFile)
return oldFile;
}
void QDeclarativeWebPage::javaScriptAlert(QWebFrame* originatingFrame, const QString& msg)
{
Q_UNUSED(originatingFrame)
emit viewItem()->alert(msg);
}
bool QDeclarativeWebPage::javaScriptConfirm(QWebFrame* originatingFrame, const QString& msg)
{
Q_UNUSED(originatingFrame)
Q_UNUSED(msg)
return false;
}
bool QDeclarativeWebPage::javaScriptPrompt(QWebFrame* originatingFrame, const QString& msg, const QString& defaultValue, QString* result)
{
Q_UNUSED(originatingFrame)
Q_UNUSED(msg)
Q_UNUSED(defaultValue)
Q_UNUSED(result)
return false;
}
QDeclarativeWebView* QDeclarativeWebPage::viewItem()
{
return static_cast<QDeclarativeWebView*>(parent());
}
QWebPage* QDeclarativeWebPage::createWindow(WebWindowType type)
{
QDeclarativeWebView* newView = viewItem()->createWindow(type);
if (newView)
return newView->page();
return 0;
}
QT_END_NAMESPACE