Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / QtCollider / QObjectProxy.cpp
blob9796a43390fa187d2d04ff18c3406079c28199bd
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"
24 #include "Common.h"
25 #include "Slot.h"
26 #include "QcSignalSpy.h"
28 #include <QApplication>
29 #include <QWidget>
30 #include <QVarLengthArray>
31 #include <QThread>
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 )
72 mutex.lock();
73 if( !qObject ) {
74 mutex.unlock();
75 return true;
78 // the signature char array
79 QVarLengthArray<char, 512> sig;
81 // serialize method name
82 int len = qstrlen( method );
83 if( len <= 0 ) {
84 mutex.unlock();
85 return false;
87 sig.append( method, len );
88 sig.append( '(' );
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;
96 int i;
97 for( i = 0; i<size && i<10; ++i ) {
98 argSlots[i].setData( slots );
99 ++slots;
102 else argSlots[0].setData( argSlot );
104 // serialize argument types
105 int i;
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 );
113 sig.append( ',' );
116 // finalize the signature
117 if( i==0 ) sig.append( ')' );
118 else sig[sig.size() - 1] = ')';
120 sig.append('\0');
122 // get the meta method
123 const QMetaObject *mo = qObject->metaObject();
125 int mi = mo->indexOfMethod( sig.constData() );
126 if( mi < 0 ) {
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() ) );
134 mutex.unlock();
135 return false;
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 );
144 void *retPtr = 0;
145 if( retSlot ) {
146 retPtr = QMetaType::construct( retType );
147 retGArg = QGenericReturnArgument( retTypeName, retPtr );
150 //do it!
151 bool success =
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 );
170 if( retPtr )
171 QMetaType::destroy( retType, retPtr );
173 mutex.unlock();
174 return success;
177 void QObjectProxy::invokeScMethod
178 ( PyrSymbol *method, const QList<QVariant> & args, PyrSlot *result,
179 bool locked )
181 qcProxyDebugMsg(1, QString("SC METHOD CALL [+++]: ") + QString(method->name) );
183 if( !locked ) {
184 QtCollider::lockLang();
187 if( _scObject ) {
188 QtCollider::runLang( _scObject, method, args, result );
190 else {
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) );
205 return;
206 case (QEvent::Type) QtCollider::Event_Proxy_SetProperty:
207 setPropertyEvent( static_cast<SetPropertyEvent*>(event) );
208 return;
209 case (QEvent::Type) QtCollider::Event_Proxy_Destroy:
210 destroyEvent( static_cast<DestroyEvent*>(event) );
211 return;
212 case (QEvent::Type) QtCollider::Event_Proxy_Release:
213 invokeScMethod(SC_SYM(prRelease));
214 return;
215 default: ;
219 bool QObjectProxy::setParent( QObjectProxy *parentProxy ) {
220 if( qObject && parentProxy->object() )
221 qObject->setParent( parentProxy->object() );
223 return true;
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.")
231 .arg( property ) );
233 return false;
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;
250 data.type = type;
251 data.method = method;
252 data.sync = sync;
253 data.enabled = enable;
255 EventHandlerData *d = _eventHandlers.data();
256 int n = _eventHandlers.size();
257 while(n--)
259 if(d->type == type)
261 *d = data;
262 break;
264 ++d;
266 if(n < 0)
267 _eventHandlers.append(data);
269 return true;
272 bool QObjectProxy::setEventHandlerEnabled( int type, bool enabled )
274 EventHandlerData *d = _eventHandlers.data();
275 int n = _eventHandlers.size();
276 while(n--)
278 if(d->type == type)
280 d->enabled = enabled;
281 break;
283 ++d;
286 return n >= 0;
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 );
301 return true;
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 );
314 return true;
316 else {
317 delete handler;
318 return false;
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 );
329 if( sigId < 0 ) {
330 qcDebugMsg( 1, QString("WARNING: No such signal: '%1'").arg(signal.constData()) );
331 return false;
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);
338 delete h;
339 return true;
343 return false;
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 );
353 if( sigId < 0 ) {
354 qcDebugMsg( 1, QString("WARNING: No such signal: '%1'").arg(signal.constData()) );
355 return false;
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);
362 delete h;
363 return true;
367 return false;
370 void QObjectProxy::destroy( DestroyAction action )
372 switch( action ) {
373 case DestroyObject:
374 delete qObject;
375 return;
376 case DestroyProxy:
377 delete this;
378 return;
379 case DestroyProxyAndObject:
380 delete qObject;
381 delete this;
382 return;
386 bool QObjectProxy::destroyEvent( DestroyEvent *e )
388 destroy( e->action() );
389 return true;
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;
407 if( obj ) {
408 if( className && !isKindOf( obj, className->u.classobj ) )
409 continue;
410 scChildren.append( obj );
414 return scChildren;
417 PyrObject *QObjectProxy::parent( PyrSymbol *className )
419 if( !qObject ) return 0;
421 QObject *parent = qObject->parent();
423 while( parent ) {
424 // see if this parent has a corresponding proxy
425 QObjectProxy *proxy = QObjectProxy::fromObject( parent );
426 if( proxy ) {
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 ) ) {
435 return scobj;
439 // if this parent was not appropriate continue to consider the parent's parent
440 parent = parent->parent();
443 return 0;
446 bool QObjectProxy::eventFilter( QObject * watched, QEvent * event )
448 int type = event->type();
450 EventHandlerData *d = _eventHandlers.data();
451 int n = _eventHandlers.size();
452 while(n--)
454 if(d->type == type)
455 break;
456 ++d;
458 if(n < 0)
460 qcProxyDebugMsg(3,QString("No handler for event (%1), forwarding to the widget")
461 .arg(type));
462 return false;
465 QList<QVariant> args;
467 if( !filterEvent( watched, event, *d, args ) ) {
468 qcProxyDebugMsg(3,QString("Event (%1, %2) not handled, forwarding to the widget")
469 .arg(type)
470 .arg(event->spontaneous() ? "spontaneous" : "inspontaneous") );
471 return false;
474 qcProxyDebugMsg(1,QString("Will handle event (%1, %2) -> (%3, %4)")
475 .arg(type)
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 ) {
488 PyrSlot result;
489 invokeScMethod( method, args, &result );
490 if( IsTrue( &result ) ) {
491 qcProxyDebugMsg(2,"Event accepted");
492 event->accept();
493 return true;
495 else if( IsFalse( &result ) ) {
496 qcProxyDebugMsg(2,"Event ignored");
497 event->ignore();
498 return true;
501 else {
502 ScMethodCallEvent *e = new ScMethodCallEvent( method, args );
503 QApplication::postEvent( this, e );
506 qcProxyDebugMsg(2,"Forwarding event to the system");
507 return false;
510 bool QObjectProxy::filterEvent( QObject *, QEvent *e, EventHandlerData & eh, QList<QVariant> & args )
512 return eh.enabled;
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;
527 return 0;