File indexing completed on 2024-03-24 15:37:26

0001 /***************************************************************************
0002  * plugin.cpp
0003  * This file is part of the KDE project
0004  * copyright (C)2007 by Sebastian Sauer (mail@dipe.org)
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
0010  * This program is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this program; see the file COPYING.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  ***************************************************************************/
0019 
0020 #include "plugin.h"
0021 #include "kross_qts_plugin_debug.h"
0022 
0023 #include "../core/manager.h"
0024 #include "../core/object.h"
0025 
0026 #include <QByteArray>
0027 #include <QUrl>
0028 #include <QColor>
0029 #include <QRect>
0030 #include <QPoint>
0031 #include <QSize>
0032 #include <QWidget>
0033 #include <QLayout>
0034 #include <QVBoxLayout>
0035 #include <QtUiTools/QUiLoader>
0036 #include <QAbstractItemView>
0037 #include <QAbstractItemModel>
0038 #include <QStringListModel>
0039 #include <QScriptClass>
0040 #include <QScriptContext>
0041 #include <QScriptValueIterator>
0042 #include <QDebug>
0043 
0044 #include <klocalizedstring.h>
0045 
0046 using namespace Kross;
0047 
0048 namespace Kross
0049 {
0050 
0051 /**
0052 * The EcmaObject implements QScriptClass and Kross::Object to provide
0053 * custom behavior to QtScript objects.
0054 */
0055 class EcmaObject : public QScriptClass, public Kross::Object
0056 {
0057 public:
0058     explicit EcmaObject(QScriptEngine *engine, const QScriptValue &object = QScriptValue())
0059         : QScriptClass(engine), Kross::Object(), m_object(object) {}
0060     ~EcmaObject() override {}
0061     QString name() const override
0062     {
0063         return "KrossObject";
0064     }
0065     QScriptValue prototype() const override
0066     {
0067         return m_object;
0068     }
0069 
0070     QVariant callMethod(const QString &name, const QVariantList &args = QVariantList()) override
0071     {
0072         QScriptValue function = m_object.property(name);
0073         if (! function.isFunction()) {
0074             qCWarning(KROSS_QTS_PLUGIN_LOG) << "EcmaScript::callFunction No such function " << name;
0075             if (QScriptContext *context = engine()->currentContext()) {
0076                 context->throwError(QScriptContext::ReferenceError, i18n("No such function \"%1\"", name));
0077             }
0078             return QVariant();
0079         }
0080         QScriptValueList arguments;
0081         foreach (const QVariant &v, args) {
0082             arguments << engine()->toScriptValue(v);
0083         }
0084         QScriptValue result = function.call(m_object, arguments);
0085         return result.toVariant();
0086     }
0087 
0088     QStringList methodNames() override
0089     {
0090         QStringList methods;
0091         QScriptValueIterator it(m_object);
0092         while (it.hasNext()) {
0093             it.next();
0094             if (it.value().isFunction()) {
0095                 methods << it.name();
0096             }
0097         }
0098         return methods;
0099     }
0100 
0101 private:
0102     QScriptValue m_object;
0103 };
0104 
0105 QScriptValue toByteArray(QScriptEngine *e, const QByteArray &ba)
0106 {
0107     return ba.isNull() ? e->nullValue() : e->newVariant(ba);
0108 }
0109 void fromByteArray(const QScriptValue &v, QByteArray &ba)
0110 {
0111     ba = v.isNull() ? QByteArray() : v.toString().toUtf8();
0112 }
0113 
0114 QScriptValue toUrl(QScriptEngine *e, const QUrl &url)
0115 {
0116     return e->newVariant(url.toString());
0117 }
0118 void fromUrl(const QScriptValue &v, QUrl &url)
0119 {
0120     url.setUrl(v.toString());
0121 }
0122 
0123 QScriptValue toColor(QScriptEngine *e, const QColor &c)
0124 {
0125     return c.isValid() ? e->newVariant(c.name()) : e->nullValue();
0126 }
0127 void fromColor(const QScriptValue &v, QColor &c)
0128 {
0129     c.setNamedColor(v.isNull() ? QString() : v.toString());
0130 }
0131 
0132 QScriptValue toRect(QScriptEngine *e, const QRect &r)
0133 {
0134     return e->toScriptValue(QVariantList() << r.x() << r.y() << r.width() << r.height());
0135 }
0136 void fromRect(const QScriptValue &v, QRect &r)
0137 {
0138     r = v.isArray() ? QRect(v.property(0).toInt32(), v.property(1).toInt32(), v.property(2).toInt32(), v.property(3).toInt32()) : QRect();
0139 }
0140 QScriptValue toRectF(QScriptEngine *e, const QRectF &r)
0141 {
0142     return e->toScriptValue(QVariantList() << r.x() << r.y() << r.width() << r.height());
0143 }
0144 void fromRectF(const QScriptValue &v, QRectF &r)
0145 {
0146     r = v.isArray() ? QRectF(v.property(0).toNumber(), v.property(1).toNumber(), v.property(2).toNumber(), v.property(3).toNumber()) : QRectF();
0147 }
0148 
0149 QScriptValue toPoint(QScriptEngine *e, const QPoint &p)
0150 {
0151     return e->toScriptValue(QVariantList() << p.x() << p.y());
0152 }
0153 void fromPoint(const QScriptValue &v, QPoint &p)
0154 {
0155     p = v.isArray() ? QPoint(v.property(0).toInt32(), v.property(1).toInt32()) : QPoint();
0156 }
0157 QScriptValue toPointF(QScriptEngine *e, const QPointF &p)
0158 {
0159     return e->toScriptValue(QVariantList() << p.x() << p.y());
0160 }
0161 void fromPointF(const QScriptValue &v, QPointF &p)
0162 {
0163     p = v.isArray() ? QPointF(v.property(0).toNumber(), v.property(1).toNumber()) : QPointF();
0164 }
0165 
0166 QScriptValue toSize(QScriptEngine *e, const QSize &s)
0167 {
0168     return e->toScriptValue(QVariantList() << s.width() << s.height());
0169 }
0170 void fromSize(const QScriptValue &v, QSize &s)
0171 {
0172     s = v.isArray() ? QSize(v.property(0).toInt32(), v.property(1).toInt32()) : QSize();
0173 }
0174 QScriptValue toSizeF(QScriptEngine *e, const QSizeF &s)
0175 {
0176     return e->toScriptValue(QVariantList() << s.width() << s.height());
0177 }
0178 void fromSizeF(const QScriptValue &v, QSizeF &s)
0179 {
0180     s = v.isArray() ? QSizeF(v.property(0).toNumber(), v.property(1).toNumber()) : QSizeF();
0181 }
0182 
0183 /*
0184 QScriptValue toVariantList(QScriptEngine *e, const QVariantList &l) {
0185     const int len = l.size();
0186     QScriptValue a = e->newArray(len);
0187     for(int i = 0; i < len; ++i)
0188         a.setProperty(i, e->toScriptValue(l[i]));
0189     return a;
0190 }
0191 void fromVariantList(const QScriptValue &v, QVariantList &l) {
0192     l.clear();
0193     const int len = v.isArray() ? v.property("length").toInt32() : 0;
0194     for(int i = 0; i < len; ++i)
0195         l << v.property(i).toVariant();
0196 }
0197 */
0198 
0199 QScriptValue toObjPtr(QScriptEngine *e, const Kross::Object::Ptr &ptr)
0200 {
0201     const EcmaObject *obj = dynamic_cast<const EcmaObject *>(ptr.data());
0202     return obj ? obj->prototype() : e->nullValue();
0203 }
0204 void fromObjPtr(const QScriptValue &v, Kross::Object::Ptr &ptr)
0205 {
0206     ptr = new EcmaObject(v.engine(), v);
0207 }
0208 
0209 QScriptValue createWidget(QScriptContext *context, QScriptEngine *engine)
0210 {
0211     const QString widgetname = context->callee().prototype().property("className").toString();
0212     Q_ASSERT(! widgetname.isEmpty());
0213     QWidget *parent = qscriptvalue_cast<QWidget *>(context->argument(0));
0214     QUiLoader loader;
0215     QWidget *widget = loader.createWidget(widgetname, parent);
0216     if (! widget) {
0217         return context->throwError(QScriptContext::TypeError, QString("No such QWidget \"%1\"").arg(widgetname));
0218     }
0219     if (parent && parent->layout()) {
0220         parent->layout()->addWidget(widget);
0221     }
0222     QScriptEngine::ValueOwnership owner = parent ? QScriptEngine::QtOwnership : QScriptEngine::ScriptOwnership;
0223     QScriptValue result = engine->newQObject(widget, owner);
0224     //result.setPrototype(context->callee().prototype());
0225     return result;
0226 }
0227 
0228 QScriptValue addWidgetLayout(QScriptContext *c, QScriptEngine *engine)
0229 {
0230     if (QLayout *layout = dynamic_cast<QLayout *>(qscriptvalue_cast<QObject *>(c->thisObject()))) {
0231         QGridLayout *gridLayout = dynamic_cast<QGridLayout *>(layout);
0232         QObject *obj = qscriptvalue_cast<QObject *>(c->argument(0));
0233         if (QWidget *w = dynamic_cast<QWidget *>(obj)) {
0234             if (gridLayout) {
0235                 gridLayout->addWidget(w, c->argument(1).toInt32(), c->argument(2).toInt32(), (Qt::Alignment)c->argument(3).toInt32());
0236             } else {
0237                 layout->addWidget(w);
0238             }
0239         } else if (QLayout *l = dynamic_cast<QLayout *>(qscriptvalue_cast<QObject *>(c->argument(0)))) {
0240             if (gridLayout) {
0241                 gridLayout->addLayout(l, c->argument(1).toInt32(), c->argument(2).toInt32(), (Qt::Alignment)c->argument(3).toInt32());
0242             } else if (QBoxLayout *bl = dynamic_cast<QBoxLayout *>(layout)) {
0243                 bl->addLayout(l);
0244             }
0245         }
0246     }
0247     return engine->nullValue();
0248 }
0249 QScriptValue createLayout(QScriptContext *context, QScriptEngine *engine, QLayout *layout)
0250 {
0251     QObject *parent = qscriptvalue_cast<QObject *>(context->argument(0));
0252     if (QWidget *parentWidget = dynamic_cast<QWidget *>(parent)) {
0253         parentWidget->setLayout(layout);
0254     } else if (QBoxLayout *parentLayout = dynamic_cast<QBoxLayout *>(parent)) {
0255         parentLayout->addLayout(layout);
0256     }
0257     QScriptValue obj = engine->newQObject(layout);
0258     obj.setProperty("addWidget", engine->newFunction(addWidgetLayout));
0259     obj.setProperty("addLayout", engine->newFunction(addWidgetLayout));
0260     return obj;
0261 }
0262 QScriptValue createVBoxLayout(QScriptContext *context, QScriptEngine *engine)
0263 {
0264     return createLayout(context, engine, new QVBoxLayout());
0265 }
0266 QScriptValue createHBoxLayout(QScriptContext *context, QScriptEngine *engine)
0267 {
0268     return createLayout(context, engine, new QHBoxLayout());
0269 }
0270 QScriptValue createGridLayout(QScriptContext *context, QScriptEngine *engine)
0271 {
0272     return createLayout(context, engine, new QGridLayout());
0273 }
0274 QScriptValue includeFunction(QScriptContext *context, QScriptEngine *engine)
0275 {
0276     if (context->argumentCount() < 1) {
0277         return engine->nullValue();
0278     }
0279     return engine->importExtension(context->argument(0).toString());
0280 }
0281 
0282 /**
0283 * Initialize some core functionality like common used types we like
0284 * to use within scripts.
0285 */
0286 void initializeCore(QScriptEngine *engine)
0287 {
0288     QScriptValue global = engine->globalObject();
0289 
0290     // compatibility to kjs/kjsembed
0291     if (! global.property("println").isValid()) {
0292         global.setProperty("println", global.property("print"));
0293     }
0294 
0295     // register common used types
0296     qScriptRegisterMetaType< QByteArray         >(engine, toByteArray,   fromByteArray);
0297     qScriptRegisterMetaType< QUrl               >(engine, toUrl,         fromUrl);
0298     qScriptRegisterMetaType< QColor             >(engine, toColor,       fromColor);
0299     qScriptRegisterMetaType< QRect              >(engine, toRect,        fromRect);
0300     qScriptRegisterMetaType< QRectF             >(engine, toRectF,       fromRectF);
0301     qScriptRegisterMetaType< QPoint             >(engine, toPoint,       fromPoint);
0302     qScriptRegisterMetaType< QPointF            >(engine, toPointF,      fromPointF);
0303     qScriptRegisterMetaType< QSize              >(engine, toSize,        fromSize);
0304     qScriptRegisterMetaType< QSizeF             >(engine, toSizeF,       fromSizeF);
0305 
0306     // we should probably go with an own wrapper for QVariant/QObject...
0307     //qScriptRegisterMetaType< QVariant           >(engine, toVariant,     fromVariant);
0308     //qScriptRegisterMetaType< QVariantList       >(engine, toVariantList, fromVariantList);
0309 
0310     // register the Kross::Object::Ptr wrapper
0311     qScriptRegisterMetaType< Kross::Object::Ptr >(engine, toObjPtr,      fromObjPtr);
0312 
0313     // register the include function that allows to importExtension
0314     global.setProperty("include", engine->newFunction(includeFunction));
0315 }
0316 
0317 /**
0318 * Initialize GUI functionality like widgets the QUiLoader provides
0319 * and some layout-managers.
0320 */
0321 void initializeGui(QScriptEngine *engine)
0322 {
0323     QScriptValue global = engine->globalObject();
0324 
0325     // register UI widgets
0326     QUiLoader loader;
0327     foreach (const QString &widgetname, loader.availableWidgets()) {
0328         QScriptValue proto = engine->newObject();
0329         proto.setProperty("className", QScriptValue(engine, widgetname));
0330         QScriptValue func = engine->newFunction(createWidget);
0331         func.setPrototype(proto);
0332         global.setProperty(widgetname, func);
0333     }
0334 
0335     // register layouts
0336     global.setProperty("QVBoxLayout", engine->newFunction(createVBoxLayout));
0337     global.setProperty("QHBoxLayout", engine->newFunction(createHBoxLayout));
0338     global.setProperty("QGridLayout", engine->newFunction(createGridLayout));
0339 }
0340 
0341 }