1 /************************************************************************
3 * Copyright 2010-2011 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 "QObjectProxy.h"
23 #include "QcApplication.h"
26 #include "QcSignalSpy.h"
28 #include <QApplication>
30 #include <QVarLengthArray>
33 #include <PyrKernel.h>
34 #include <VMGlobals.h>
36 using namespace QtCollider
;
38 void interpretMouseEvent( QEvent
*e
, QList
<QVariant
> &args
);
39 void interpretKeyEvent( QEvent
*e
, QList
<QVariant
> &args
);
41 QObjectProxy::QObjectProxy( QObject
*qObject_
, PyrObject
*scObject_
)
42 : qObject( qObject_
),
43 _scObject( scObject_
),
44 _scClassName( slotRawSymbol( &scObject_
->classptr
->name
)->name
)
46 ProxyToken
*token
= new ProxyToken( this, qObject
);
47 // WARNING: make sure the signal is already in normalized signature,
48 // to avoid triggering very expensive normalization!
49 connect( qObject
, SIGNAL(destroyed(QObject
*)), this, SLOT(invalidate()) );
50 qObject
->installEventFilter( this );
51 _eventHandlers
.reserve(10);
54 QObjectProxy::~QObjectProxy()
56 qcProxyDebugMsg( 1, QString("Proxy is being deleted.") );
59 bool QObjectProxy::compareThread() {
60 return gMainVMGlobals
->canCallOS
;
63 void QObjectProxy::invalidate() {
64 qcProxyDebugMsg( 1, QString("Object has been deleted. Invalidating proxy.") );
65 mutex
.lock(); qObject
= 0; mutex
.unlock();
66 QApplication::postEvent( this, new QEvent((QEvent::Type
) QtCollider::Event_Proxy_Release
) );
69 bool QObjectProxy::invokeMethod( const char *method
, PyrSlot
*retSlot
, PyrSlot
*argSlot
,
70 Qt::ConnectionType ctype
)
78 // the signature char array
79 QVarLengthArray
<char, 512> sig
;
81 // serialize method name
82 int len
= qstrlen( method
);
87 sig
.append( method
, len
);
90 // get data from argument slots
91 QtCollider::Variant argSlots
[10];
93 if( isKindOfSlot( argSlot
, class_array
) ) {
94 PyrSlot
*slots
= slotRawObject( argSlot
)->slots
;
95 int size
= slotRawObject( argSlot
)->size
;
97 for( i
= 0; i
<size
&& i
<10; ++i
) {
98 argSlots
[i
].setData( slots
);
102 else argSlots
[0].setData( argSlot
);
104 // serialize argument types
106 for( i
= 0; i
< 10; ++i
) {
107 int type
= argSlots
[i
].type();
108 if( type
== QMetaType::Void
) break;
109 const char *typeName
= QMetaType::typeName( type
);
110 int len
= qstrlen( typeName
);
111 if( len
<= 0 ) break;
112 sig
.append( typeName
, len
);
116 // finalize the signature
117 if( i
==0 ) sig
.append( ')' );
118 else sig
[sig
.size() - 1] = ')';
122 // get the meta method
123 const QMetaObject
*mo
= qObject
->metaObject();
125 int mi
= mo
->indexOfMethod( sig
.constData() );
127 QByteArray mnorm
= QMetaObject::normalizedSignature( sig
.constData() );
128 mi
= mo
->indexOfMethod( mnorm
.constData() );
131 if( mi
< 0 || mi
>= mo
->methodCount() ) {
132 qcProxyDebugMsg( 1, QString("WARNING: No such method: %1::%2").arg( mo
->className() )
133 .arg( sig
.constData() ) );
138 QMetaMethod mm
= mo
->method( mi
);
140 // construct the return data object
141 QGenericReturnArgument retGArg
;
142 const char *retTypeName
= mm
.typeName();
143 int retType
= QMetaType::type( retTypeName
);
146 retPtr
= QMetaType::construct( retType
);
147 retGArg
= QGenericReturnArgument( retTypeName
, retPtr
);
152 mm
.invoke( qObject
, ctype
, retGArg
,
153 argSlots
[0].asGenericArgument(),
154 argSlots
[1].asGenericArgument(),
155 argSlots
[2].asGenericArgument(),
156 argSlots
[3].asGenericArgument(),
157 argSlots
[4].asGenericArgument(),
158 argSlots
[5].asGenericArgument(),
159 argSlots
[6].asGenericArgument(),
160 argSlots
[7].asGenericArgument(),
161 argSlots
[8].asGenericArgument(),
162 argSlots
[9].asGenericArgument());
164 // store the return data into the return slot
165 if( success
&& retPtr
) {
166 QVariant
retVar( retType
, retPtr
);
167 Slot::setVariant( retSlot
, retVar
);
171 QMetaType::destroy( retType
, retPtr
);
177 void QObjectProxy::invokeScMethod
178 ( PyrSymbol
*method
, const QList
<QVariant
> & args
, PyrSlot
*result
,
181 qcProxyDebugMsg(1, QString("SC METHOD CALL [+++]: ") + QString(method
->name
) );
184 QtCollider::lockLang();
188 QtCollider::runLang( _scObject
, method
, args
, result
);
191 if(result
) SetNil( result
);
192 qcDebugMsg(1, "WARNING: no SC object");
195 if( !locked
) QtCollider::unlockLang();
197 qcProxyDebugMsg(1, QString("SC METHOD CALL [---]: ") + QString(method
->name
) );
200 void QObjectProxy::customEvent( QEvent
*event
)
202 switch ( event
->type() ) {
203 case (QEvent::Type
) QtCollider::Event_ScMethodCall
:
204 scMethodCallEvent( static_cast<ScMethodCallEvent
*>(event
) );
206 case (QEvent::Type
) QtCollider::Event_Proxy_SetProperty
:
207 setPropertyEvent( static_cast<SetPropertyEvent
*>(event
) );
209 case (QEvent::Type
) QtCollider::Event_Proxy_Destroy
:
210 destroyEvent( static_cast<DestroyEvent
*>(event
) );
212 case (QEvent::Type
) QtCollider::Event_Proxy_Release
:
213 invokeScMethod(SC_SYM(prRelease
));
219 bool QObjectProxy::setParent( QObjectProxy
*parentProxy
) {
220 if( qObject
&& parentProxy
->object() )
221 qObject
->setParent( parentProxy
->object() );
226 bool QObjectProxy::setProperty( const char *property
, const QVariant
& val
)
228 if( !qObject
) return true;
229 if( !qObject
->setProperty( property
, val
) ) {
230 qcProxyDebugMsg(1, QString("WARNING: Property '%1' not found. Setting dynamic property.")
236 bool QObjectProxy::setPropertyEvent( SetPropertyEvent
*e
)
238 return setProperty( e
->property
->name
, e
->value
);
241 QVariant
QObjectProxy::property( const char *property
)
243 return qObject
? qObject
->property( property
) : QVariant();
246 bool QObjectProxy::setEventHandler( int type
, PyrSymbol
*method
,
247 QtCollider::Synchronicity sync
, bool enable
)
249 EventHandlerData data
;
251 data
.method
= method
;
253 data
.enabled
= enable
;
255 EventHandlerData
*d
= _eventHandlers
.data();
256 int n
= _eventHandlers
.size();
267 _eventHandlers
.append(data
);
272 bool QObjectProxy::setEventHandlerEnabled( int type
, bool enabled
)
274 EventHandlerData
*d
= _eventHandlers
.data();
275 int n
= _eventHandlers
.size();
280 d
->enabled
= enabled
;
289 bool QObjectProxy::connectObject( const char *signal
, PyrObject
*object
,
290 Qt::ConnectionType ctype
)
292 if( !qObject
) return true;
294 QcFunctionSignalHandler
*handler
=
295 new QcFunctionSignalHandler( this, signal
, object
, ctype
);
297 if( !handler
->isValid() ) { delete handler
; return false; }
299 funcSigHandlers
.append( handler
);
304 bool QObjectProxy::connectMethod( const char *signal
, PyrSymbol
*method
,
305 Qt::ConnectionType ctype
)
307 if( !qObject
) return true;
309 QcMethodSignalHandler
*handler
=
310 new QcMethodSignalHandler( this, signal
, method
, ctype
);
312 if( handler
->isValid() ) {
313 methodSigHandlers
.append( handler
);
322 bool QObjectProxy::disconnectObject( const char *sig
, PyrObject
*object
)
324 if( !qObject
) return true;
326 const QMetaObject
*mo
= qObject
->metaObject();
327 QByteArray signal
= QMetaObject::normalizedSignature( sig
);
328 int sigId
= mo
->indexOfSignal( signal
);
330 qcDebugMsg( 1, QString("WARNING: No such signal: '%1'").arg(signal
.constData()) );
334 for( int i
= 0; i
< funcSigHandlers
.size(); ++i
) {
335 QcFunctionSignalHandler
*h
= funcSigHandlers
[i
];
336 if( h
->indexOfSignal() == sigId
&& h
->function() == object
) {
337 funcSigHandlers
.removeAt(i
);
346 bool QObjectProxy::disconnectMethod( const char *sig
, PyrSymbol
*method
)
348 if( !qObject
) return true;
350 const QMetaObject
*mo
= qObject
->metaObject();
351 QByteArray signal
= QMetaObject::normalizedSignature( sig
);
352 int sigId
= mo
->indexOfSignal( signal
);
354 qcDebugMsg( 1, QString("WARNING: No such signal: '%1'").arg(signal
.constData()) );
358 for( int i
= 0; i
< methodSigHandlers
.size(); ++i
) {
359 QcMethodSignalHandler
*h
= methodSigHandlers
[i
];
360 if( h
->indexOfSignal() == sigId
&& h
->method() == method
) {
361 methodSigHandlers
.removeAt(i
);
370 void QObjectProxy::destroy( DestroyAction action
)
379 case DestroyProxyAndObject
:
386 bool QObjectProxy::destroyEvent( DestroyEvent
*e
)
388 destroy( e
->action() );
392 QList
<PyrObject
*> QObjectProxy::children( PyrSymbol
*className
)
394 QList
<PyrObject
*> scChildren
;
396 if( !qObject
) return scChildren
;
398 const QObjectList
&children
= qObject
->children();
400 Q_FOREACH( QObject
*child
, children
) {
402 QObjectProxy
*proxy
= QObjectProxy::fromObject( child
);
403 if( !proxy
) continue;
405 PyrObject
* obj
= proxy
->_scObject
;
408 if( className
&& !isKindOf( obj
, className
->u
.classobj
) )
410 scChildren
.append( obj
);
417 PyrObject
*QObjectProxy::parent( PyrSymbol
*className
)
419 if( !qObject
) return 0;
421 QObject
*parent
= qObject
->parent();
424 // see if this parent has a corresponding proxy
425 QObjectProxy
*proxy
= QObjectProxy::fromObject( parent
);
427 // if parent does not have a corresponding SC object (it is just
428 // being deleted) return no parent;
429 PyrObject
*scobj
= proxy
->_scObject
;
430 if( !scobj
) return 0;
432 // if parent SC object is of desired class (or no class specified)
433 // return it, else continue
434 if( !className
|| isKindOf( scobj
, className
->u
.classobj
) ) {
439 // if this parent was not appropriate continue to consider the parent's parent
440 parent
= parent
->parent();
446 bool QObjectProxy::eventFilter( QObject
* watched
, QEvent
* event
)
448 int type
= event
->type();
450 EventHandlerData
*d
= _eventHandlers
.data();
451 int n
= _eventHandlers
.size();
460 qcProxyDebugMsg(3,QString("No handler for event (%1), forwarding to the widget")
465 QList
<QVariant
> args
;
467 if( !filterEvent( watched
, event
, *d
, args
) ) {
468 qcProxyDebugMsg(3,QString("Event (%1, %2) not handled, forwarding to the widget")
470 .arg(event
->spontaneous() ? "spontaneous" : "inspontaneous") );
474 qcProxyDebugMsg(1,QString("Will handle event (%1, %2) -> (%3, %4)")
476 .arg(event
->spontaneous() ? "spontaneous" : "inspontaneous")
477 .arg(d
->method
->name
)
478 .arg(d
->sync
== Synchronous
? "sync" : "async") );
480 return invokeEventHandler( event
, *d
, args
);
483 bool QObjectProxy::invokeEventHandler( QEvent
*event
, EventHandlerData
&eh
, QList
<QVariant
> & args
)
485 PyrSymbol
*method
= eh
.method
;
487 if( eh
.sync
== Synchronous
) {
489 invokeScMethod( method
, args
, &result
);
490 if( IsTrue( &result
) ) {
491 qcProxyDebugMsg(2,"Event accepted");
495 else if( IsFalse( &result
) ) {
496 qcProxyDebugMsg(2,"Event ignored");
502 ScMethodCallEvent
*e
= new ScMethodCallEvent( method
, args
);
503 QApplication::postEvent( this, e
);
506 qcProxyDebugMsg(2,"Forwarding event to the system");
510 bool QObjectProxy::filterEvent( QObject
*, QEvent
*e
, EventHandlerData
& eh
, QList
<QVariant
> & args
)
515 inline void QObjectProxy::scMethodCallEvent( ScMethodCallEvent
*e
)
517 invokeScMethod( e
->method
, e
->args
, 0, e
->locked
);
520 QObjectProxy
* QObjectProxy::fromObject( QObject
*object
)
522 const QObjectList
&children
= object
->children();
523 Q_FOREACH( QObject
*child
, children
) {
524 ProxyToken
*token
= qobject_cast
<QtCollider::ProxyToken
*>( child
);
525 if( token
) return token
->proxy
;