"Post Window" -> "Post window" prevents it being seen as two separate
[supercollider.git] / QtCollider / primitives / prim_QObject.cpp
blob223bf893ba87db1e968319d3719f773b264c3019
1 /************************************************************************
3 * Copyright 2010-2012 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 namespace QtCollider {
49 int QObject_Finalize( struct VMGlobals *, struct PyrObject * );
51 QC_LANG_PRIMITIVE( QObject_New, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
53 if(NotSym(a+0)) return errWrongType;
55 PyrObject *scObject = slotRawObject( r );
56 QString qtClassName = QString(slotRawSymbol(a+0)->name);
58 qcSCObjectDebugMsg( 1, scObject, QString("CREATE: %2").arg(qtClassName) );
60 if( !QcApplication::compareThread() ) return QtCollider::wrongThreadError();
62 QcAbstractFactory *f = QtCollider::factories().value( qtClassName );
64 if( !f ) {
65 qcErrorMsg( QString("Factory for class '%1' not found!").arg(qtClassName) );
66 return errFailed;
69 QObjectProxy *proxy = 0;
71 Variant arg[10];
73 PyrSlot *slotArg = a+1;
75 if( isKindOfSlot( slotArg, class_array ) ) {
76 PyrObject *array = slotRawObject( slotArg );
77 PyrSlot *s = array->slots;
78 int size = array->size;
79 for( int i = 0; i<size && i<10; ++i, ++s ) {
80 arg[i].setData( s );
83 else {
84 arg[0].setData( slotArg );
87 proxy = f->newInstance( scObject, arg );
88 if( !proxy ) return errFailed;
90 SetPtr( scObject->slots, proxy );
92 InstallFinalizer( g, scObject, 1, QObject_Finalize );
94 return errNone;
97 QC_LANG_PRIMITIVE( QObject_Destroy, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
99 qcSCObjectDebugMsg( 1, slotRawObject(r), "DESTROY" );
101 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
103 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
105 // Post the destruction event asynchronously;
106 DestroyEvent *e = new DestroyEvent( QObjectProxy::DestroyObject );
107 QApplication::postEvent( proxy, e );
109 return errNone;
112 int QObject_Finalize( struct VMGlobals *, struct PyrObject *obj )
114 qcSCObjectDebugMsg( 1, obj, "FINALIZE" );
116 QObjectProxy *proxy = (QObjectProxy*) slotRawPtr( obj->slots );
118 // Invalidate proxy's SC object pointer directly.
119 // Note that it is protected by language mutex;
120 proxy->finalize();
122 // Post the destruction event asynchronously;
123 DestroyEvent *e = new DestroyEvent( QObjectProxy::DestroyProxyAndObject );
124 QApplication::postEvent( proxy, e );
126 return errNone;
129 QC_LANG_PRIMITIVE( QObject_SetParent, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
131 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
133 QObjectProxy *parent = Slot::toObjectProxy( a );
134 if( !parent ) return errWrongType;
136 qcSCObjectDebugMsg( 1, slotRawObject(r), "SET PARENT" );
138 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
140 bool ok = proxy->setParent( parent );
142 return ok ? errNone : errFailed;
145 static void qcGetProperties( const QMetaObject *mo, PyrSlot *r, VMGlobals *g )
147 int count = mo->propertyCount();
149 PyrObject *array = newPyrArray( g->gc, count, 0, true );
150 SetObject( r, array );
152 PyrSlot *s = array->slots;
153 for( int i = 0; i < count; ++i, ++s ) {
154 SetSymbol( s, getsym( mo->property(i).name() ) );
155 array->size++;
159 static void qcGetMethods (
160 const QMetaObject *mo,
161 bool getPlain, bool getSignals, bool getSlots,
162 PyrSlot *r, VMGlobals *g )
164 int count = mo->methodCount();
166 PyrObject *array = newPyrArray( g->gc, count, 0, true );
167 SetObject( r, array );
169 PyrSlot *s = array->slots;
170 for( int i = 0; i < count; ++i ) {
171 QMetaMethod method = mo->method(i);
172 switch( method.methodType() ) {
173 case QMetaMethod::Method:
174 if( !getPlain || (method.access() != QMetaMethod::Public) ) continue;
175 break;
176 case QMetaMethod::Signal:
177 if( !getSignals ) continue;
178 break;
179 case QMetaMethod::Slot:
180 if( !getSlots || (method.access() != QMetaMethod::Public) ) continue;
181 break;
182 default:
183 continue;
185 Slot::setString( s, QString::fromLatin1( method.signature() ) );
186 array->size++;
187 g->gc->GCWrite( array, s );
188 ++s;
192 QC_LANG_PRIMITIVE( QMetaObject_Properties, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
194 if( !QcApplication::compareThread() ) return QtCollider::wrongThreadError();
196 PyrSlot *sClassName = slotRawObject(r)->slots+0;
197 if(NotSym(sClassName)) return errWrongType;
198 QString className( slotRawSymbol(sClassName)->name );
200 QcAbstractFactory *f = QtCollider::factories().value( className );
202 if( !f ) {
203 qcErrorMsg( QString("Factory for class '%1' not found!").arg(className) );
204 return errFailed;
207 const QMetaObject *mo = f->metaObject();
208 qcGetProperties( mo, r, g );
210 return errNone;
213 QC_LANG_PRIMITIVE( QMetaObject_Methods, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
215 if( !QcApplication::compareThread() ) return QtCollider::wrongThreadError();
217 PyrSlot *sClassName = slotRawObject(r)->slots+0;
218 if(NotSym(sClassName)) return errWrongType;
219 QString className( slotRawSymbol(sClassName)->name );
221 QcAbstractFactory *f = QtCollider::factories().value( className );
223 if( !f ) {
224 qcErrorMsg( QString("Factory for class '%1' not found!").arg(className) );
225 return errFailed;
228 bool getPlain = IsTrue(a+0);
229 bool getSignals = IsTrue(a+1);
230 bool getSlots = IsTrue(a+2);
232 const QMetaObject *mo = f->metaObject();
233 qcGetMethods( mo, getPlain, getSignals, getSlots, r, g );
235 return errNone;
238 QC_LANG_PRIMITIVE( QObject_GetProperties, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
240 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
241 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
243 QObject *obj = proxy->object();
244 if( !obj ) {
245 SetNil(r);
246 return errNone;
249 const QMetaObject *mo = obj->metaObject();
250 qcGetProperties( mo, r, g );
252 return errNone;
255 QC_LANG_PRIMITIVE( QObject_GetMethods, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
257 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
258 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
260 QObject *obj = proxy->object();
261 if( !obj ) {
262 SetNil(r);
263 return errNone;
266 bool getPlain = IsTrue(a+0);
267 bool getSignals = IsTrue(a+1);
268 bool getSlots = IsTrue(a+2);
270 const QMetaObject *mo = obj->metaObject();
271 qcGetMethods( mo, getPlain, getSignals, getSlots, r, g );
273 return errNone;
276 QC_LANG_PRIMITIVE( QObject_SetProperty, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
278 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
280 if( NotSym( a ) ) return errWrongType;
281 PyrSymbol *property = slotRawSymbol( a );
282 bool sync = IsTrue( a+2 );
284 qcSCObjectDebugMsg( 1, slotRawObject(r), QString("SET: %1").arg(property->name) );
286 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
288 QVariant val = Slot::toVariant( a+1 );
290 if( sync ) {
291 proxy->setProperty( property->name, val );
292 } else {
293 SetPropertyEvent *e = new SetPropertyEvent();
294 e->property = property;
295 e->value = val;
296 QApplication::postEvent( proxy, e );
299 return errNone;
302 QC_LANG_PRIMITIVE( QObject_GetProperty, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
304 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
306 if( NotSym(a) ) return errWrongType;
307 PyrSymbol *symProp = slotRawSymbol( a );
309 qcSCObjectDebugMsg( 1, slotRawObject(r), QString("GET: %1").arg(symProp->name) );
311 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
313 QVariant val = proxy->property( symProp->name );
314 if( !val.isValid() ) {
315 qcDebugMsg(1, QString("WARNING: Invalid property '%1'").arg(symProp->name));
316 SetNil(r);
317 return errNone;
320 if( Slot::setVariant(r, val) )
321 return errNone;
322 else
323 return errFailed;
326 QC_LANG_PRIMITIVE( QObject_SetEventHandler, 4, PyrSlot *r, PyrSlot *a, VMGlobals *g )
328 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
330 if( NotInt( a+0 ) || NotSym( a+1 ) ) return errWrongType;
331 int eventType = Slot::toInt( a+0 );
332 PyrSymbol *method = 0; slotSymbolVal( a+1, &method );
333 Synchronicity sync = IsTrue( a+2 ) ? Synchronous : Asynchronous;
334 bool enabled = IsTrue( a+3 );
336 qcSCObjectDebugMsg( 1, slotRawObject(r),
337 QString("SET EVENT HANDLER: type %1 -> %2 [%3, %4]")
338 .arg(eventType).arg(method->name)
339 .arg(sync == Synchronous ? "SYNC" : "ASYNC")
340 .arg(enabled ? "on" : "off") );
342 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
344 bool ok = proxy->setEventHandler( eventType, method, sync, enabled );
346 return ok ? errNone : errFailed;
349 QC_LANG_PRIMITIVE( QObject_SetEventHandlerEnabled, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
351 if( NotInt( a+0 ) ) return errWrongType;
352 bool enabled = IsTrue( a+1 );
353 if( !enabled && !IsFalse( a+1 ) ) return errWrongType;
354 int type = Slot::toInt( a+0 );
356 qcSCObjectDebugMsg( 1, slotRawObject(r),
357 QString("SET EVENT HANDLER STATE: type %1 = %2")
358 .arg(type).arg(enabled ? "on" : "off") );
360 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
362 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
364 bool ok = proxy->setEventHandlerEnabled( type, enabled );
366 return ok ? errNone : errFailed;
369 QC_LANG_PRIMITIVE( QObject_ConnectMethod, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
371 if( NotSym(a+0) || NotSym( a+1 ) ) return errWrongType;
373 PyrSymbol *signal = slotRawSymbol(a+0);
374 PyrSymbol *handler = slotRawSymbol(a+1);
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->name).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->name, handler, ctype );
388 return ok ? errNone : errFailed;
391 QC_LANG_PRIMITIVE( QObject_DisconnectMethod, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
393 if( NotSym(a+0) || NotSym( a+1 ) ) return errWrongType;
395 PyrSymbol *signal = slotRawSymbol(a+0);
396 PyrSymbol *handler = slotRawSymbol(a+1);
398 qcSCObjectDebugMsg( 1, slotRawObject(r),
399 QString("DISCONNECT METHOD: %1 -> %2").arg(signal->name).arg(handler->name) );
401 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
403 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
405 bool ok = proxy->disconnectMethod( signal->name, handler );
407 return ok ? errNone : errFailed;
410 QC_LANG_PRIMITIVE( QObject_ConnectObject, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
412 if( NotSym(a+0) || NotObj( a+1 ) ) return errWrongType;
414 PyrSymbol *signal = slotRawSymbol(a+0);
415 PyrObject *handlerObj = slotRawObject( a+1 );
416 Qt::ConnectionType ctype =
417 Slot::toBool( a+2 ) ? Qt::DirectConnection : Qt::QueuedConnection;
419 qcSCObjectDebugMsg( 1, slotRawObject(r),
420 QString("CONNECT OBJECT: %1 -> %2 [%3]")
421 .arg( signal->name )
422 .arg( slotRawSymbol( &handlerObj->classptr->name )->name )
423 .arg( IsTrue(a+2) ? "SYNC" : "ASYNC") );
425 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
427 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
429 bool ok = proxy->connectObject( signal->name, handlerObj, ctype );
431 return ok ? errNone : errFailed;
434 QC_LANG_PRIMITIVE( QObject_DisconnectObject, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
436 if( NotSym(a+0) || NotObj( a+1 ) ) return errWrongType;
438 PyrSymbol *signal = slotRawSymbol(a+0);
439 PyrObject *handlerObj = slotRawObject( a+1 );
441 qcSCObjectDebugMsg( 1, slotRawObject(r),
442 QString("DISCONNECT OBJECT: %1").arg(signal->name) );
444 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
446 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
448 bool ok = proxy->disconnectObject( signal->name, handlerObj );
450 return ok ? errNone : errFailed;
453 QC_LANG_PRIMITIVE( QObject_ConnectSlot, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
455 // Args: signal, receiver, slot
456 if( !isKindOfSlot( a+1, SC_CLASS(QObject) )
457 || NotSym( a+0 ) || NotSym( a+2 ) ) return errWrongType;
459 PyrSymbol *symSig = slotRawSymbol( a+0 );
460 PyrSymbol *symSlot = slotRawSymbol( a+2 );
461 QObjectProxy *sndProxy = QOBJECT_FROM_SLOT( r );
462 QObjectProxy *rcvProxy = QOBJECT_FROM_SLOT( a+1 );
464 qcSCObjectDebugMsg( 1, slotRawObject(r),
465 QString("CONNECT TO SLOT: %1 -> %2").arg(symSig->name).arg(symSlot->name) );
467 QString strSig = QString("2") + symSig->name;
468 QString strSlot = QString("1") + symSlot->name;
470 sndProxy->lock();
471 rcvProxy->lock();
472 bool ok;
473 if( !sndProxy->object() || !rcvProxy->object() ) {
474 ok = true;
476 else {
477 ok = QObject::connect( sndProxy->object(), strSig.toStdString().c_str(),
478 rcvProxy->object(), strSlot.toStdString().c_str() );
480 sndProxy->unlock();
481 rcvProxy->unlock();
483 if (!ok) {
484 qcErrorMsg( QString("Failed to connect %1::%2 to %3::%4!\n")
485 .arg(sndProxy->scClassName()).arg(symSig->name)
486 .arg(rcvProxy->scClassName()).arg(symSlot->name)
488 return errFailed;
491 return errNone;
494 QC_LANG_PRIMITIVE( QObject_InvokeMethod, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
496 if( NotSym( a+0 ) ) return errWrongType;
498 PyrSymbol *method = slotRawSymbol( a+0 );
499 PyrSlot *methodArgs = a+1;
500 bool sync = !IsFalse( a+2 );
502 qcSCObjectDebugMsg( 1, slotRawObject(r),
503 QString("INVOKE: '%1' [%2]").arg(method->name).arg(sync ? "SYNC" : "ASYNC") );
505 QObjectProxy *proxy = QOBJECT_FROM_SLOT( r );
507 PyrSlot *retSlot;
508 Qt::ConnectionType cType;
510 if( sync ) {
511 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
512 retSlot = r;
513 cType = Qt::DirectConnection;
515 else {
516 retSlot = 0;
517 cType = Qt::QueuedConnection;
520 bool ok = proxy->invokeMethod( method->name, retSlot, methodArgs, cType );
522 return ok ? errNone : errFailed;
525 QC_LANG_PRIMITIVE( QObject_IsValid, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
527 // NOTE Though we could check validity without posting an event (by locking the proxy),
528 // we post a sync event to maximize the chance that a DeferredDelete event issued earlier
529 // can get processed by the object in question before it is checked for validity.
531 QObjectProxy *proxy = Slot::toObjectProxy( r );
533 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
535 SetBool( r, proxy->object() != 0 );
537 return errNone;
540 QC_LANG_PRIMITIVE( QObject_GetChildren, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
542 if( NotSym(a) && NotNil(a) ) return errWrongType;
544 QObjectProxy *proxy = Slot::toObjectProxy( r );
545 PyrSymbol *className = IsSym( a ) ? slotRawSymbol( a ) : 0;
547 qcSCObjectDebugMsg( 1, slotRawObject(r),
548 QString("GET CHILDREN: of class '%1'")
549 .arg( className ? className->name : "QObject" ) );
551 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
553 QList<PyrObject*> children = proxy->children( className );
555 int count = children.count();
556 PyrObject *array = newPyrArray( g->gc, count, 0, true );
557 SetObject( r, array );
559 PyrSlot *s = array->slots;
560 Q_FOREACH( PyrObject *obj, children ) {
561 SetObject( s, obj );
562 ++array->size;
563 g->gc->GCWrite( array, s );
564 ++s;
567 return errNone;
570 QC_LANG_PRIMITIVE( QObject_GetParent, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
572 if( NotSym(a) && NotNil(a) ) return errWrongType;
574 QObjectProxy *proxy = Slot::toObjectProxy( r );
575 PyrSymbol *className = IsSym( a ) ? slotRawSymbol( a ) : 0;
577 qcSCObjectDebugMsg( 1, slotRawObject(r), QString("GET PARENT") );
579 if( !proxy->compareThread() ) return QtCollider::wrongThreadError();
581 PyrObject *parent = proxy->parent( className );
583 if( parent ) SetObject( r, parent );
584 else SetNil( r );
586 return errNone;
589 void defineQObjectPrimitives()
591 LangPrimitiveDefiner definer;
592 definer.define<QObject_New>();
593 definer.define<QObject_Destroy>();
594 definer.define<QObject_SetParent>();
595 definer.define<QMetaObject_Properties>();
596 definer.define<QMetaObject_Methods>();
597 definer.define<QObject_GetProperties>();
598 definer.define<QObject_GetMethods>();
599 definer.define<QObject_SetProperty>();
600 definer.define<QObject_GetProperty>();
601 definer.define<QObject_SetEventHandler>();
602 definer.define<QObject_SetEventHandlerEnabled>();
603 definer.define<QObject_ConnectMethod>();
604 definer.define<QObject_DisconnectMethod>();
605 definer.define<QObject_ConnectObject>();
606 definer.define<QObject_DisconnectObject>();
607 definer.define<QObject_ConnectSlot>();
608 definer.define<QObject_InvokeMethod>();
609 definer.define<QObject_IsValid>();
610 definer.define<QObject_GetChildren>();
611 definer.define<QObject_GetParent>();
614 } // namespace QtCollider