bump version to 3.5beta1
[supercollider.git] / QtCollider / QObjectProxy.cpp
blob5dd86651f785675aaa8e5c20a3f4ba281b5fbe7f
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 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 )
68 mutex.lock();
69 if( !qObject ) {
70 mutex.unlock();
71 return true;
74 // the signature char array
75 QVarLengthArray<char, 512> sig;
77 // serialize method name
78 int len = qstrlen( method );
79 if( len <= 0 ) {
80 mutex.unlock();
81 return false;
83 sig.append( method, len );
84 sig.append( '(' );
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;
92 int i;
93 for( i = 0; i<size && i<10; ++i ) {
94 argSlots[i].setData( slots );
95 ++slots;
98 else argSlots[0].setData( argSlot );
100 // serialize argument types
101 int i;
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 );
109 sig.append( ',' );
112 // finalize the signature
113 if( i==0 ) sig.append( ')' );
114 else sig[sig.size() - 1] = ')';
116 sig.append('\0');
118 // get the meta method
119 const QMetaObject *mo = qObject->metaObject();
121 int mi = mo->indexOfMethod( sig.constData() );
122 if( mi < 0 ) {
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() ) );
130 mutex.unlock();
131 return false;
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 );
140 void *retPtr = 0;
141 if( retSlot ) {
142 retPtr = QMetaType::construct( retType );
143 retGArg = QGenericReturnArgument( retTypeName, retPtr );
146 //do it!
147 bool success =
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 );
166 if( retPtr )
167 QMetaType::destroy( retType, retPtr );
169 mutex.unlock();
170 return success;
173 void QObjectProxy::invokeScMethod
174 ( PyrSymbol *method, const QList<QVariant> & args, PyrSlot *result,
175 bool locked )
177 qcProxyDebugMsg(1, QString("SC METHOD CALL [+++]: ") + QString(method->name) );
179 if( !locked ) {
180 QtCollider::lockLang();
183 if( _scObject ) {
184 QtCollider::runLang( _scObject, method, args, result );
186 else {
187 SetNil( 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) );
201 return;
202 case (QEvent::Type) QtCollider::Event_Proxy_SetProperty:
203 setPropertyEvent( static_cast<SetPropertyEvent*>(event) );
204 return;
205 case (QEvent::Type) QtCollider::Event_Proxy_Destroy:
206 destroyEvent( static_cast<DestroyEvent*>(event) );
207 return;
208 default: ;
212 bool QObjectProxy::setParent( QObjectProxy *parentProxy ) {
213 if( qObject && parentProxy->object() )
214 qObject->setParent( parentProxy->object() );
216 return true;
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.")
224 .arg( property ) );
226 return false;
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;
243 data.type = type;
244 data.method = method;
245 data.sync = sync;
247 // if 'enable' is true, insert the new event handler enabled,
248 // otherwise copy current state, or set disabled if none.
249 if( enable ) {
250 data.enabled = true;
252 else {
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 );
261 return true;
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 ) {
270 d.enabled = enabled;
271 _eventHandlers.insert( type, d );
274 return true;
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 );
289 return true;
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 );
302 return true;
304 else {
305 delete handler;
306 return false;
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 );
317 if( sigId < 0 ) {
318 qcDebugMsg( 1, QString("WARNING: No such signal: '%1'").arg(signal.constData()) );
319 return false;
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);
326 delete h;
327 return true;
331 return false;
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 );
341 if( sigId < 0 ) {
342 qcDebugMsg( 1, QString("WARNING: No such signal: '%1'").arg(signal.constData()) );
343 return false;
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);
350 delete h;
351 return true;
355 return false;
358 void QObjectProxy::destroy( DestroyAction action )
360 switch( action ) {
361 case DestroyObject:
362 delete qObject;
363 return;
364 case DestroyProxy:
365 delete this;
366 return;
367 case DestroyProxyAndObject:
368 delete qObject;
369 delete this;
370 return;
374 bool QObjectProxy::destroyEvent( DestroyEvent *e )
376 destroy( e->action() );
377 return true;
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;
395 if( obj ) {
396 if( className && !isKindOf( obj, className->u.classobj ) )
397 continue;
398 scChildren.append( obj );
402 return scChildren;
405 PyrObject *QObjectProxy::parent( PyrSymbol *className )
407 if( !qObject ) return 0;
409 QObject *parent = qObject->parent();
411 while( parent ) {
412 // see if this parent has a corresponding proxy
413 QObjectProxy *proxy = QObjectProxy::fromObject( parent );
414 if( proxy ) {
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 ) ) {
423 return scobj;
427 // if this parent was not appropriate continue to consider the parent's parent
428 parent = parent->parent();
431 return 0;
434 bool QObjectProxy::eventFilter( QObject * watched, QEvent * event )
436 int type = event->type();
438 EventHandlerData eh;
439 QList<QVariant> args;
441 if( !filterEvent( watched, event, eh, args ) ) {
442 qcProxyDebugMsg(3,QString("Event (%1, %2) not handled, forwarding to the widget")
443 .arg(type)
444 .arg(event->spontaneous() ? "spontaneous" : "inspontaneous") );
445 return false;
448 qcProxyDebugMsg(1,QString("Will handle event (%1, %2) -> (%3, %4)")
449 .arg(type)
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 ) {
462 PyrSlot result;
463 invokeScMethod( method, args, &result );
464 if( IsTrue( &result ) ) {
465 qcProxyDebugMsg(2,"Event accepted");
466 event->accept();
467 return true;
469 else if( IsFalse( &result ) ) {
470 qcProxyDebugMsg(2,"Event ignored");
471 event->ignore();
472 return true;
475 else {
476 ScMethodCallEvent *e = new ScMethodCallEvent( method, args );
477 QApplication::postEvent( this, e );
480 qcProxyDebugMsg(2,"Forwarding event to the system");
481 return false;
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;
503 return 0;