// // SharedObject.cpp // libraries/metavoxels/src // // Created by Andrzej Kapolka on 2/5/14. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include #include #include #include #include #include #include "Bitstream.h" #include "MetavoxelUtil.h" #include "SharedObject.h" REGISTER_META_OBJECT(SharedObject) SharedObject::SharedObject() : _id(_nextID.fetchAndAddOrdered(1)), _originID(_id), _remoteID(0), _remoteOriginID(0) { QWriteLocker locker(&_weakHashLock); _weakHash.insert(_id, this); } void SharedObject::setID(int id) { QWriteLocker locker(&_weakHashLock); _weakHash.remove(_id); _weakHash.insert(_id = id, this); } void SharedObject::incrementReferenceCount() { _referenceCount.ref(); } void SharedObject::decrementReferenceCount() { if (!_referenceCount.deref()) { { QWriteLocker locker(&_weakHashLock); _weakHash.remove(_id); } delete this; } } SharedObject* SharedObject::clone(bool withID, SharedObject* target) const { // default behavior is to make a copy using the no-arg constructor and copy the stored properties const QMetaObject* metaObject = this->metaObject(); if (!target) { target = static_cast(metaObject->newInstance()); } for (int i = 0; i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (property.isStored()) { if (property.userType() == qMetaTypeId()) { SharedObject* value = property.read(this).value().data(); property.write(target, QVariant::fromValue(value ? value->clone(withID) : value)); } else { property.write(target, property.read(this)); } } } foreach (const QByteArray& propertyName, dynamicPropertyNames()) { target->setProperty(propertyName, property(propertyName)); } if (withID) { target->setOriginID(_originID); } return target; } bool SharedObject::equals(const SharedObject* other, bool sharedAncestry) const { if (!other) { return false; } if (other == this) { return true; } // default behavior is to compare the properties const QMetaObject* metaObject = this->metaObject(); if (metaObject != other->metaObject() && !sharedAncestry) { return false; } // use the streamer, if we have one const ObjectStreamer* streamer = Bitstream::getObjectStreamer(metaObject); if (streamer) { if (!streamer->equal(this, other)) { return false; } } else { for (int i = 0; i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (property.isStored() && property.read(this) != property.read(other)) { return false; } } } QList dynamicPropertyNames = this->dynamicPropertyNames(); if (dynamicPropertyNames.size() != other->dynamicPropertyNames().size()) { return false; } foreach (const QByteArray& propertyName, dynamicPropertyNames) { if (property(propertyName) != other->property(propertyName)) { return false; } } return true; } void SharedObject::dump(QDebug debug) const { debug << this; const QMetaObject* metaObject = this->metaObject(); for (int i = 0; i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (property.isStored()) { debug << property.name() << property.read(this); } } QList dynamicPropertyNames = this->dynamicPropertyNames(); foreach (const QByteArray& propertyName, dynamicPropertyNames) { debug << propertyName << property(propertyName); } } QAtomicInt SharedObject::_nextID(1); WeakSharedObjectHash SharedObject::_weakHash; QReadWriteLock SharedObject::_weakHashLock; void pruneWeakSharedObjectHash(WeakSharedObjectHash& hash) { for (WeakSharedObjectHash::iterator it = hash.begin(); it != hash.end(); ) { if (!it.value()) { it = hash.erase(it); } else { it++; } } } SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, bool nullable, QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(); layout->setAlignment(Qt::AlignTop); setLayout(layout); QFormLayout* form = new QFormLayout(); layout->addLayout(form); form->addRow("Type:", _type = new QComboBox()); if (nullable) { _type->addItem("(none)"); } foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(metaObject)) { // add add constructable subclasses if (metaObject->constructorCount() > 0) { _type->addItem(metaObject->className(), QVariant::fromValue(metaObject)); } } connect(_type, SIGNAL(currentIndexChanged(int)), SLOT(updateType())); updateType(); } void SharedObjectEditor::setObject(const SharedObjectPointer& object) { _object = object; const QMetaObject* metaObject = object ? object->metaObject() : NULL; int index = _type->findData(QVariant::fromValue(metaObject)); if (index != -1) { // ensure that we call updateType to obtain the values if (_type->currentIndex() == index) { updateType(); } else { _type->setCurrentIndex(index); } } } void SharedObjectEditor::detachObject() { SharedObject* oldObject = _object.data(); if (!_object.detach()) { return; } oldObject->disconnect(this); const QMetaObject* metaObject = _object->metaObject(); QFormLayout* form = static_cast(layout()->itemAt(1)); for (int i = 0; i < form->rowCount(); i++) { QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); QMetaProperty property = metaObject->property(widget->property("propertyIndex").toInt()); connect(_object.data(), signal(property.notifySignal().methodSignature()), SLOT(updateProperty())); } } const QMetaObject* getOwningAncestor(const QMetaObject* metaObject, int propertyIndex) { while (propertyIndex < metaObject->propertyOffset()) { metaObject = metaObject->superClass(); } return metaObject; } void SharedObjectEditor::updateType() { // delete the existing rows if (layout()->count() > 1) { QFormLayout* form = static_cast(layout()->takeAt(1)); while (!form->isEmpty()) { QLayoutItem* item = form->takeAt(0); if (item->widget()) { delete item->widget(); } delete item; } delete form; } QObject* oldObject = static_cast(_object.data()); const QMetaObject* oldMetaObject = NULL; if (oldObject) { oldMetaObject = oldObject->metaObject(); oldObject->disconnect(this); } const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value(); if (!metaObject) { _object.reset(); return; } QObject* newObject = metaObject->newInstance(); QFormLayout* form = new QFormLayout(); static_cast(layout())->addLayout(form); for (int i = QObject::staticMetaObject.propertyCount(); i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (!property.isDesignable()) { continue; } if (oldMetaObject && i < oldMetaObject->propertyCount() && getOwningAncestor(metaObject, i) == getOwningAncestor(oldMetaObject, i)) { // copy the state of the shared ancestry property.write(newObject, property.read(oldObject)); } QWidget* widget = QItemEditorFactory::defaultFactory()->createEditor(property.userType(), NULL); if (widget) { widget->setProperty("propertyIndex", i); form->addRow(QByteArray(property.name()) + ':', widget); QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); const QMetaObject* widgetMetaObject = widget->metaObject(); QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName)); widgetProperty.write(widget, property.read(newObject)); if (widgetProperty.hasNotifySignal()) { connect(widget, signal(widgetProperty.notifySignal().methodSignature()), SLOT(propertyChanged())); } if (property.hasNotifySignal()) { widget->setProperty("notifySignalIndex", property.notifySignalIndex()); connect(newObject, signal(property.notifySignal().methodSignature()), SLOT(updateProperty())); } } } _object = static_cast(newObject); } void SharedObjectEditor::propertyChanged() { QFormLayout* form = static_cast(layout()->itemAt(1)); for (int i = 0; i < form->rowCount(); i++) { QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); if (widget != sender()) { continue; } detachObject(); QObject* object = _object.data(); QMetaProperty property = object->metaObject()->property(widget->property("propertyIndex").toInt()); QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); property.write(object, widget->property(valuePropertyName)); } } void SharedObjectEditor::updateProperty() { QFormLayout* form = static_cast(layout()->itemAt(1)); for (int i = 0; i < form->rowCount(); i++) { QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget(); if (widget->property("notifySignalIndex").toInt() != senderSignalIndex()) { continue; } QMetaProperty property = _object->metaObject()->property(widget->property("propertyIndex").toInt()); QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType()); widget->setProperty(valuePropertyName, property.read(_object.data())); } }