/************************************************************************
*
* Copyright 2010-2012 Jakob Leben (jakob.leben@gmail.com)
*
* This file is part of SuperCollider Qt GUI.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
************************************************************************/
#include "primitives.h"
#include "../QObjectProxy.h"
#include "../QcObjectFactory.h"
#include "../QcApplication.h"
#include "../Common.h"
#include "../type_codec.hpp"
#include "../metatype.hpp"
#include "../painting.h"
#include
#include
#include
#include
#include
#include
#include
#if defined _WIN32
# include "SC_Win32Utils.h"
#elif defined __FreeBSD__
# include
#else
# include
#endif
#define IS_OBJECT_NIL(a) IsNil(slotRawObject(a)->slots)
#define QOBJECT_FROM_SLOT(s) ((QObjectProxy*)slotRawPtr(slotRawObject(s)->slots))
#define CLASS_NAME(slot) slotRawSymbol(&slotRawObject(slot)->classptr->name)->name
namespace QtCollider {
int QObject_Finalize(struct VMGlobals*, struct PyrObject*);
QC_LANG_PRIMITIVE(QObject_New, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (NotSym(a + 0))
return errWrongType;
PyrObject* scObject = slotRawObject(r);
QString qtClassName = QString(slotRawSymbol(a + 0)->name);
qcSCObjectDebugMsg(1, scObject, QStringLiteral("CREATE: %2").arg(qtClassName));
if (!QcApplication::compareThread())
return QtCollider::wrongThreadError();
QcAbstractFactory* f = QtCollider::factories().value(qtClassName);
if (!f) {
qcErrorMsg(QStringLiteral("Factory for class '%1' not found!").arg(qtClassName));
return errFailed;
}
QObjectProxy* proxy = 0;
MetaValue args[10];
PyrSlot* argSlot = a + 1;
int argc;
if (isKindOfSlot(argSlot, class_array)) {
PyrObject* array = slotRawObject(argSlot);
argSlot = array->slots;
argc = array->size;
} else {
argc = 1;
}
for (int i = 0; i < argc && i < 10; ++i) {
PyrSlot* slot = argSlot + i;
MetaType* type = MetaType::find(slot);
if (type) {
void* mem = alloca(type->size());
Q_ASSERT(mem);
args[i].read(mem, type, slot);
}
}
proxy = f->newInstance(scObject, args);
if (!proxy)
return errFailed;
SetPtr(scObject->slots, proxy);
InstallFinalizer(g, scObject, 1, QObject_Finalize);
return errNone;
}
QC_LANG_PRIMITIVE(QObject_Destroy, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
qcSCObjectDebugMsg(1, slotRawObject(r), "DESTROY");
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
// Post the destruction event asynchronously;
DestroyEvent* e = new DestroyEvent(QObjectProxy::DestroyObject);
QApplication::postEvent(proxy, e);
return errNone;
}
int QObject_Finalize(struct VMGlobals*, struct PyrObject* obj) {
qcSCObjectDebugMsg(1, obj, "FINALIZE");
QObjectProxy* proxy = (QObjectProxy*)slotRawPtr(obj->slots);
// Invalidate proxy's SC object pointer directly.
// Note that it is protected by language mutex;
proxy->finalize();
// Post the destruction event asynchronously;
DestroyEvent* e = new DestroyEvent(QObjectProxy::DestroyProxyAndObject);
QApplication::postEvent(proxy, e);
return errNone;
}
QC_LANG_PRIMITIVE(QObject_SetParent, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
QObjectProxy* parent = QtCollider::get(a);
if (!parent)
return errWrongType;
qcSCObjectDebugMsg(1, slotRawObject(r), "SET PARENT");
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
bool ok = proxy->setParent(parent);
return ok ? errNone : errFailed;
}
static void qcGetProperties(const QMetaObject* mo, PyrSlot* r, VMGlobals* g) {
int count = mo->propertyCount();
PyrObject* array = newPyrArray(g->gc, count, 0, true);
SetObject(r, array);
PyrSlot* s = array->slots;
for (int i = 0; i < count; ++i, ++s) {
SetSymbol(s, getsym(mo->property(i).name()));
array->size++;
}
}
static void qcGetMethods(const QMetaObject* mo, bool getPlain, bool getSignals, bool getSlots, PyrSlot* r,
VMGlobals* g) {
int count = mo->methodCount();
PyrObject* array = newPyrArray(g->gc, count, 0, true);
SetObject(r, array);
PyrSlot* s = array->slots;
for (int i = 0; i < count; ++i) {
QMetaMethod method = mo->method(i);
switch (method.methodType()) {
case QMetaMethod::Method:
if (!getPlain || (method.access() != QMetaMethod::Public))
continue;
break;
case QMetaMethod::Signal:
if (!getSignals)
continue;
break;
case QMetaMethod::Slot:
if (!getSlots || (method.access() != QMetaMethod::Public))
continue;
break;
default:
continue;
}
QtCollider::set(s, QString::fromLatin1(method.methodSignature()));
array->size++;
g->gc->GCWrite(array, s);
++s;
}
}
QC_LANG_PRIMITIVE(QMetaObject_Properties, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (!QcApplication::compareThread())
return QtCollider::wrongThreadError();
PyrSlot* sClassName = slotRawObject(r)->slots + 0;
if (NotSym(sClassName))
return errWrongType;
QString className(slotRawSymbol(sClassName)->name);
QcAbstractFactory* f = QtCollider::factories().value(className);
if (!f) {
qcErrorMsg(QStringLiteral("Factory for class '%1' not found!").arg(className));
return errFailed;
}
const QMetaObject* mo = f->metaObject();
qcGetProperties(mo, r, g);
return errNone;
}
QC_LANG_PRIMITIVE(QMetaObject_Methods, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (!QcApplication::compareThread())
return QtCollider::wrongThreadError();
PyrSlot* sClassName = slotRawObject(r)->slots + 0;
if (NotSym(sClassName))
return errWrongType;
QString className(slotRawSymbol(sClassName)->name);
QcAbstractFactory* f = QtCollider::factories().value(className);
if (!f) {
qcErrorMsg(QStringLiteral("Factory for class '%1' not found!").arg(className));
return errFailed;
}
bool getPlain = IsTrue(a + 0);
bool getSignals = IsTrue(a + 1);
bool getSlots = IsTrue(a + 2);
const QMetaObject* mo = f->metaObject();
qcGetMethods(mo, getPlain, getSignals, getSlots, r, g);
return errNone;
}
QC_LANG_PRIMITIVE(QObject_GetProperties, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
QObject* obj = proxy->object();
if (!obj) {
SetNil(r);
return errNone;
}
const QMetaObject* mo = obj->metaObject();
qcGetProperties(mo, r, g);
return errNone;
}
QC_LANG_PRIMITIVE(QObject_GetMethods, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
QObject* obj = proxy->object();
if (!obj) {
SetNil(r);
return errNone;
}
bool getPlain = IsTrue(a + 0);
bool getSignals = IsTrue(a + 1);
bool getSlots = IsTrue(a + 2);
const QMetaObject* mo = obj->metaObject();
qcGetMethods(mo, getPlain, getSignals, getSlots, r, g);
return errNone;
}
QC_LANG_PRIMITIVE(QObject_SetProperty, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (NotSym(a))
return errWrongType;
PyrSymbol* property = slotRawSymbol(a);
bool sync = IsTrue(a + 2);
qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("SET: %1").arg(property->name));
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
QVariant val = QtCollider::get(a + 1);
if (sync && !QtCollider::isPaintingObject(proxy)) {
proxy->setProperty(property->name, val);
} else {
SetPropertyEvent* e = new SetPropertyEvent();
e->property = property;
e->value = val;
QApplication::postEvent(proxy, e);
}
return errNone;
}
QC_LANG_PRIMITIVE(QObject_GetProperty, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (NotSym(a))
return errWrongType;
PyrSymbol* symProp = slotRawSymbol(a);
qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("GET: %1").arg(symProp->name));
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
QVariant val = proxy->property(symProp->name);
if (!val.isValid()) {
qcDebugMsg(1, QStringLiteral("WARNING: Invalid property '%1'").arg(symProp->name));
SetNil(r);
return errNone;
}
if (QtCollider::set(r, val))
return errNone;
else
return errFailed;
}
QC_LANG_PRIMITIVE(QObject_SetEventHandler, 4, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (NotInt(a + 0) || NotSym(a + 1))
return errWrongType;
int eventType = QtCollider::get(a + 0);
PyrSymbol* method = 0;
slotSymbolVal(a + 1, &method);
Synchronicity sync = IsTrue(a + 2) ? Synchronous : Asynchronous;
bool enabled = IsTrue(a + 3);
qcSCObjectDebugMsg(1, slotRawObject(r),
QStringLiteral("SET EVENT HANDLER: type %1 -> %2 [%3, %4]")
.arg(eventType)
.arg(method->name)
.arg(sync == Synchronous ? "SYNC" : "ASYNC")
.arg(enabled ? "on" : "off"));
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
bool ok = proxy->setEventHandler(eventType, method, sync, enabled);
return ok ? errNone : errFailed;
}
QC_LANG_PRIMITIVE(QObject_SetEventHandlerEnabled, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (NotInt(a + 0))
return errWrongType;
bool enabled = IsTrue(a + 1);
if (!enabled && !IsFalse(a + 1))
return errWrongType;
int type = QtCollider::get(a + 0);
qcSCObjectDebugMsg(1, slotRawObject(r),
QStringLiteral("SET EVENT HANDLER STATE: type %1 = %2").arg(type).arg(enabled ? "on" : "off"));
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
bool ok = proxy->setEventHandlerEnabled(type, enabled);
return ok ? errNone : errFailed;
}
QC_LANG_PRIMITIVE(QObject_ConnectMethod, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (NotSym(a + 0) || NotSym(a + 1))
return errWrongType;
PyrSymbol* signal = slotRawSymbol(a + 0);
PyrSymbol* handler = slotRawSymbol(a + 1);
Qt::ConnectionType ctype = IsTrue(a + 2) ? Qt::DirectConnection : Qt::QueuedConnection;
qcSCObjectDebugMsg(1, slotRawObject(r),
QStringLiteral("CONNECT METHOD: %1 -> %2 [%3]")
.arg(signal->name)
.arg(handler->name)
.arg(IsTrue(a + 2) ? "SYNC" : "ASYNC"));
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
bool ok = proxy->connectMethod(signal->name, handler, ctype);
return ok ? errNone : errFailed;
}
QC_LANG_PRIMITIVE(QObject_DisconnectMethod, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (NotSym(a + 0) || NotSym(a + 1))
return errWrongType;
PyrSymbol* signal = slotRawSymbol(a + 0);
PyrSymbol* handler = slotRawSymbol(a + 1);
qcSCObjectDebugMsg(1, slotRawObject(r),
QStringLiteral("DISCONNECT METHOD: %1 -> %2").arg(signal->name).arg(handler->name));
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
bool ok = proxy->disconnectMethod(signal->name, handler);
return ok ? errNone : errFailed;
}
QC_LANG_PRIMITIVE(QObject_ConnectObject, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (NotSym(a + 0) || NotObj(a + 1))
return errWrongType;
PyrSymbol* signal = slotRawSymbol(a + 0);
PyrObject* handlerObj = slotRawObject(a + 1);
Qt::ConnectionType ctype = IsTrue(a + 2) ? Qt::DirectConnection : Qt::QueuedConnection;
qcSCObjectDebugMsg(1, slotRawObject(r),
QStringLiteral("CONNECT OBJECT: %1 -> %2 [%3]")
.arg(signal->name)
.arg(slotRawSymbol(&handlerObj->classptr->name)->name)
.arg(IsTrue(a + 2) ? "SYNC" : "ASYNC"));
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
bool ok = proxy->connectObject(signal->name, handlerObj, ctype);
return ok ? errNone : errFailed;
}
QC_LANG_PRIMITIVE(QObject_DisconnectObject, 2, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (NotSym(a + 0) || NotObj(a + 1))
return errWrongType;
PyrSymbol* signal = slotRawSymbol(a + 0);
PyrObject* handlerObj = slotRawObject(a + 1);
qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("DISCONNECT OBJECT: %1").arg(signal->name));
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
bool ok = proxy->disconnectObject(signal->name, handlerObj);
return ok ? errNone : errFailed;
}
QC_LANG_PRIMITIVE(QObject_ConnectSlot, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
// Args: signal, receiver, slot
if (!isKindOfSlot(a + 1, SC_CLASS(QObject)) || NotSym(a + 0) || NotSym(a + 2))
return errWrongType;
PyrSymbol* symSig = slotRawSymbol(a + 0);
PyrSymbol* symSlot = slotRawSymbol(a + 2);
QObjectProxy* sndProxy = QOBJECT_FROM_SLOT(r);
QObjectProxy* rcvProxy = QOBJECT_FROM_SLOT(a + 1);
qcSCObjectDebugMsg(1, slotRawObject(r),
QStringLiteral("CONNECT TO SLOT: %1 -> %2").arg(symSig->name).arg(symSlot->name));
QString strSig = QStringLiteral("2") + symSig->name;
QString strSlot = QStringLiteral("1") + symSlot->name;
sndProxy->lock();
rcvProxy->lock();
bool ok;
if (!sndProxy->object() || !rcvProxy->object()) {
ok = true;
} else {
ok = QObject::connect(sndProxy->object(), strSig.toStdString().c_str(), rcvProxy->object(),
strSlot.toStdString().c_str());
}
sndProxy->unlock();
rcvProxy->unlock();
if (!ok) {
qcErrorMsg(QStringLiteral("Failed to connect %1::%2 to %3::%4!\n")
.arg(sndProxy->scClassName())
.arg(symSig->name)
.arg(rcvProxy->scClassName())
.arg(symSlot->name));
return errFailed;
}
return errNone;
}
QC_LANG_PRIMITIVE(QObject_InvokeMethod, 3, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (NotSym(a + 0))
return errWrongType;
PyrSymbol* method = slotRawSymbol(a + 0);
PyrSlot* methodArgs = a + 1;
bool sync = !IsFalse(a + 2);
qcSCObjectDebugMsg(1, slotRawObject(r),
QStringLiteral("INVOKE: '%1' [%2]").arg(method->name).arg(sync ? "SYNC" : "ASYNC"));
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
PyrSlot* retSlot;
Qt::ConnectionType cType;
if (sync) {
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
retSlot = r;
cType = Qt::DirectConnection;
} else {
retSlot = 0;
cType = Qt::QueuedConnection;
}
bool ok = proxy->invokeMethod(method->name, retSlot, methodArgs, cType);
return ok ? errNone : errFailed;
}
QC_LANG_PRIMITIVE(QObject_IsValid, 0, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
bool needLock = !proxy->compareThread();
if (needLock)
proxy->lock();
bool hasObject = proxy->object() != 0;
if (needLock)
proxy->unlock();
SetBool(r, hasObject);
return errNone;
}
QC_LANG_PRIMITIVE(QObject_GetChildren, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (NotSym(a) && NotNil(a))
return errWrongType;
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
PyrSymbol* className = IsSym(a) ? slotRawSymbol(a) : 0;
qcSCObjectDebugMsg(1, slotRawObject(r),
QStringLiteral("GET CHILDREN: of class '%1'").arg(className ? className->name : "QObject"));
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
QList children = proxy->children(className);
int count = children.count();
PyrObject* array = newPyrArray(g->gc, count, 0, true);
SetObject(r, array);
PyrSlot* s = array->slots;
Q_FOREACH (PyrObject* obj, children) {
SetObject(s, obj);
++array->size;
g->gc->GCWrite(array, s);
++s;
}
return errNone;
}
QC_LANG_PRIMITIVE(QObject_GetParent, 1, PyrSlot* r, PyrSlot* a, VMGlobals* g) {
if (NotSym(a) && NotNil(a))
return errWrongType;
QObjectProxy* proxy = QOBJECT_FROM_SLOT(r);
PyrSymbol* className = IsSym(a) ? slotRawSymbol(a) : 0;
qcSCObjectDebugMsg(1, slotRawObject(r), QStringLiteral("GET PARENT"));
if (!proxy->compareThread())
return QtCollider::wrongThreadError();
PyrObject* parent = proxy->parent(className);
if (parent)
SetObject(r, parent);
else
SetNil(r);
return errNone;
}
void defineQObjectPrimitives() {
LangPrimitiveDefiner definer;
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
definer.define();
}
} // namespace QtCollider