class library: PriorityQueue - implement removeValue, hide array
[supercollider.git] / QtCollider / primitives / prim_QObject.cpp
blob4a2d8e6e4391217141b592cf1284bf41813ff3dd
1 /************************************************************************
3 * Copyright 2010 Jakob Leben (jakob.leben@gmail.com)
5 * This file is part of SuperCollider Qt GUI.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ************************************************************************/
22 #include "primitives.h"
23 #include "../QObjectProxy.h"
24 #include "../QcObjectFactory.h"
25 #include "../QcApplication.h"
26 #include "../Common.h"
27 #include "../Slot.h"
29 #include <PyrObject.h>
30 #include <PyrKernel.h>
31 #include <GC.h>
32 #include <SCBase.h>
34 #include <QMetaObject>
35 #include <QMetaProperty>
36 #include <QMetaMethod>
38 #define IS_OBJECT_NIL( a ) \
39 IsNil( slotRawObject(a)->slots )
41 #define QOBJECT_FROM_SLOT( s ) \
42 ((QObjectProxy*) slotRawPtr( slotRawObject( s )->slots ))
44 #define CLASS_NAME( slot ) \
45 slotRawSymbol( &slotRawObject( slot )->classptr->name )->name
47 using namespace QtCollider;
49 int QObject_Finalize( struct VMGlobals *, struct PyrObject * );
51 QC_LANG_PRIMITIVE( QObject_New, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
53 PyrObject *scObject = slotRawObject( r );
54 QString qtClassName = Slot::toString( a+0 );
56 qcSCObjectDebugMsg( 1, scObject, QString("CREATE: %2").arg(qtClassName) );
58 if( !QcApplication::compareThread() ) return QtCollider::wrongThreadError();
60 QcAbstractFactory *f = QtCollider::factories().value( qtClassName );
62 if( !f ) {
63 qcErrorMsg( QString("Factory for class '%1' not found!").arg(qtClassName) );
64 return errFailed;
67 QObjectProxy *proxy = 0;
69 Variant arg[10];
71 PyrSlot *slotArg = a+1;
73 if( isKindOfSlot( slotArg, class_Array ) ) {
74 PyrObject *array = slotRawObject( slotArg );
75 PyrSlot *s = array->slots;
76 int size = array->size;
77 for( int i = 0; i<size && i<10; ++i, ++s ) {
78 arg[i].setData( s );
81 else {
82 arg[0].setData( slotArg );
85 proxy = f->newInstance( scObject, arg );
86 if( !proxy ) return errFailed;
88 SetPtr( scObject->slots, proxy );
90 InstallFinalizer( g, scObject, 1, QObject_Finalize );
92 return errNone;
95 QC_LANG_PRIMITIVE( QObject_Destroy, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
97 qcSCObjectDebugMsg( 1, slotRawObject(r), "DESTROY" );
99 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
101 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
103 // Post the destruction event asynchronously;
104 DestroyEvent *e = new DestroyEvent( QObjectProxy::DestroyObject );
105 QApplication::postEvent( proxy, e );
107 return errNone;
110 int QObject_Finalize( struct VMGlobals *, struct PyrObject *obj )
112 qcSCObjectDebugMsg( 1, obj, "FINALIZE" );
114 QObjectProxy *proxy = (QObjectProxy*) slotRawPtr( obj->slots );
116 // Invalidate proxy's SC object pointer directly.
117 // Note that it is protected by language mutex;
118 proxy->finalize();
120 // Post the destruction event asynchronously;
121 DestroyEvent *e = new DestroyEvent( QObjectProxy::DestroyProxyAndObject );
122 QApplication::postEvent( proxy, e );
124 return errNone;
127 QC_LANG_PRIMITIVE( QObject_ManuallyFinalize, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
129 qcSCObjectDebugMsg( 1, slotRawObject(r), "MANUAL FINALIZE" );
131 // WARNING we assume that proxy's deletion will be deferred until any
132 // language shutdown code using it will have been executed, so any
133 // shutdown code is safe.
135 return QObject_Finalize( g, slotRawObject(r) );
138 QC_LANG_PRIMITIVE( QObject_SetParent, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
140 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
142 QObjectProxy *parent = Slot::toObjectProxy( a );
143 if( !parent ) return errWrongType;
145 qcSCObjectDebugMsg( 1, slotRawObject(r), "SET PARENT" );
147 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
149 bool ok = proxy->setParent( parent );
151 return ok ? errNone : errFailed;
154 static void qcGetProperties( const QMetaObject *mo, PyrSlot *r, VMGlobals *g )
156 int count = mo->propertyCount();
158 PyrObject *array = newPyrArray( g->gc, count, 0, true );
159 SetObject( r, array );
161 PyrSlot *s = array->slots;
162 for( int i = 0; i < count; ++i, ++s ) {
163 SetSymbol( s, getsym( mo->property(i).name() ) );
164 array->size++;
168 static void qcGetMethods (
169 const QMetaObject *mo,
170 bool getPlain, bool getSignals, bool getSlots,
171 PyrSlot *r, VMGlobals *g )
173 int count = mo->methodCount();
175 PyrObject *array = newPyrArray( g->gc, count, 0, true );
176 SetObject( r, array );
178 PyrSlot *s = array->slots;
179 for( int i = 0; i < count; ++i ) {
180 QMetaMethod method = mo->method(i);
181 switch( method.methodType() ) {
182 case QMetaMethod::Method:
183 if( !getPlain || (method.access() != QMetaMethod::Public) ) continue;
184 break;
185 case QMetaMethod::Signal:
186 if( !getSignals ) continue;
187 break;
188 case QMetaMethod::Slot:
189 if( !getSlots || (method.access() != QMetaMethod::Public) ) continue;
190 break;
191 default:
192 continue;
194 Slot::setString( s, QString::fromLatin1( method.signature() ) );
195 array->size++;
196 g->gc->GCWrite( array, s );
197 ++s;
201 QC_LANG_PRIMITIVE( QMetaObject_Properties, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
203 if( !QcApplication::compareThread() ) return QtCollider::wrongThreadError();
205 QString className = Slot::toString( slotRawObject(r)->slots+0 );
207 QcAbstractFactory *f = QtCollider::factories().value( className );
209 if( !f ) {
210 qcErrorMsg( QString("Factory for class '%1' not found!").arg(className) );
211 return errFailed;
214 const QMetaObject *mo = f->metaObject();
215 qcGetProperties( mo, r, g );
217 return errNone;
220 QC_LANG_PRIMITIVE( QMetaObject_Methods, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
222 if( !QcApplication::compareThread() ) return QtCollider::wrongThreadError();
224 QString className = Slot::toString(slotRawObject(r)->slots+0);
226 QcAbstractFactory *f = QtCollider::factories().value( className );
228 if( !f ) {
229 qcErrorMsg( QString("Factory for class '%1' not found!").arg(className) );
230 return errFailed;
233 bool getPlain = IsTrue(a+0);
234 bool getSignals = IsTrue(a+1);
235 bool getSlots = IsTrue(a+2);
237 const QMetaObject *mo = f->metaObject();
238 qcGetMethods( mo, getPlain, getSignals, getSlots, r, g );
240 return errNone;
243 QC_LANG_PRIMITIVE( QObject_GetProperties, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
245 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
246 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
248 QObject *obj = proxy->object();
249 if( !obj ) {
250 SetNil(r);
251 return errNone;
254 const QMetaObject *mo = obj->metaObject();
255 qcGetProperties( mo, r, g );
257 return errNone;
260 QC_LANG_PRIMITIVE( QObject_GetMethods, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
262 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
263 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
265 QObject *obj = proxy->object();
266 if( !obj ) {
267 SetNil(r);
268 return errNone;
271 bool getPlain = IsTrue(a+0);
272 bool getSignals = IsTrue(a+1);
273 bool getSlots = IsTrue(a+2);
275 const QMetaObject *mo = obj->metaObject();
276 qcGetMethods( mo, getPlain, getSignals, getSlots, r, g );
278 return errNone;
281 QC_LANG_PRIMITIVE( QObject_SetProperty, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
283 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
285 if( NotSym( a ) ) return errWrongType;
286 PyrSymbol *property = slotRawSymbol( a );
287 bool sync = IsTrue( a+2 );
289 qcSCObjectDebugMsg( 1, slotRawObject(r), QString("SET: %1").arg(property->name) );
291 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
293 QVariant val = Slot::toVariant( a+1 );
295 if( sync ) {
296 proxy->setProperty( property->name, val );
297 } else {
298 SetPropertyEvent *e = new SetPropertyEvent();
299 e->property = property;
300 e->value = val;
301 QApplication::postEvent( proxy, e );
304 return errNone;
307 QC_LANG_PRIMITIVE( QObject_GetProperty, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
309 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
311 if( NotSym(a) ) return errWrongType;
312 PyrSymbol *symProp = slotRawSymbol( a );
314 qcSCObjectDebugMsg( 1, slotRawObject(r), QString("GET: %1").arg(symProp->name) );
316 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
318 QVariant val = proxy->property( symProp->name );
319 if( !val.isValid() ) return errFailed;
321 if( Slot::setVariant(r, val) )
322 return errNone;
323 else
324 return errFailed;
327 QC_LANG_PRIMITIVE( QObject_SetEventHandler, 4, PyrSlot *r, PyrSlot *a, VMGlobals *g )
329 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
331 if( NotInt( a+0 ) || NotSym( a+1 ) ) return errWrongType;
332 int eventType = Slot::toInt( a+0 );
333 PyrSymbol *method = 0; slotSymbolVal( a+1, &method );
334 Synchronicity sync = IsTrue( a+2 ) ? Synchronous : Asynchronous;
335 bool enabled = IsTrue( a+3 );
337 qcSCObjectDebugMsg( 1, slotRawObject(r),
338 QString("SET EVENT HANDLER: type %1 -> %2 [%3, %4]")
339 .arg(eventType).arg(method->name)
340 .arg(sync == Synchronous ? "SYNC" : "ASYNC")
341 .arg(enabled ? "on" : "off") );
343 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
345 bool ok = proxy->setEventHandler( eventType, method, sync, enabled );
347 return ok ? errNone : errFailed;
350 QC_LANG_PRIMITIVE( QObject_SetEventHandlerEnabled, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
352 if( NotInt( a+0 ) ) return errWrongType;
353 bool enabled = IsTrue( a+1 );
354 if( !enabled && !IsFalse( a+1 ) ) return errWrongType;
355 int type = Slot::toInt( a+0 );
357 qcSCObjectDebugMsg( 1, slotRawObject(r),
358 QString("SET EVENT HANDLER STATE: type %1 = %2")
359 .arg(type).arg(enabled ? "on" : "off") );
361 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
363 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
365 bool ok = proxy->setEventHandlerEnabled( type, enabled );
367 return ok ? errNone : errFailed;
370 QC_LANG_PRIMITIVE( QObject_ConnectMethod, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
372 QString signal = Slot::toString( a+0 );
373 if( signal.isEmpty() || NotSym( a+1 ) ) return errWrongType;
374 PyrSymbol *handler = 0; slotSymbolVal( a+1, &handler );
375 Qt::ConnectionType ctype =
376 Slot::toBool( a+2 ) ? Qt::DirectConnection : Qt::QueuedConnection;
378 qcSCObjectDebugMsg( 1, slotRawObject(r),
379 QString("CONNECT METHOD: %1 -> %2 [%3]").arg(signal).arg(handler->name)
380 .arg( IsTrue(a+2) ? "SYNC" : "ASYNC") );
382 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
384 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
386 bool ok = proxy->connectMethod( signal.toAscii().constData(), handler, ctype );
388 return ok ? errNone : errFailed;
391 QC_LANG_PRIMITIVE( QObject_DisconnectMethod, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
393 QString signal = Slot::toString( a+0 );
394 if( signal.isEmpty() || NotSym( a+1 ) ) return errWrongType;
395 PyrSymbol *handler = 0; slotSymbolVal( a+1, &handler );
397 qcSCObjectDebugMsg( 1, slotRawObject(r),
398 QString("DISCONNECT METHOD: %1 -> %2").arg(signal).arg(handler->name) );
400 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
402 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
404 bool ok = proxy->disconnectMethod( signal.toAscii().constData(), handler );
406 return ok ? errNone : errFailed;
409 QC_LANG_PRIMITIVE( QObject_ConnectObject, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
411 QString signal = Slot::toString( a+0 );
412 if( signal.isEmpty() || NotObj( a+1 ) ) return errWrongType;
413 PyrObject *handlerObj = slotRawObject( a+1 );
414 Qt::ConnectionType ctype =
415 Slot::toBool( a+2 ) ? Qt::DirectConnection : Qt::QueuedConnection;
417 qcSCObjectDebugMsg( 1, slotRawObject(r),
418 QString("CONNECT OBJECT: %1 -> %2 [%3]")
419 .arg(signal)
420 .arg( slotRawSymbol( &handlerObj->classptr->name )->name )
421 .arg( IsTrue(a+2) ? "SYNC" : "ASYNC") );
423 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
425 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
427 bool ok = proxy->connectObject( signal.toAscii().constData(), handlerObj, ctype );
429 return ok ? errNone : errFailed;
432 QC_LANG_PRIMITIVE( QObject_DisconnectObject, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
434 QString signal = Slot::toString( a+0 );
435 if( signal.isEmpty() || NotObj( a+1 ) ) return errWrongType;
436 PyrObject *handlerObj = slotRawObject( a+1 );
438 qcSCObjectDebugMsg( 1, slotRawObject(r),
439 QString("DISCONNECT OBJECT: %1").arg(signal) );
441 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
443 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
445 bool ok = proxy->disconnectObject( signal.toAscii().constData(), handlerObj );
447 return ok ? errNone : errFailed;
450 QC_LANG_PRIMITIVE( QObject_ConnectSlot, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
452 // Args: signal, receiver, slot
453 if( !isKindOfSlot( a+1, class_QObject )
454 || NotSym( a+0 ) || NotSym( a+2 ) ) return errWrongType;
456 PyrSymbol *symSig = slotRawSymbol( a+0 );
457 PyrSymbol *symSlot = slotRawSymbol( a+2 );
458 QObjectProxy *sndProxy = QOBJECT_FROM_SLOT( r );
459 QObjectProxy *rcvProxy = QOBJECT_FROM_SLOT( a+1 );
461 qcSCObjectDebugMsg( 1, slotRawObject(r),
462 QString("CONNECT TO SLOT: %1 -> %2").arg(symSig->name).arg(symSlot->name) );
464 QString strSig = QString("2") + symSig->name;
465 QString strSlot = QString("1") + symSlot->name;
467 sndProxy->lock();
468 rcvProxy->lock();
469 bool ok;
470 if( !sndProxy->object() || !rcvProxy->object() ) {
471 ok = true;
473 else {
474 ok = QObject::connect( sndProxy->object(), strSig.toStdString().c_str(),
475 rcvProxy->object(), strSlot.toStdString().c_str() );
477 sndProxy->unlock();
478 rcvProxy->unlock();
480 if (!ok) {
481 qcErrorMsg( QString("Failed to connect %1::%2 to %3::%4!\n")
482 .arg(sndProxy->scClassName()).arg(symSig->name)
483 .arg(rcvProxy->scClassName()).arg(symSlot->name)
485 return errFailed;
488 return errNone;
491 QC_LANG_PRIMITIVE( QObject_InvokeMethod, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
493 if( NotSym( a+0 ) ) return errWrongType;
495 PyrSymbol *method = slotRawSymbol( a+0 );
496 PyrSlot *methodArgs = a+1;
497 bool sync = !IsFalse( a+2 );
499 qcSCObjectDebugMsg( 1, slotRawObject(r),
500 QString("INVOKE: '%1' [%2]").arg(method->name).arg(sync ? "SYNC" : "ASYNC") );
502 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
504 PyrSlot *retSlot;
505 Qt::ConnectionType cType;
507 if( sync ) {
508 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
509 retSlot = r;
510 cType = Qt::DirectConnection;
512 else {
513 retSlot = 0;
514 cType = Qt::QueuedConnection;
517 bool ok = proxy->invokeMethod( method->name, retSlot, methodArgs, cType );
519 return ok ? errNone : errFailed;
522 QC_LANG_PRIMITIVE( QObject_IsValid, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
524 // NOTE Though we could check validity without posting an event (by locking the proxy),
525 // we post a sync event to maximize the chance that a DeferredDelete event issued earlier
526 // can get processed by the object in question before it is checked for validity.
528 QObjectProxy *proxy = Slot::toObjectProxy( r );
530 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
532 SetBool( r, proxy->object() != 0 );
534 return errNone;
537 QC_LANG_PRIMITIVE( QObject_GetChildren, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
539 if( NotSym(a) && NotNil(a) ) return errWrongType;
541 QObjectProxy *proxy = Slot::toObjectProxy( r );
542 PyrSymbol *className = IsSym( a ) ? slotRawSymbol( a ) : 0;
544 qcSCObjectDebugMsg( 1, slotRawObject(r),
545 QString("GET CHILDREN: of class '%1'")
546 .arg( className ? className->name : "QObject" ) );
548 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
550 QList<PyrObject*> children = proxy->children( className );
552 int count = children.count();
553 PyrObject *array = newPyrArray( g->gc, count, 0, true );
554 SetObject( r, array );
556 PyrSlot *s = array->slots;
557 Q_FOREACH( PyrObject *obj, children ) {
558 SetObject( s, obj );
559 ++array->size;
560 g->gc->GCWrite( array, s );
561 ++s;
564 return errNone;
567 QC_LANG_PRIMITIVE( QObject_GetParent, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
569 if( NotSym(a) && NotNil(a) ) return errWrongType;
571 QObjectProxy *proxy = Slot::toObjectProxy( r );
572 PyrSymbol *className = IsSym( a ) ? slotRawSymbol( a ) : 0;
574 qcSCObjectDebugMsg( 1, slotRawObject(r), QString("GET PARENT") );
576 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
578 PyrObject *parent = proxy->parent( className );
580 if( parent ) SetObject( r, parent );
581 else SetNil( r );
583 return errNone;