Variant getValue( const PropertyAccessor & pa ) { // This createKey is added as a work-around for objects that were // not registered with the IObjectManager properly on creation. // // Objects need a key to be shared with other plugins. // E.g. getValue() will be sharing your object with plg_command_manager. // // Better to register your object with IObjectManager::registerObject() // or IObjectManager::registerUnmanagedObject() when you create the // ObjectHandle. // // @see IObjectManager Key key; if (!createKey( pa, key )) { return pa.getValue(); } // TODO: assert access is only on the main thread auto range = commands_.equal_range( key ); for (auto it = range.first; it != range.second; ++it) { auto instance = it->second; commandManager_.waitForInstance(instance); } commands_.erase( range.first, range.second ); return pa.getValue(); }
TEST_F( TestCommandFixture, undo_redo ) { auto & controller = getReflectionController(); auto objHandle = klass_->createManagedObject(); PropertyAccessor counter = klass_->bindProperty("counter", objHandle ); CHECK(counter.isValid()); int oldValue = -1; { Variant variant = counter.getValue(); CHECK(variant.tryCast( oldValue )); } auto & commandSystemProvider = getCommandSystemProvider(); const int TEST_VALUE = 57; { int value = TEST_VALUE; controller.setValue( counter, value ); } { int value = 0; Variant variant = controller.getValue( counter ); CHECK(variant.tryCast( value )); CHECK_EQUAL(TEST_VALUE, value); } { commandSystemProvider.undo(); CHECK(!commandSystemProvider.canUndo()); CHECK(commandSystemProvider.canRedo()); int value = 0; Variant variant = controller.getValue( counter ); CHECK(variant.tryCast( value )); CHECK_EQUAL(oldValue, value); } { commandSystemProvider.redo(); CHECK(commandSystemProvider.canUndo()); CHECK(!commandSystemProvider.canRedo()); int value = 0; Variant variant = controller.getValue( counter ); CHECK(variant.tryCast( value )); CHECK_EQUAL(TEST_VALUE, value); } }
//============================================================================== bool SetReflectedPropertyCommand::validateArguments(const ObjectHandle& arguments) const { if ( !arguments.isValid() ) { return false; } auto commandArgs = arguments.getBase< ReflectedPropertyCommandArgument >(); if ( commandArgs == nullptr ) { return false; } auto objManager = definitionManager_.getObjectManager(); if ( objManager == nullptr ) { return false; } const ObjectHandle & object = objManager->getObject( commandArgs->getContextId() ); if (!object.isValid()) { return false; } const IClassDefinition* defn = object.getDefinition( definitionManager_ ); PropertyAccessor property = defn->bindProperty(commandArgs->getPropertyPath(), object ); if (property.isValid() == false) { return false; } const MetaType * dataType = commandArgs->getPropertyValue().type(); const MetaType * propertyValueType = property.getValue().type(); if ( !dataType->canConvertTo(propertyValueType) ) { return false; } return true; }
//------------------------------------------------------------------------------ void ClassDefinition::bindPropertyImpl( const char * name, const ObjectHandle & pBase, PropertyAccessor & o_PropertyAccessor ) const { if (!*name) { // empty name causes noop return; } // find property operator auto propOperator = name; while (true) { if( !*propOperator || *propOperator == INDEX_OPEN || *propOperator == DOT_OPERATOR ) { break; } propOperator += 1; } auto propName = name; auto propLength = propOperator - propName; auto baseProp = findProperty( propName, propLength ); if (baseProp == nullptr) { // error: property `propName` is not found o_PropertyAccessor.setBaseProperty( nullptr ); return; } o_PropertyAccessor.setObject( pBase ); o_PropertyAccessor.setBaseProperty( baseProp ); assert( strncmp( propName, o_PropertyAccessor.getName(), propLength ) == 0 ); if (!*propOperator) { // no operator, so that's it return; } Variant propVal = o_PropertyAccessor.getValue(); if (*propOperator == INDEX_OPEN) { auto wholeIndex = propOperator; // read "multidimensional" indices without recursive bind (optimization) while (true) { Collection collection; if (!propVal.tryCast( collection )) { // error: index operator is applicable to collections only o_PropertyAccessor.setBaseProperty( nullptr ); return; } // determine key type (heterogeneous keys are not supported yet) const auto begin = collection.begin(); const auto end = collection.end(); if (begin == end) { // error: can't index empty collection o_PropertyAccessor.setBaseProperty( nullptr ); return; } // read key Variant key( begin.key().type() ); { propOperator += 1; // skip INDEX_OPEN FixedMemoryStream dataStream( propOperator ); TextStream stream( dataStream ); stream >> key >> match( INDEX_CLOSE ); if (stream.fail()) { // error: either key can't be read, or it isn't followed by INDEX_CLOSE o_PropertyAccessor.setBaseProperty( nullptr ); return; } // skip key and closing bracket propOperator += stream.seek( 0, std::ios_base::cur ); } auto it = collection.find( key ); // If (it == end), still return a valid property accessor to end, // rather than an invalid property accessor. if (!*propOperator || (it == end)) { // name parsing is completed auto baseProp = std::make_shared< CollectionElementHolder >( collection, it, collection.valueType(), wholeIndex ); // o_PropertyAccessor.setObject(); - keep current base o_PropertyAccessor.setBaseProperty( baseProp ); return; } propVal = it.value(); if (*propOperator == INDEX_OPEN) { continue; } // parse next operator break; } }