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"
29 #include <PyrObject.h>
30 #include <PyrKernel.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 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
);
63 qcErrorMsg( QString("Factory for class '%1' not found!").arg(qtClassName
) );
67 QObjectProxy
*proxy
= 0;
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
) {
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
);
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
);
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;
120 // Post the destruction event asynchronously;
121 DestroyEvent
*e
= new DestroyEvent( QObjectProxy::DestroyProxyAndObject
);
122 QApplication::postEvent( proxy
, e
);
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() ) );
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;
185 case QMetaMethod::Signal
:
186 if( !getSignals
) continue;
188 case QMetaMethod::Slot
:
189 if( !getSlots
|| (method
.access() != QMetaMethod::Public
) ) continue;
194 Slot::setString( s
, QString::fromLatin1( method
.signature() ) );
196 g
->gc
->GCWrite( array
, 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
);
210 qcErrorMsg( QString("Factory for class '%1' not found!").arg(className
) );
214 const QMetaObject
*mo
= f
->metaObject();
215 qcGetProperties( mo
, r
, g
);
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
);
229 qcErrorMsg( QString("Factory for class '%1' not found!").arg(className
) );
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
);
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();
254 const QMetaObject
*mo
= obj
->metaObject();
255 qcGetProperties( mo
, r
, g
);
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();
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
);
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 );
296 proxy
->setProperty( property
->name
, val
);
298 SetPropertyEvent
*e
= new SetPropertyEvent();
299 e
->property
= property
;
301 QApplication::postEvent( proxy
, e
);
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() ) {
320 qcErrorMsg(QString("Failed to get the value of property '%1'").arg(symProp
->name
));
324 if( Slot::setVariant(r
, val
) )
330 QC_LANG_PRIMITIVE( QObject_SetEventHandler
, 4, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
332 QObjectProxy
*proxy
= QOBJECT_FROM_SLOT( r
);
334 if( NotInt( a
+0 ) || NotSym( a
+1 ) ) return errWrongType
;
335 int eventType
= Slot::toInt( a
+0 );
336 PyrSymbol
*method
= 0; slotSymbolVal( a
+1, &method
);
337 Synchronicity sync
= IsTrue( a
+2 ) ? Synchronous
: Asynchronous
;
338 bool enabled
= IsTrue( a
+3 );
340 qcSCObjectDebugMsg( 1, slotRawObject(r
),
341 QString("SET EVENT HANDLER: type %1 -> %2 [%3, %4]")
342 .arg(eventType
).arg(method
->name
)
343 .arg(sync
== Synchronous
? "SYNC" : "ASYNC")
344 .arg(enabled
? "on" : "off") );
346 if( !proxy
->compareThread() ) return QtCollider::wrongThreadError();
348 bool ok
= proxy
->setEventHandler( eventType
, method
, sync
, enabled
);
350 return ok
? errNone
: errFailed
;
353 QC_LANG_PRIMITIVE( QObject_SetEventHandlerEnabled
, 2, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
355 if( NotInt( a
+0 ) ) return errWrongType
;
356 bool enabled
= IsTrue( a
+1 );
357 if( !enabled
&& !IsFalse( a
+1 ) ) return errWrongType
;
358 int type
= Slot::toInt( a
+0 );
360 qcSCObjectDebugMsg( 1, slotRawObject(r
),
361 QString("SET EVENT HANDLER STATE: type %1 = %2")
362 .arg(type
).arg(enabled
? "on" : "off") );
364 QObjectProxy
*proxy
= QOBJECT_FROM_SLOT( r
);
366 if( !proxy
->compareThread() ) return QtCollider::wrongThreadError();
368 bool ok
= proxy
->setEventHandlerEnabled( type
, enabled
);
370 return ok
? errNone
: errFailed
;
373 QC_LANG_PRIMITIVE( QObject_ConnectMethod
, 3, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
375 QString signal
= Slot::toString( a
+0 );
376 if( signal
.isEmpty() || NotSym( a
+1 ) ) return errWrongType
;
377 PyrSymbol
*handler
= 0; slotSymbolVal( a
+1, &handler
);
378 Qt::ConnectionType ctype
=
379 Slot::toBool( a
+2 ) ? Qt::DirectConnection
: Qt::QueuedConnection
;
381 qcSCObjectDebugMsg( 1, slotRawObject(r
),
382 QString("CONNECT METHOD: %1 -> %2 [%3]").arg(signal
).arg(handler
->name
)
383 .arg( IsTrue(a
+2) ? "SYNC" : "ASYNC") );
385 QObjectProxy
*proxy
= QOBJECT_FROM_SLOT( r
);
387 if( !proxy
->compareThread() ) return QtCollider::wrongThreadError();
389 bool ok
= proxy
->connectMethod( signal
.toAscii().constData(), handler
, ctype
);
391 return ok
? errNone
: errFailed
;
394 QC_LANG_PRIMITIVE( QObject_DisconnectMethod
, 2, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
396 QString signal
= Slot::toString( a
+0 );
397 if( signal
.isEmpty() || NotSym( a
+1 ) ) return errWrongType
;
398 PyrSymbol
*handler
= 0; slotSymbolVal( a
+1, &handler
);
400 qcSCObjectDebugMsg( 1, slotRawObject(r
),
401 QString("DISCONNECT METHOD: %1 -> %2").arg(signal
).arg(handler
->name
) );
403 QObjectProxy
*proxy
= QOBJECT_FROM_SLOT( r
);
405 if( !proxy
->compareThread() ) return QtCollider::wrongThreadError();
407 bool ok
= proxy
->disconnectMethod( signal
.toAscii().constData(), handler
);
409 return ok
? errNone
: errFailed
;
412 QC_LANG_PRIMITIVE( QObject_ConnectObject
, 3, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
414 QString signal
= Slot::toString( a
+0 );
415 if( signal
.isEmpty() || NotObj( a
+1 ) ) return errWrongType
;
416 PyrObject
*handlerObj
= slotRawObject( a
+1 );
417 Qt::ConnectionType ctype
=
418 Slot::toBool( a
+2 ) ? Qt::DirectConnection
: Qt::QueuedConnection
;
420 qcSCObjectDebugMsg( 1, slotRawObject(r
),
421 QString("CONNECT OBJECT: %1 -> %2 [%3]")
423 .arg( slotRawSymbol( &handlerObj
->classptr
->name
)->name
)
424 .arg( IsTrue(a
+2) ? "SYNC" : "ASYNC") );
426 QObjectProxy
*proxy
= QOBJECT_FROM_SLOT( r
);
428 if( !proxy
->compareThread() ) return QtCollider::wrongThreadError();
430 bool ok
= proxy
->connectObject( signal
.toAscii().constData(), handlerObj
, ctype
);
432 return ok
? errNone
: errFailed
;
435 QC_LANG_PRIMITIVE( QObject_DisconnectObject
, 2, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
437 QString signal
= Slot::toString( a
+0 );
438 if( signal
.isEmpty() || NotObj( a
+1 ) ) return errWrongType
;
439 PyrObject
*handlerObj
= slotRawObject( a
+1 );
441 qcSCObjectDebugMsg( 1, slotRawObject(r
),
442 QString("DISCONNECT OBJECT: %1").arg(signal
) );
444 QObjectProxy
*proxy
= QOBJECT_FROM_SLOT( r
);
446 if( !proxy
->compareThread() ) return QtCollider::wrongThreadError();
448 bool ok
= proxy
->disconnectObject( signal
.toAscii().constData(), 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, 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
;
473 if( !sndProxy
->object() || !rcvProxy
->object() ) {
477 ok
= QObject::connect( sndProxy
->object(), strSig
.toStdString().c_str(),
478 rcvProxy
->object(), strSlot
.toStdString().c_str() );
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
)
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
);
508 Qt::ConnectionType cType
;
511 if( !proxy
->compareThread() ) return QtCollider::wrongThreadError();
513 cType
= Qt::DirectConnection
;
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 );
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
) {
563 g
->gc
->GCWrite( array
, s
);
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
);
589 void defineQObjectPrimitives()
591 LangPrimitiveDefiner definer
;
592 definer
.define
<QObject_New
>();
593 definer
.define
<QObject_Destroy
>();
594 definer
.define
<QObject_ManuallyFinalize
>();
595 definer
.define
<QObject_SetParent
>();
596 definer
.define
<QMetaObject_Properties
>();
597 definer
.define
<QMetaObject_Methods
>();
598 definer
.define
<QObject_GetProperties
>();
599 definer
.define
<QObject_GetMethods
>();
600 definer
.define
<QObject_SetProperty
>();
601 definer
.define
<QObject_GetProperty
>();
602 definer
.define
<QObject_SetEventHandler
>();
603 definer
.define
<QObject_SetEventHandlerEnabled
>();
604 definer
.define
<QObject_ConnectMethod
>();
605 definer
.define
<QObject_DisconnectMethod
>();
606 definer
.define
<QObject_ConnectObject
>();
607 definer
.define
<QObject_DisconnectObject
>();
608 definer
.define
<QObject_ConnectSlot
>();
609 definer
.define
<QObject_InvokeMethod
>();
610 definer
.define
<QObject_IsValid
>();
611 definer
.define
<QObject_GetChildren
>();
612 definer
.define
<QObject_GetParent
>();
615 } // namespace QtCollider