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 connect( qObject
, SIGNAL( destroyed( QObject
* ) ), this, SLOT( invalidate() ) );
48 qObject
->installEventFilter( this );
51 QObjectProxy::~QObjectProxy()
53 qcProxyDebugMsg( 1, QString("Proxy is being deleted.") );
56 bool QObjectProxy::compareThread() {
57 return QThread::currentThread() == this->thread();
60 void QObjectProxy::invalidate() {
61 qcProxyDebugMsg( 1, QString("Object has been deleted. Invalidating proxy.") );
62 mutex
.lock(); qObject
= 0; mutex
.unlock();
65 bool QObjectProxy::invokeMethod( const char *method
, PyrSlot
*retSlot
, PyrSlot
*argSlot
,
66 Qt::ConnectionType ctype
)
74 // the signature char array
75 QVarLengthArray
<char, 512> sig
;
77 // serialize method name
78 int len
= qstrlen( method
);
83 sig
.append( method
, len
);
86 // get data from argument slots
87 QtCollider::Variant argSlots
[10];
89 if( isKindOfSlot( argSlot
, class_Array
) ) {
90 PyrSlot
*slots
= slotRawObject( argSlot
)->slots
;
91 int size
= slotRawObject( argSlot
)->size
;
93 for( i
= 0; i
<size
&& i
<10; ++i
) {
94 argSlots
[i
].setData( slots
);
98 else argSlots
[0].setData( argSlot
);
100 // serialize argument types
102 for( i
= 0; i
< 10; ++i
) {
103 int type
= argSlots
[i
].type();
104 if( type
== QMetaType::Void
) break;
105 const char *typeName
= QMetaType::typeName( type
);
106 int len
= qstrlen( typeName
);
107 if( len
<= 0 ) break;
108 sig
.append( typeName
, len
);
112 // finalize the signature
113 if( i
==0 ) sig
.append( ')' );
114 else sig
[sig
.size() - 1] = ')';
118 // get the meta method
119 const QMetaObject
*mo
= qObject
->metaObject();
121 int mi
= mo
->indexOfMethod( sig
.constData() );
123 QByteArray mnorm
= QMetaObject::normalizedSignature( sig
.constData() );
124 mi
= mo
->indexOfMethod( mnorm
.constData() );
127 if( mi
< 0 || mi
>= mo
->methodCount() ) {
128 qcProxyDebugMsg( 1, QString("WARNING: No such method: %1::%2").arg( mo
->className() )
129 .arg( sig
.constData() ) );
134 QMetaMethod mm
= mo
->method( mi
);
136 // construct the return data object
137 QGenericReturnArgument retGArg
;
138 const char *retTypeName
= mm
.typeName();
139 int retType
= QMetaType::type( retTypeName
);
142 retPtr
= QMetaType::construct( retType
);
143 retGArg
= QGenericReturnArgument( retTypeName
, retPtr
);
148 mm
.invoke( qObject
, ctype
, retGArg
,
149 argSlots
[0].asGenericArgument(),
150 argSlots
[1].asGenericArgument(),
151 argSlots
[2].asGenericArgument(),
152 argSlots
[3].asGenericArgument(),
153 argSlots
[4].asGenericArgument(),
154 argSlots
[5].asGenericArgument(),
155 argSlots
[6].asGenericArgument(),
156 argSlots
[7].asGenericArgument(),
157 argSlots
[8].asGenericArgument(),
158 argSlots
[9].asGenericArgument());
160 // store the return data into the return slot
161 if( success
&& retPtr
) {
162 QVariant
retVar( retType
, retPtr
);
163 Slot::setVariant( retSlot
, retVar
);
167 QMetaType::destroy( retType
, retPtr
);
173 void QObjectProxy::invokeScMethod
174 ( PyrSymbol
*method
, const QList
<QVariant
> & args
, PyrSlot
*result
,
177 qcProxyDebugMsg(1, QString("SC METHOD CALL [+++]: ") + QString(method
->name
) );
180 QtCollider::lockLang();
184 QtCollider::runLang( _scObject
, method
, args
, result
);
188 qcDebugMsg(1, "WARNING: no SC object");
191 if( !locked
) QtCollider::unlockLang();
193 qcProxyDebugMsg(1, QString("SC METHOD CALL [---]: ") + QString(method
->name
) );
196 void QObjectProxy::customEvent( QEvent
*event
)
198 switch ( event
->type() ) {
199 case (QEvent::Type
) QtCollider::Event_ScMethodCall
:
200 scMethodCallEvent( static_cast<ScMethodCallEvent
*>(event
) );
202 case (QEvent::Type
) QtCollider::Event_Proxy_SetProperty
:
203 setPropertyEvent( static_cast<SetPropertyEvent
*>(event
) );
205 case (QEvent::Type
) QtCollider::Event_Proxy_Destroy
:
206 destroyEvent( static_cast<DestroyEvent
*>(event
) );
212 bool QObjectProxy::setParent( QObjectProxy
*parentProxy
) {
213 if( qObject
&& parentProxy
->object() )
214 qObject
->setParent( parentProxy
->object() );
219 bool QObjectProxy::setProperty( const char *property
, const QVariant
& val
)
221 if( !qObject
) return true;
222 if( !qObject
->setProperty( property
, val
) ) {
223 qcProxyDebugMsg(1, QString("WARNING: Property '%1' not found. Setting dynamic property.")
229 bool QObjectProxy::setPropertyEvent( SetPropertyEvent
*e
)
231 return setProperty( e
->property
->name
, e
->value
);
234 QVariant
QObjectProxy::property( const char *property
)
236 return qObject
? qObject
->property( property
) : QVariant();
239 bool QObjectProxy::setEventHandler( int type
, PyrSymbol
*method
,
240 QtCollider::Synchronicity sync
, bool enable
)
242 EventHandlerData data
;
244 data
.method
= method
;
247 // if 'enable' is true, insert the new event handler enabled,
248 // otherwise copy current state, or set disabled if none.
253 EventHandlerData d
= _eventHandlers
.value( type
);
254 if( d
.type
!= QEvent::None
) data
.enabled
= d
.enabled
;
255 else data
.enabled
= false;
258 // NOTE: will replace if same key
259 _eventHandlers
.insert( type
, data
);
264 bool QObjectProxy::setEventHandlerEnabled( int type
, bool enabled
)
266 EventHandlerData d
= _eventHandlers
.value( type
);
267 if( d
.type
!= type
) return false;
269 if( d
.enabled
!= enabled
) {
271 _eventHandlers
.insert( type
, d
);
277 bool QObjectProxy::connectObject( const char *signal
, PyrObject
*object
,
278 Qt::ConnectionType ctype
)
280 if( !qObject
) return true;
282 QcFunctionSignalHandler
*handler
=
283 new QcFunctionSignalHandler( this, signal
, object
, ctype
);
285 if( !handler
->isValid() ) { delete handler
; return false; }
287 funcSigHandlers
.append( handler
);
292 bool QObjectProxy::connectMethod( const char *signal
, PyrSymbol
*method
,
293 Qt::ConnectionType ctype
)
295 if( !qObject
) return true;
297 QcMethodSignalHandler
*handler
=
298 new QcMethodSignalHandler( this, signal
, method
, ctype
);
300 if( handler
->isValid() ) {
301 methodSigHandlers
.append( handler
);
310 bool QObjectProxy::disconnectObject( const char *sig
, PyrObject
*object
)
312 if( !qObject
) return true;
314 const QMetaObject
*mo
= qObject
->metaObject();
315 QByteArray signal
= QMetaObject::normalizedSignature( sig
);
316 int sigId
= mo
->indexOfSignal( signal
);
318 qcDebugMsg( 1, QString("WARNING: No such signal: '%1'").arg(signal
.constData()) );
322 for( int i
= 0; i
< funcSigHandlers
.size(); ++i
) {
323 QcFunctionSignalHandler
*h
= funcSigHandlers
[i
];
324 if( h
->indexOfSignal() == sigId
&& h
->function() == object
) {
325 funcSigHandlers
.removeAt(i
);
334 bool QObjectProxy::disconnectMethod( const char *sig
, PyrSymbol
*method
)
336 if( !qObject
) return true;
338 const QMetaObject
*mo
= qObject
->metaObject();
339 QByteArray signal
= QMetaObject::normalizedSignature( sig
);
340 int sigId
= mo
->indexOfSignal( signal
);
342 qcDebugMsg( 1, QString("WARNING: No such signal: '%1'").arg(signal
.constData()) );
346 for( int i
= 0; i
< methodSigHandlers
.size(); ++i
) {
347 QcMethodSignalHandler
*h
= methodSigHandlers
[i
];
348 if( h
->indexOfSignal() == sigId
&& h
->method() == method
) {
349 methodSigHandlers
.removeAt(i
);
358 void QObjectProxy::destroy( DestroyAction action
)
367 case DestroyProxyAndObject
:
374 bool QObjectProxy::destroyEvent( DestroyEvent
*e
)
376 destroy( e
->action() );
380 QList
<PyrObject
*> QObjectProxy::children( PyrSymbol
*className
)
382 QList
<PyrObject
*> scChildren
;
384 if( !qObject
) return scChildren
;
386 const QObjectList
&children
= qObject
->children();
388 Q_FOREACH( QObject
*child
, children
) {
390 QObjectProxy
*proxy
= QObjectProxy::fromObject( child
);
391 if( !proxy
) continue;
393 PyrObject
* obj
= proxy
->_scObject
;
396 if( className
&& !isKindOf( obj
, className
->u
.classobj
) )
398 scChildren
.append( obj
);
405 PyrObject
*QObjectProxy::parent( PyrSymbol
*className
)
407 if( !qObject
) return 0;
409 QObject
*parent
= qObject
->parent();
412 // see if this parent has a corresponding proxy
413 QObjectProxy
*proxy
= QObjectProxy::fromObject( parent
);
415 // if parent does not have a corresponding SC object (it is just
416 // being deleted) return no parent;
417 PyrObject
*scobj
= proxy
->_scObject
;
418 if( !scobj
) return 0;
420 // if parent SC object is of desired class (or no class specified)
421 // return it, else continue
422 if( !className
|| isKindOf( scobj
, className
->u
.classobj
) ) {
427 // if this parent was not appropriate continue to consider the parent's parent
428 parent
= parent
->parent();
434 bool QObjectProxy::eventFilter( QObject
* watched
, QEvent
* event
)
436 int type
= event
->type();
439 QList
<QVariant
> args
;
441 if( !filterEvent( watched
, event
, eh
, args
) ) {
442 qcProxyDebugMsg(3,QString("Event (%1, %2) not handled, forwarding to the widget")
444 .arg(event
->spontaneous() ? "spontaneous" : "inspontaneous") );
448 qcProxyDebugMsg(1,QString("Will handle event (%1, %2) -> (%3, %4)")
450 .arg(event
->spontaneous() ? "spontaneous" : "inspontaneous")
451 .arg(eh
.method
->name
)
452 .arg(eh
.sync
== Synchronous
? "sync" : "async") );
454 return invokeEventHandler( event
, eh
, args
);
457 bool QObjectProxy::invokeEventHandler( QEvent
*event
, EventHandlerData
&eh
, QList
<QVariant
> & args
)
459 PyrSymbol
*method
= eh
.method
;
461 if( eh
.sync
== Synchronous
) {
463 invokeScMethod( method
, args
, &result
);
464 if( IsTrue( &result
) ) {
465 qcProxyDebugMsg(2,"Event accepted");
469 else if( IsFalse( &result
) ) {
470 qcProxyDebugMsg(2,"Event ignored");
476 ScMethodCallEvent
*e
= new ScMethodCallEvent( method
, args
);
477 QApplication::postEvent( this, e
);
480 qcProxyDebugMsg(2,"Forwarding event to the system");
484 bool QObjectProxy::filterEvent( QObject
*, QEvent
*e
, EventHandlerData
& eh
, QList
<QVariant
> & args
)
486 int type
= e
->type();
487 eh
= _eventHandlers
.value( type
, EventHandlerData() );
488 return ( eh
.type
== type
) && eh
.enabled
;
491 inline void QObjectProxy::scMethodCallEvent( ScMethodCallEvent
*e
)
493 invokeScMethod( e
->method
, e
->args
, 0, e
->locked
);
496 QObjectProxy
* QObjectProxy::fromObject( QObject
*object
)
498 const QObjectList
&children
= object
->children();
499 Q_FOREACH( QObject
*child
, children
) {
500 ProxyToken
*token
= qobject_cast
<QtCollider::ProxyToken
*>( child
);
501 if( token
) return token
->proxy
;