00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "gluonobject.h"
00020 #include "gluonobjectprivate.h"
00021 #include "debughelper.h"
00022 #include "gluonvarianttypes.h"
00023 #include "metainfo.h"
00024
00025 #include <QtCore/QVariant>
00026 #include <QtCore/QPointF>
00027 #include <QtGui/QColor>
00028 #include <QtGui/QVector3D>
00029 #include <QtCore/QDebug>
00030 #include <QtCore/QMetaClassInfo>
00031 #include <QSizeF>
00032
00033 REGISTER_OBJECTTYPE( GluonCore, GluonObject )
00034
00035 using namespace GluonCore;
00036
00037 static int qlist_qurl_typeID = qRegisterMetaType< QList<QUrl> >();
00038
00039 GluonObject::GluonObject( QObject* parent )
00040 : QObject( parent )
00041 , d( new GluonObjectPrivate() )
00042 {
00043
00044 QString theClassName( metaObject()->className() );
00045 if( theClassName.contains( ':' ) )
00046 setName( theClassName.right( theClassName.length() - theClassName.lastIndexOf( ':' ) - 1 ) );
00047 else
00048 setName( theClassName );
00049 }
00050
00051 GluonObject::GluonObject( const QString& name, QObject* parent )
00052 : QObject( parent )
00053 , d( new GluonObjectPrivate() )
00054 {
00055 setName( name );
00056 }
00057
00058 GluonObject::GluonObject( const GluonCore::GluonObject& rt )
00059 : d( new GluonObjectPrivate( *rt.d ) )
00060 {
00061 }
00062
00063 GluonObject::~GluonObject()
00064 {
00065 }
00066
00067 void
00068 GluonObject::debug( const QString& debugText ) const
00069 {
00070 DEBUG_BLOCK
00071 DEBUG_TEXT( debugText );
00072 emit showDebug( QString( "%1: %2" ).arg( fullyQualifiedName() ).arg( debugText ) );
00073 }
00074
00075 void
00076 GluonObject::debug( const QString& debugText, const QString& arg ) const
00077 {
00078 DEBUG_BLOCK
00079 DEBUG_TEXT2( debugText, arg );
00080 emit showDebug( QString( "%1: %2" ).arg( fullyQualifiedName() ).arg( debugText.arg( arg ) ) );
00081 }
00082
00083 GluonObject *
00084 GluonObject::clone() const
00085 {
00086 const QMetaObject* metaObj = metaObject();
00087 if( !metaObj )
00088 debug( QString( "Failed to get the meta object for object %1" ).arg( fullyQualifiedName() ) );
00089
00090
00091 GluonObject* parentObject = qobject_cast<GluonObject*>( parent() );
00092 if( !parentObject )
00093 return 0;
00094
00095 return clone( parentObject );
00096 }
00097
00098 GluonObject *
00099 GluonObject::clone( GluonObject* parentObject ) const
00100 {
00101 const QMetaObject* metaObj = metaObject();
00102 if( !metaObj )
00103 debug( QString( "Failed to get the meta object for object %1" ).arg( fullyQualifiedName() ) );
00104
00105 GluonObject* newObject = GluonObjectFactory::instance()->instantiateObjectByName( metaObj->className() );
00106 parentObject->addChild( newObject );
00107
00108
00109 foreach( QObject * child, children() )
00110 {
00111 GluonObject* childObject = qobject_cast<GluonObject*>( child );
00112 if( childObject )
00113 {
00114 childObject->clone( newObject );
00115 }
00116 }
00117
00118
00119 int count = metaObj->propertyCount();
00120 for( int i = 0; i < count; ++i )
00121 {
00122 QMetaProperty metaproperty = metaObj->property( i );
00123 newObject->setProperty( metaproperty.name(), metaproperty.read( this ) );
00124 }
00125
00126
00127 QList<QByteArray> propertyNames = dynamicPropertyNames();
00128 foreach( const QByteArray & propName, propertyNames )
00129 {
00130 newObject->setProperty( propName, property( propName ) );
00131 }
00132
00133 #ifdef __GNUC__
00134 #warning Clones with properties pointing to children should point to new children rather than the old children
00135 #endif
00136
00137
00138 newObject->postCloneSanitize();
00139 return newObject;
00140 }
00141
00142 void
00143 GluonObject::sanitize()
00144 {
00145 DEBUG_BLOCK
00146
00147
00148 foreach( QObject * child, children() )
00149 {
00150
00151
00152 if( qobject_cast<GluonObject*>( child ) )
00153 {
00154 qobject_cast<GluonObject*>( child )->sanitize();
00155 }
00156 }
00157
00158
00159
00160 if( !gameProject() )
00161 {
00162 QObject* currentParent = parent();
00163 while( currentParent )
00164 {
00165 if( currentParent->metaObject()->className() == QString( "GluonEngine::GameProject" ) )
00166 {
00167 setGameProject( qobject_cast<GluonObject*>( currentParent ) );
00168 break;
00169 }
00170 currentParent = currentParent->parent();
00171 }
00172 }
00173
00174
00175
00176
00177 QStringList objectTypeNames = GluonObjectFactory::instance()->objectTypeNames();
00178
00179 const QMetaObject* metaobject = metaObject();
00180 if( metaobject == 0 )
00181 return;
00182 int count = metaobject->propertyCount();
00183 for( int i = 0; i < count; ++i )
00184 {
00185 QMetaProperty metaproperty = metaobject->property( i );
00186
00187
00188
00189 if( metaproperty.type() != QVariant::String )
00190 continue;
00191
00192 const QString theName( metaproperty.name() );
00193 if( theName == "objectName" || theName == "name" )
00194 continue;
00195
00196 QString theValue( metaproperty.read( this ).toString() );
00197
00198
00199 if( !theValue.endsWith( ')' ) )
00200 continue;
00201
00202
00203
00204 sanitizeReference( theName, theValue );
00205 }
00206
00207
00208 QList<QByteArray> propertyNames = dynamicPropertyNames();
00209 foreach( const QByteArray & propName, propertyNames )
00210 {
00211 const QString theName( propName );
00212 if( theName == "objectName" || theName == "name" )
00213 continue;
00214
00215
00216
00217 if( property( propName ).type() != QVariant::String )
00218 continue;
00219
00220 QString theValue( property( propName ).toString() );
00221
00222
00223 if( !theValue.endsWith( ')' ) )
00224 continue;
00225
00226
00227
00228 sanitizeReference( theName, theValue );
00229 }
00230 }
00231
00232 GluonObject *
00233 GluonObject::gameProject() const
00234 {
00235 return d->gameProject;
00236 }
00237
00238 void
00239 GluonObject::setGameProject( GluonObject* newGameProject )
00240 {
00241 d->gameProject = newGameProject;
00242
00243 const QObjectList& allChildren = children();
00244 foreach( QObject * child, allChildren )
00245 {
00246 GluonObject* gobj = qobject_cast<GluonObject*>( child );
00247 if( gobj )
00248 gobj->setGameProject( newGameProject );
00249 }
00250 }
00251
00252 QString
00253 GluonObject::name() const
00254 {
00255 return d->name;
00256 }
00257
00258 const QStringList
00259 GluonObject::supportedMimeTypes() const
00260 {
00261
00262 return QStringList();
00263 }
00264
00265 void
00266 GluonObject::setName( const QString& newName )
00267 {
00268
00269 if( newName.isEmpty() )
00270 return;
00271
00272
00273 d->name = "";
00274
00275 QString theName( newName );
00276
00277 theName.replace( '/', ' ' );
00278
00279
00280 if( parent() )
00281 {
00282 bool nameIsOK = true;
00283 int addedNumber = 0;
00284 QObjectList theChildren = parent()->children();
00285 do
00286 {
00287 addedNumber++;
00288 nameIsOK = true;
00289 foreach( QObject * child, theChildren )
00290 {
00291 GluonObject* theChild = qobject_cast<GluonObject*>( child );
00292 if( theChild )
00293 {
00294 if( theChild->name() == theName )
00295 {
00296 theName = QString( newName + " %1" ).arg( addedNumber );
00297 nameIsOK = false;
00298 break;
00299 }
00300 }
00301 }
00302 }
00303 while( !nameIsOK );
00304 }
00305 d->name = theName;
00306 setObjectName( d->name );
00307 }
00308
00309 QString
00310 GluonObject::fullyQualifiedName() const
00311 {
00312 QString theName( name() );
00313 if( qobject_cast<GluonObject*>( parent() ) )
00314 theName = QString( "%1/%2" ).arg( qobject_cast<GluonObject*>( parent() )->fullyQualifiedName() ).arg( theName );
00315 return theName;
00316 }
00317
00318 QString
00319 GluonObject::fullyQualifiedFileName() const
00320 {
00321 QString qualifiedName = fullyQualifiedName();
00322 QString ext;
00323 if( qualifiedName.contains( '.' ) )
00324 {
00325 ext = qualifiedName.section( '.', -1 ).toLower();
00326 qualifiedName = qualifiedName.left( qualifiedName.lastIndexOf( '.' ) ).toLower();
00327 }
00328
00329
00330 QRegExp rx( "[\\/\\\\\\:\\.,\\* ]" );
00331 qualifiedName.replace( rx, "_" );
00332 if( !ext.isEmpty() )
00333 qualifiedName.append( '.' + ext );
00334
00335 return qualifiedName;
00336 }
00337
00338 GluonCore::GluonObject *
00339 GluonObject::findItemByName( QString qualifiedName )
00340 {
00341
00342
00343 QStringList names = qualifiedName.split( '/' );
00344 if( names.at( 0 ) == name() )
00345 names.removeFirst();
00346 return findItemByNameInObject( names, this );
00347 }
00348
00349 GluonObject *
00350 GluonObject::root()
00351 {
00352 if( qobject_cast<GluonObject*>( parent() ) )
00353 return qobject_cast<GluonObject*>( parent() )->root();
00354 return this;
00355 }
00356
00357 void GluonObject::addChild( GluonObject* child )
00358 {
00359 child->setParent( this );
00360 child->setName( child->name() );
00361 }
00362
00363 GluonObject* GluonObject::child( int index ) const
00364 {
00365 return qobject_cast<GluonObject*>( children().at( index ) );
00366 }
00367
00368 GluonObject* GluonObject::child( const QString& name ) const
00369 {
00370 foreach( QObject * child, children() )
00371 {
00372 GluonObject* obj = qobject_cast<GluonObject*>( child );
00373 if( obj && obj->name() == name )
00374 return obj;
00375 }
00376 return 0;
00377 }
00378
00379 bool GluonObject::removeChild( GluonObject* child )
00380 {
00381 child->setParent( 0 );
00382 return true;
00383 }
00384
00385 QString
00386 GluonObject::toGDL( int indentLevel ) const
00387 {
00388
00389 QString serializedObject;
00390
00391
00392
00393 QString indentChars( indentLevel * 4, ' ' );
00394
00395
00396 if( indentLevel > 0 )
00397 serializedObject += '\n';
00398
00399 QString minimalClassName( metaObject()->className() );
00400 if( QString( metaObject()->className() ).startsWith( QString( "Gluon::" ) ) )
00401 minimalClassName = minimalClassName.right( minimalClassName.length() - 7 );
00402 serializedObject += QString( "%1{ %2(%3)" ).arg( indentChars ).arg( minimalClassName ).arg( name() );
00403
00404 serializedObject += propertiesToGDL( indentLevel + 1 );
00405 serializedObject += childrenToGDL( indentLevel + 1 );
00406
00407 return QString( "%1\n%2}" ).arg( serializedObject ).arg( indentChars );
00408 }
00409
00410 QString
00411 GluonObject::childrenToGDL( int indentLevel ) const
00412 {
00413 QString serializedChildren;
00414
00415
00416 foreach( QObject * child, children() )
00417 {
00418 GluonObject* theChild = qobject_cast<GluonObject*>( child );
00419 if( theChild )
00420 serializedChildren += theChild->toGDL( indentLevel );
00421 }
00422
00423 return serializedChildren;
00424 }
00425
00426 QString
00427 GluonObject::propertiesToGDL( int indentLevel ) const
00428 {
00429 DEBUG_BLOCK
00430 QString serializedObject;
00431
00432 QString indentChars( indentLevel * 4, ' ' );
00433
00434
00435 const QMetaObject* metaobject = metaObject();
00436 int count = metaobject->propertyCount();
00437 if( count == 2 )
00438 {
00439
00440 }
00441 for( int i = 0; i < count; ++i )
00442 {
00443 QMetaProperty metaproperty = metaobject->property( i );
00444 const QString theName( metaproperty.name() );
00445 if( theName == "objectName" || theName == "name" || !metaproperty.isWritable() )
00446 continue;
00447 serializedObject += stringFromProperty( theName, indentChars );
00448 }
00449
00450
00451 QList<QByteArray> propertyNames = dynamicPropertyNames();
00452 if( propertyNames.length() == 0 )
00453 {
00454
00455 }
00456 foreach( const QByteArray & propName, propertyNames )
00457 {
00458 const QString theName( propName );
00459 serializedObject += stringFromProperty( theName, indentChars );
00460 }
00461
00462 return serializedObject;
00463 }
00464
00465 void
00466 GluonObject::setPropertyFromString( const QString& propertyName, const QString& propertyValue )
00467 {
00468 DEBUG_BLOCK
00469 QVariant value;
00470
00471
00472 QRegExp rx( "((\\w*\\:*\\**)+)\\((.+)\\)" );
00473 rx.indexIn( propertyValue );
00474
00475 QString theTypeName = rx.cap( 1 );
00476 QString theValue = rx.cap( 3 );
00477
00478 if( theTypeName == "string" )
00479 {
00480 value = theValue;
00481 }
00482 else if( theTypeName == "bool" )
00483 {
00484 value = theValue == "true" ? true : false;
00485 }
00486 else if( theTypeName == "float" )
00487 {
00488 value = theValue.toFloat();
00489 }
00490 else if( theTypeName == "int" )
00491 {
00492 value = theValue.toInt();
00493 }
00494 else if( theTypeName == "uint" )
00495 {
00496 value = theValue.toUInt();
00497 }
00498 else if( theTypeName == "file" || theTypeName == "url" )
00499 {
00500
00501 value = QVariant( QUrl( theValue ) );
00502 }
00503 else if( theTypeName == "vector2d" )
00504 {
00505 float x = 0.0f, y = 0.0f;
00506
00507 QStringList splitValues = theValue.split( ';' );
00508 if( splitValues.length() > 0 )
00509 {
00510 x = splitValues.at( 0 ).toFloat();
00511 y = splitValues.at( 1 ).toFloat();
00512 }
00513 value = QPointF( x, y );
00514 }
00515 else if( theTypeName == "vector3d" )
00516 {
00517 float x = 0.0f, y = 0.0f, z = 0.0f;
00518
00519 QStringList splitValues = theValue.split( ';' );
00520 if( splitValues.length() > 0 )
00521 {
00522 x = splitValues.at( 0 ).toFloat();
00523 y = splitValues.at( 1 ).toFloat();
00524 z = splitValues.at( 2 ).toFloat();
00525 }
00526 value = QVector3D( x, y, z );
00527 }
00528 else if( theTypeName == "quaternion" )
00529 {
00530 float x = 0.0f, y = 0.0f, z = 0.0f, w = 0.0f;
00531
00532 QStringList splitValues = theValue.split( ';' );
00533 if( splitValues.length() > 0 )
00534 {
00535 x = splitValues.at( 0 ).toFloat();
00536 y = splitValues.at( 1 ).toFloat();
00537 z = splitValues.at( 2 ).toFloat();
00538 w = splitValues.at( 3 ).toFloat();
00539 }
00540
00541 value = QQuaternion( w, x, y, z );
00542 }
00543 else if( theTypeName == "rgba" )
00544 {
00545 int r = 0, g = 0, b = 0, a = 0;
00546 QStringList splitValues = theValue.split( ';' );
00547 if( splitValues.length() > 0 )
00548 r = splitValues[0].toInt();
00549 if( splitValues.length() > 1 )
00550 g = splitValues[1].toInt();
00551 if( splitValues.length() > 2 )
00552 b = splitValues[2].toInt();
00553 if( splitValues.length() > 3 )
00554 a = splitValues[3].toInt();
00555 value = QColor( r, g, b, a );
00556 }
00557 else if( theTypeName == "size2d" )
00558 {
00559 float w = 0.0f;
00560 float h = 0.0f;
00561
00562 QStringList splitValues = theValue.split( ';' );
00563 if( splitValues.length() > 0 )
00564 {
00565 w = splitValues.at( 0 ).toFloat();
00566 h = splitValues.at( 1 ).toFloat();
00567 }
00568 value = QSizeF( w, h );
00569 }
00570 else
00571 {
00572
00573 value = propertyValue;
00574
00575
00576 setProperty(( propertyName + "_sanitizable" ).toUtf8(), value );
00577 return;
00578 }
00579
00580 setProperty( propertyName.toUtf8(), value );
00581 }
00582
00583 QString
00584 GluonObject::stringFromProperty( const QString& propertyName, const QString& indentChars ) const
00585 {
00586 DEBUG_BLOCK
00587 QString value;
00588
00589 QVariant theValue = property( propertyName.toUtf8() );
00590
00591 QColor theColor;
00592 QVector3D theVector;
00593 QQuaternion theQuat;
00594 switch( theValue.type() )
00595 {
00596
00597
00598
00599
00600 case QVariant::String:
00601 if( !theValue.toString().isEmpty() )
00602 value = "string(" + theValue.toString() + ')';
00603 break;
00604 case QVariant::Bool:
00605 value = QString( "bool(%1)" ).arg( theValue.toString() );
00606 break;
00607
00608 case QVariant::Double:
00609 if( theValue.toDouble() )
00610 value = QString( "float(%1)" ).arg( theValue.toDouble() );
00611 break;
00612 case QVariant::Vector3D:
00613 theVector = theValue.value<QVector3D>();
00614 value = QString( "vector3d(%1;%2;%3)" ).arg( theVector.x() ).arg( theVector.y() ).arg( theVector.z() );
00615 break;
00616 case QVariant::Quaternion:
00617 theQuat = theValue.value<QQuaternion>();
00618 value = QString( "quaternion(%1;%2;%3;%4)" ).arg( theQuat.x() ).arg( theQuat.y() ).arg( theQuat.z() ).arg( theQuat.scalar() );
00619 break;
00620 case QVariant::Int:
00621 if( theValue.toInt() )
00622 value = QString( "int(%1)" ).arg( theValue.toInt() );
00623 break;
00624 case QVariant::UInt:
00625 if( theValue.toUInt() )
00626 value = QString( "uint(%1)" ).arg( theValue.toUInt() );
00627 break;
00628 case QVariant::Size:
00629 case QVariant::SizeF:
00630 value = QString( "size2d(%1;%2)" ).arg( theValue.toSizeF().width() ).arg( theValue.toSizeF().height() );
00631 break;
00632 case QVariant::PointF:
00633 case QVariant::Vector2D:
00634 value = QString( "vector2d(%1;%2)" ).arg( theValue.toPointF().x() ).arg( theValue.toPointF().y() );
00635 break;
00636 case QVariant::Color:
00637 theColor = theValue.value<QColor>();
00638 value = QString( "rgba(%1;%2;%3;%4)" ).arg( theColor.red() ).arg( theColor.green() ).arg( theColor.blue() ).arg( theColor.alpha() );
00639 break;
00640 case QVariant::Url:
00641 if( !theValue.toUrl().isEmpty() )
00642 {
00643 if( theValue.toString().startsWith( QLatin1String( "file" ) ) )
00644 value = QString( "file(%1)" ).arg( theValue.toUrl().toString() );
00645 else
00646 value = QString( "url(%1)" ).arg( theValue.toUrl().toString() );
00647 }
00648 break;
00649 default:
00650 GluonObject* theObject = GluonObjectFactory::instance()->wrappedObject( theValue );
00651 if( theObject )
00652 {
00653 value = QString( "%1(%2)" ).arg( theObject->metaObject()->className() ).arg( theObject->fullyQualifiedName() );
00654 }
00655 else
00656 {
00657 DEBUG_TEXT( QString( "Property %1 is of an unrecognised type %2" ).arg( propertyName ).arg( theValue.typeName() ) );
00658 value = theValue.toString();
00659 }
00660 break;
00661 }
00662
00663 QString returnString = QString( "\n%1%2 %3" ).arg( indentChars ).arg( propertyName ).arg( value );
00664
00665 if( value.isEmpty() )
00666 {
00667 value = QString( "(empty value)" );
00668 returnString.clear();
00669 }
00670
00671
00672
00673 return returnString;
00674 }
00675
00676 GluonObject *
00677 GluonObject::findItemByNameInObject( QStringList qualifiedName, GluonObject* object )
00678 {
00679
00680 DEBUG_BLOCK
00681 GluonObject* foundChild = 0;
00682
00683 QString lookingFor( qualifiedName[0] );
00684 qualifiedName.removeFirst();
00685
00686
00687 foreach( QObject * child, object->children() )
00688 {
00689 if( qobject_cast<GluonObject*>( child ) )
00690 {
00691 if( qobject_cast<GluonObject*>( child )->name() == lookingFor )
00692 {
00693 foundChild = qobject_cast<GluonObject*>( child );
00694 break;
00695 }
00696 }
00697 }
00698
00699
00700 if( foundChild != 0 )
00701 {
00702 if( qualifiedName.count() > 0 )
00703 {
00704
00705 return GluonObject::findItemByNameInObject( qualifiedName, foundChild );
00706 }
00707 else
00708 {
00709
00710 }
00711 }
00712 else
00713 {
00714 DEBUG_TEXT( "Did not find child! Bailing out" );
00715 }
00716
00717 return foundChild;
00718 }
00719
00720
00721 void
00722 GluonObject::sanitizeReference( const QString& propName, const QString& propValue )
00723 {
00724 QStringList objectTypeNames = GluonObjectFactory::instance()->objectTypeNames();
00725
00726 foreach( const QString & typeName, objectTypeNames )
00727 {
00728
00729
00730 if( propValue.startsWith( typeName + '(' ) )
00731 {
00732 QString propertyName = propName;
00733 if( propertyName.contains( "_sanitizable" ) )
00734 {
00735 setProperty( propertyName.toUtf8(), QVariant() );
00736 propertyName = propertyName.left( propertyName.lastIndexOf( "_sanitizable" ) );
00737 }
00738
00739 QString theReferencedName = propValue.mid( typeName.length() + 1, propValue.length() - ( typeName.length() + 2 ) );
00740 QVariant theReferencedObject;
00741
00742 GluonObject* theObject = root()->findItemByName( theReferencedName );
00743
00744 if( !theObject )
00745 {
00746 debug( QString( "Warning: Invalid reference for property %1 on object %2" ).arg( propertyName, name() ) );
00747 return;
00748 }
00749
00750 QMetaProperty property = metaObject()->property( metaObject()->indexOfProperty( propertyName.toUtf8() ) );
00751 theReferencedObject = GluonObjectFactory::instance()->wrapObject( QString( property.typeName() ), theObject );
00752
00753 setProperty( propertyName.toUtf8(), theReferencedObject );
00754 break;
00755 }
00756 }
00757 }
00758
00759 MetaInfo *
00760 GluonObject::metaInfo()
00761 {
00762 if( !d->metaInfo )
00763 d->metaInfo = new MetaInfo( this );
00764 return d->metaInfo;
00765 }
00766
00767 bool
00768 GluonObject::hasMetaInfo() const
00769 {
00770 if( d->metaInfo )
00771 return true;
00772 return false;
00773 }
00774
00775 #include "gluonobject.moc"