Files correlati : cg0.exe cg0700a.msk cg0700b.msk cg3.exe cg4.exe Bug : Commento: Merge 1.0 libraries
578 lines
18 KiB
C++
578 lines
18 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
** Contact: http://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the examples of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:BSD$
|
|
** You may use this file under the terms of the BSD license as follows:
|
|
**
|
|
** "Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions are
|
|
** met:
|
|
** * Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** * 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.
|
|
** * Neither the name of The Qt Company Ltd 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
|
** OWNER 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."
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "environment.h"
|
|
#include "qcontext2dcanvas.h"
|
|
#include "context2d.h"
|
|
#include <QScriptValueIterator>
|
|
#include <QDateTime>
|
|
|
|
struct FakeDomEvent
|
|
{
|
|
enum KeyCodes {
|
|
DOM_VK_UNDEFINED = 0x0,
|
|
DOM_VK_RIGHT_ALT = 0x12,
|
|
DOM_VK_LEFT_ALT = 0x12,
|
|
DOM_VK_LEFT_CONTROL = 0x11,
|
|
DOM_VK_RIGHT_CONTROL = 0x11,
|
|
DOM_VK_LEFT_SHIFT = 0x10,
|
|
DOM_VK_RIGHT_SHIFT = 0x10,
|
|
DOM_VK_META = 0x9D,
|
|
DOM_VK_BACK_SPACE = 0x08,
|
|
DOM_VK_CAPS_LOCK = 0x14,
|
|
DOM_VK_DELETE = 0x7F,
|
|
DOM_VK_END = 0x23,
|
|
DOM_VK_ENTER = 0x0D,
|
|
DOM_VK_ESCAPE = 0x1B,
|
|
DOM_VK_HOME = 0x24,
|
|
DOM_VK_NUM_LOCK = 0x90,
|
|
DOM_VK_PAUSE = 0x13,
|
|
DOM_VK_PRINTSCREEN = 0x9A,
|
|
DOM_VK_SCROLL_LOCK = 0x91,
|
|
DOM_VK_SPACE = 0x20,
|
|
DOM_VK_TAB = 0x09,
|
|
DOM_VK_LEFT = 0x25,
|
|
DOM_VK_RIGHT = 0x27,
|
|
DOM_VK_UP = 0x26,
|
|
DOM_VK_DOWN = 0x28,
|
|
DOM_VK_PAGE_DOWN = 0x22,
|
|
DOM_VK_PAGE_UP = 0x21,
|
|
DOM_VK_F1 = 0x70,
|
|
DOM_VK_F2 = 0x71,
|
|
DOM_VK_F3 = 0x72,
|
|
DOM_VK_F4 = 0x73,
|
|
DOM_VK_F5 = 0x74,
|
|
DOM_VK_F6 = 0x75,
|
|
DOM_VK_F7 = 0x76,
|
|
DOM_VK_F8 = 0x77,
|
|
DOM_VK_F9 = 0x78,
|
|
DOM_VK_F10 = 0x79,
|
|
DOM_VK_F11 = 0x7A,
|
|
DOM_VK_F12 = 0x7B,
|
|
DOM_VK_F13 = 0xF000,
|
|
DOM_VK_F14 = 0xF001,
|
|
DOM_VK_F15 = 0xF002,
|
|
DOM_VK_F16 = 0xF003,
|
|
DOM_VK_F17 = 0xF004,
|
|
DOM_VK_F18 = 0xF005,
|
|
DOM_VK_F19 = 0xF006,
|
|
DOM_VK_F20 = 0xF007,
|
|
DOM_VK_F21 = 0xF008,
|
|
DOM_VK_F22 = 0xF009,
|
|
DOM_VK_F23 = 0xF00A,
|
|
DOM_VK_F24 = 0xF00B
|
|
};
|
|
|
|
static int qtToDomKey(int keyCode);
|
|
};
|
|
|
|
int FakeDomEvent::qtToDomKey(int keyCode)
|
|
{
|
|
switch (keyCode) {
|
|
case Qt::Key_Backspace:
|
|
return DOM_VK_BACK_SPACE;
|
|
case Qt::Key_Enter:
|
|
return DOM_VK_ENTER;
|
|
case Qt::Key_Return:
|
|
return DOM_VK_ENTER;
|
|
case Qt::Key_NumLock:
|
|
return DOM_VK_NUM_LOCK;
|
|
case Qt::Key_Alt:
|
|
return DOM_VK_RIGHT_ALT;
|
|
case Qt::Key_Control:
|
|
return DOM_VK_LEFT_CONTROL;
|
|
case Qt::Key_Shift:
|
|
return DOM_VK_LEFT_SHIFT;
|
|
case Qt::Key_Meta:
|
|
return DOM_VK_META;
|
|
case Qt::Key_CapsLock:
|
|
return DOM_VK_CAPS_LOCK;
|
|
case Qt::Key_Delete:
|
|
return DOM_VK_DELETE;
|
|
case Qt::Key_End:
|
|
return DOM_VK_END;
|
|
case Qt::Key_Escape:
|
|
return DOM_VK_ESCAPE;
|
|
case Qt::Key_Home:
|
|
return DOM_VK_HOME;
|
|
case Qt::Key_Pause:
|
|
return DOM_VK_PAUSE;
|
|
case Qt::Key_Print:
|
|
return DOM_VK_PRINTSCREEN;
|
|
case Qt::Key_ScrollLock:
|
|
return DOM_VK_SCROLL_LOCK;
|
|
case Qt::Key_Left:
|
|
return DOM_VK_LEFT;
|
|
case Qt::Key_Right:
|
|
return DOM_VK_RIGHT;
|
|
case Qt::Key_Up:
|
|
return DOM_VK_UP;
|
|
case Qt::Key_Down:
|
|
return DOM_VK_DOWN;
|
|
case Qt::Key_PageDown:
|
|
return DOM_VK_PAGE_DOWN;
|
|
case Qt::Key_PageUp:
|
|
return DOM_VK_PAGE_UP;
|
|
case Qt::Key_F1:
|
|
return DOM_VK_F1;
|
|
case Qt::Key_F2:
|
|
return DOM_VK_F2;
|
|
case Qt::Key_F3:
|
|
return DOM_VK_F3;
|
|
case Qt::Key_F4:
|
|
return DOM_VK_F4;
|
|
case Qt::Key_F5:
|
|
return DOM_VK_F5;
|
|
case Qt::Key_F6:
|
|
return DOM_VK_F6;
|
|
case Qt::Key_F7:
|
|
return DOM_VK_F7;
|
|
case Qt::Key_F8:
|
|
return DOM_VK_F8;
|
|
case Qt::Key_F9:
|
|
return DOM_VK_F9;
|
|
case Qt::Key_F10:
|
|
return DOM_VK_F10;
|
|
case Qt::Key_F11:
|
|
return DOM_VK_F11;
|
|
case Qt::Key_F12:
|
|
return DOM_VK_F12;
|
|
case Qt::Key_F13:
|
|
return DOM_VK_F13;
|
|
case Qt::Key_F14:
|
|
return DOM_VK_F14;
|
|
case Qt::Key_F15:
|
|
return DOM_VK_F15;
|
|
case Qt::Key_F16:
|
|
return DOM_VK_F16;
|
|
case Qt::Key_F17:
|
|
return DOM_VK_F17;
|
|
case Qt::Key_F18:
|
|
return DOM_VK_F18;
|
|
case Qt::Key_F19:
|
|
return DOM_VK_F19;
|
|
case Qt::Key_F20:
|
|
return DOM_VK_F20;
|
|
case Qt::Key_F21:
|
|
return DOM_VK_F21;
|
|
case Qt::Key_F22:
|
|
return DOM_VK_F22;
|
|
case Qt::Key_F23:
|
|
return DOM_VK_F23;
|
|
case Qt::Key_F24:
|
|
return DOM_VK_F24;
|
|
}
|
|
return keyCode;
|
|
}
|
|
|
|
//! [0]
|
|
Environment::Environment(QObject *parent)
|
|
: QObject(parent)
|
|
{
|
|
m_engine = new QScriptEngine(this);
|
|
|
|
m_document = m_engine->newQObject(
|
|
new Document(this), QScriptEngine::QtOwnership,
|
|
QScriptEngine::ExcludeSuperClassContents);
|
|
|
|
CanvasGradientPrototype::setup(m_engine);
|
|
|
|
m_originalGlobalObject = m_engine->globalObject();
|
|
reset();
|
|
}
|
|
//! [0]
|
|
|
|
Environment::~Environment()
|
|
{
|
|
}
|
|
|
|
QScriptEngine *Environment::engine() const
|
|
{
|
|
return m_engine;
|
|
}
|
|
|
|
QScriptValue Environment::document() const
|
|
{
|
|
return m_document;
|
|
}
|
|
|
|
int Environment::setTimeout(const QScriptValue &expression, int delay)
|
|
{
|
|
if (expression.isString() || expression.isFunction()) {
|
|
int timerId = startTimer(delay);
|
|
m_timeoutHash.insert(timerId, expression);
|
|
return timerId;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Environment::clearTimeout(int timerId)
|
|
{
|
|
killTimer(timerId);
|
|
m_timeoutHash.remove(timerId);
|
|
}
|
|
|
|
//! [1]
|
|
int Environment::setInterval(const QScriptValue &expression, int delay)
|
|
{
|
|
if (expression.isString() || expression.isFunction()) {
|
|
int timerId = startTimer(delay);
|
|
m_intervalHash.insert(timerId, expression);
|
|
return timerId;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Environment::clearInterval(int timerId)
|
|
{
|
|
killTimer(timerId);
|
|
m_intervalHash.remove(timerId);
|
|
}
|
|
|
|
void Environment::timerEvent(QTimerEvent *event)
|
|
{
|
|
int id = event->timerId();
|
|
QScriptValue expression = m_intervalHash.value(id);
|
|
if (!expression.isValid()) {
|
|
expression = m_timeoutHash.value(id);
|
|
if (expression.isValid())
|
|
killTimer(id);
|
|
}
|
|
if (expression.isString()) {
|
|
evaluate(expression.toString());
|
|
} else if (expression.isFunction()) {
|
|
expression.call();
|
|
}
|
|
maybeEmitScriptError();
|
|
}
|
|
//! [1]
|
|
|
|
//! [5]
|
|
void Environment::addCanvas(QContext2DCanvas *canvas)
|
|
{
|
|
m_canvases.append(canvas);
|
|
}
|
|
|
|
QContext2DCanvas *Environment::canvasByName(const QString &name) const
|
|
{
|
|
for (int i = 0; i < m_canvases.size(); ++i) {
|
|
QContext2DCanvas *canvas = m_canvases.at(i);
|
|
if (canvas->objectName() == name)
|
|
return canvas;
|
|
}
|
|
return 0;
|
|
}
|
|
//! [5]
|
|
|
|
QList<QContext2DCanvas*> Environment::canvases() const
|
|
{
|
|
return m_canvases;
|
|
}
|
|
|
|
void Environment::reset()
|
|
{
|
|
if (m_engine->isEvaluating())
|
|
m_engine->abortEvaluation();
|
|
|
|
{
|
|
QHash<int, QScriptValue>::const_iterator it;
|
|
for (it = m_intervalHash.constBegin(); it != m_intervalHash.constEnd(); ++it)
|
|
killTimer(it.key());
|
|
m_intervalHash.clear();
|
|
for (it = m_timeoutHash.constBegin(); it != m_timeoutHash.constEnd(); ++it)
|
|
killTimer(it.key());
|
|
m_timeoutHash.clear();
|
|
}
|
|
|
|
for (int i = 0; i < m_canvases.size(); ++i)
|
|
m_canvases.at(i)->reset();
|
|
|
|
QScriptValue self = m_engine->newQObject(
|
|
this, QScriptEngine::QtOwnership,
|
|
QScriptEngine::ExcludeSuperClassContents);
|
|
|
|
{
|
|
QScriptValueIterator it(m_originalGlobalObject);
|
|
while (it.hasNext()) {
|
|
it.next();
|
|
self.setProperty(it.scriptName(), it.value(), it.flags());
|
|
}
|
|
}
|
|
|
|
self.setProperty("self", self);
|
|
self.setProperty("window", self);
|
|
|
|
QScriptValue navigator = m_engine->newObject();
|
|
navigator.setProperty("appCodeName", "context2d");
|
|
navigator.setProperty("appMinorVersion", 1);
|
|
navigator.setProperty("appVersion", 1);
|
|
navigator.setProperty("browserLanguage", "en_US");
|
|
navigator.setProperty("cookieEnabled", false);
|
|
navigator.setProperty("cpuClass", "i686");
|
|
navigator.setProperty("onLine", false);
|
|
navigator.setProperty("platform", "bogus OS");
|
|
navigator.setProperty("systemLanguage", "en_US");
|
|
navigator.setProperty("userAgent", "Context2D/1.1");
|
|
navigator.setProperty("userLanguage", "en_US");
|
|
self.setProperty("navigator", navigator);
|
|
|
|
m_engine->setGlobalObject(self);
|
|
|
|
m_engine->collectGarbage();
|
|
}
|
|
|
|
QScriptValue Environment::evaluate(const QString &code, const QString &fileName)
|
|
{
|
|
return m_engine->evaluate(code, fileName);
|
|
}
|
|
|
|
bool Environment::hasIntervalTimers() const
|
|
{
|
|
return !m_intervalHash.isEmpty();
|
|
}
|
|
|
|
// This is used by the Context2D QtScript benchmark.
|
|
void Environment::triggerTimers()
|
|
{
|
|
for (int x = 0; x < 2; ++x) {
|
|
QList<int> timerIds = x ? m_intervalHash.keys() : m_timeoutHash.keys();
|
|
for (int i = 0; i < timerIds.size(); ++i) {
|
|
QTimerEvent fakeEvent(timerIds.at(i));
|
|
timerEvent(&fakeEvent);
|
|
}
|
|
}
|
|
}
|
|
|
|
//! [2]
|
|
QScriptValue Environment::toWrapper(QObject *object)
|
|
{
|
|
return m_engine->newQObject(object, QScriptEngine::QtOwnership,
|
|
QScriptEngine::PreferExistingWrapperObject
|
|
| QScriptEngine::ExcludeSuperClassContents);
|
|
}
|
|
//! [2]
|
|
|
|
//! [3]
|
|
void Environment::handleEvent(QContext2DCanvas *canvas, QMouseEvent *e)
|
|
{
|
|
QString type;
|
|
switch (e->type()) {
|
|
case QEvent::MouseButtonPress:
|
|
type = "mousedown"; break;
|
|
case QEvent::MouseButtonRelease:
|
|
type = "mouseup"; break;
|
|
case QEvent::MouseMove:
|
|
type = "mousemove"; break;
|
|
default: break;
|
|
}
|
|
if (type.isEmpty())
|
|
return;
|
|
|
|
QScriptValue handlerObject;
|
|
QScriptValue handler = eventHandler(canvas, type, &handlerObject);
|
|
if (!handler.isFunction())
|
|
return;
|
|
|
|
QScriptValue scriptEvent = newFakeDomEvent(type, toWrapper(canvas));
|
|
// MouseEvent
|
|
scriptEvent.setProperty("screenX", e->globalX(), QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("screenY", e->globalY(), QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("clientX", e->x(), QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("clientY", e->y(), QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("layerX", e->x(), QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("layerY", e->y(), QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("pageX", e->x(), QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("pageY", e->y(), QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("altKey", (e->modifiers() & Qt::AltModifier) != 0,
|
|
QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("ctrlKey", (e->modifiers() & Qt::ControlModifier) != 0,
|
|
QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("metaKey", (e->modifiers() & Qt::MetaModifier) != 0,
|
|
QScriptValue::ReadOnly);
|
|
scriptEvent.setProperty("shiftKey", (e->modifiers() & Qt::ShiftModifier) != 0,
|
|
QScriptValue::ReadOnly);
|
|
int button = 0;
|
|
if (e->button() == Qt::RightButton)
|
|
button = 2;
|
|
else if (e->button() == Qt::MidButton)
|
|
button = 1;
|
|
scriptEvent.setProperty("button", button);
|
|
scriptEvent.setProperty("relatedTarget", m_engine->nullValue(),
|
|
QScriptValue::ReadOnly);
|
|
handler.call(handlerObject, QScriptValueList() << scriptEvent);
|
|
maybeEmitScriptError();
|
|
}
|
|
//! [3]
|
|
|
|
void Environment::handleEvent(QContext2DCanvas *canvas, QKeyEvent *e)
|
|
{
|
|
QString type;
|
|
switch (e->type()) {
|
|
case QEvent::KeyPress:
|
|
type = "keydown"; break;
|
|
case QEvent::KeyRelease:
|
|
type = "keyup"; break;
|
|
default: break;
|
|
}
|
|
if (type.isEmpty())
|
|
return;
|
|
|
|
QScriptValue handlerObject;
|
|
QScriptValue handler = eventHandler(canvas, type, &handlerObject);
|
|
if (!handler.isFunction())
|
|
return;
|
|
|
|
QScriptValue scriptEvent = newFakeDomEvent(type, toWrapper(canvas));
|
|
// KeyEvent
|
|
scriptEvent.setProperty("isChar", !e->text().isEmpty());
|
|
scriptEvent.setProperty("charCode", e->text());
|
|
scriptEvent.setProperty("keyCode", FakeDomEvent::qtToDomKey(e->key()));
|
|
scriptEvent.setProperty("which", e->key());
|
|
|
|
handler.call(handlerObject, QScriptValueList() << scriptEvent);
|
|
maybeEmitScriptError();
|
|
}
|
|
|
|
QScriptValue Environment::eventHandler(QContext2DCanvas *canvas, const QString &type,
|
|
QScriptValue *who)
|
|
{
|
|
QString handlerName = "on" + type;
|
|
QScriptValue obj = toWrapper(canvas);
|
|
QScriptValue handler = obj.property(handlerName);
|
|
if (!handler.isValid()) {
|
|
obj = m_document;
|
|
handler = obj.property(handlerName);
|
|
}
|
|
if (who && handler.isFunction())
|
|
*who = obj;
|
|
return handler;
|
|
}
|
|
|
|
//! [4]
|
|
QScriptValue Environment::newFakeDomEvent(const QString &type, const QScriptValue &target)
|
|
{
|
|
QScriptValue e = m_engine->newObject();
|
|
// Event
|
|
e.setProperty("type", type, QScriptValue::ReadOnly);
|
|
e.setProperty("bubbles", true, QScriptValue::ReadOnly);
|
|
e.setProperty("cancelable", false, QScriptValue::ReadOnly);
|
|
e.setProperty("target", target, QScriptValue::ReadOnly);
|
|
e.setProperty("currentTarget", target, QScriptValue::ReadOnly);
|
|
e.setProperty("eventPhase", 3); // bubbling
|
|
e.setProperty("timeStamp", QDateTime::currentDateTime().toTime_t());
|
|
// UIEvent
|
|
e.setProperty("detail", 0, QScriptValue::ReadOnly);
|
|
e.setProperty("view", m_engine->globalObject(), QScriptValue::ReadOnly);
|
|
return e;
|
|
}
|
|
//! [4]
|
|
|
|
void Environment::maybeEmitScriptError()
|
|
{
|
|
if (m_engine->hasUncaughtException())
|
|
emit scriptError(m_engine->uncaughtException());
|
|
}
|
|
|
|
|
|
Document::Document(Environment *env)
|
|
: QObject(env)
|
|
{
|
|
}
|
|
|
|
Document::~Document()
|
|
{
|
|
}
|
|
|
|
QScriptValue Document::getElementById(const QString &id) const
|
|
{
|
|
Environment *env = qobject_cast<Environment*>(parent());
|
|
QContext2DCanvas *canvas = env->canvasByName(id);
|
|
if (!canvas)
|
|
return QScriptValue();
|
|
return env->toWrapper(canvas);
|
|
}
|
|
|
|
QScriptValue Document::getElementsByTagName(const QString &name) const
|
|
{
|
|
if (name != "canvas")
|
|
return QScriptValue();
|
|
Environment *env = qobject_cast<Environment*>(parent());
|
|
QList<QContext2DCanvas*> list = env->canvases();
|
|
QScriptValue result = env->engine()->newArray(list.size());
|
|
for (int i = 0; i < list.size(); ++i)
|
|
result.setProperty(i, env->toWrapper(list.at(i)));
|
|
return result;
|
|
}
|
|
|
|
void Document::addEventListener(const QString &type, const QScriptValue &listener,
|
|
bool useCapture)
|
|
{
|
|
Q_UNUSED(useCapture);
|
|
if (listener.isFunction()) {
|
|
Environment *env = qobject_cast<Environment*>(parent());
|
|
QScriptValue self = env->toWrapper(this);
|
|
self.setProperty("on" + type, listener);
|
|
}
|
|
}
|
|
|
|
|
|
QColor colorFromString(const QString &name);
|
|
|
|
CanvasGradientPrototype::CanvasGradientPrototype(QObject *parent)
|
|
: QObject(parent)
|
|
{
|
|
}
|
|
|
|
void CanvasGradientPrototype::addColorStop(qreal offset, const QString &color)
|
|
{
|
|
CanvasGradient *self = qscriptvalue_cast<CanvasGradient*>(thisObject());
|
|
if (!self || (self->value.type() == QGradient::NoGradient))
|
|
return;
|
|
self->value.setColorAt(offset, colorFromString(color));
|
|
}
|
|
|
|
void CanvasGradientPrototype::setup(QScriptEngine *engine)
|
|
{
|
|
CanvasGradientPrototype *proto = new CanvasGradientPrototype();
|
|
engine->setDefaultPrototype(qMetaTypeId<CanvasGradient>(),
|
|
engine->newQObject(proto, QScriptEngine::ScriptOwnership,
|
|
QScriptEngine::ExcludeSuperClassContents));
|
|
}
|