bool FPropertyEditor::GetEditConditionPropertyAddress( UBoolProperty*& ConditionProperty, FPropertyNode& InPropertyNode, TArray<FPropertyConditionInfo>& ConditionPropertyAddresses ) { bool bResult = false; bool bNegate = false; UBoolProperty* EditConditionProperty = PropertyCustomizationHelpers::GetEditConditionProperty(InPropertyNode.GetProperty(), bNegate); if ( EditConditionProperty != NULL ) { FPropertyNode* ParentNode = InPropertyNode.GetParentNode(); check(ParentNode); UProperty* Property = InPropertyNode.GetProperty(); if (Property) { bool bStaticArray = (Property->ArrayDim > 1) && (InPropertyNode.GetArrayIndex() != INDEX_NONE); if (bStaticArray) { //in the case of conditional static arrays, we have to go up one more level to get the proper parent struct. ParentNode = ParentNode->GetParentNode(); check(ParentNode); } } auto ComplexParentNode = ParentNode->FindComplexParent(); if (ComplexParentNode) { for (int32 Index = 0; Index < ComplexParentNode->GetInstancesNum(); ++Index) { TWeakObjectPtr<UObject> Object = ComplexParentNode->GetInstanceAsUObject(Index); if( Object.IsValid() ) { UObject* Obj = Object.Get(); // Get the address corresponding to the base of this property (i.e. if a struct property, set BaseOffset to the address of value for the whole struct) uint8* BaseOffset = ParentNode->GetValueAddress((uint8*)Obj); check(BaseOffset != NULL); FPropertyConditionInfo NewCondition; // now calculate the address of the property value being used as the condition and add it to the array. NewCondition.Address = EditConditionProperty->ContainerPtrToValuePtr<uint8>(BaseOffset); NewCondition.bNegateValue = bNegate; ConditionPropertyAddresses.Add(NewCondition); bResult = true; } } } } if ( bResult ) { // set the output variable ConditionProperty = EditConditionProperty; } return bResult; }
bool FObjectPropertyNode::GetReadAddressUncached(FPropertyNode& InNode, bool InRequiresSingleSelection, FReadAddressListData& OutAddresses, bool bComparePropertyContents, bool bObjectForceCompare, bool bArrayPropertiesCanDifferInSize) const { // Are any objects selected for property editing? if( !GetNumObjects()) { return false; } UProperty* InItemProperty = InNode.GetProperty(); // Is there a InItemProperty bound to the InItemProperty window? if( !InItemProperty ) { return false; } // Requesting a single selection? if( InRequiresSingleSelection && GetNumObjects() > 1) { // Fail: we're editing properties for multiple objects. return false; } //assume all properties are the same unless proven otherwise bool bAllTheSame = true; ////////////////////////////////////////// // If this item is the child of an array, return NULL if there is a different number // of items in the array in different objects, when multi-selecting. if( Cast<UArrayProperty>(InItemProperty->GetOuter()) ) { FPropertyNode* ParentNode = InNode.GetParentNode(); check(ParentNode); const UObject* TempObject = GetUObject(0); if( TempObject ) { uint8* BaseAddr = ParentNode->GetValueBaseAddress( (uint8*)TempObject ); if( BaseAddr ) { const int32 Num = FScriptArrayHelper::Num(BaseAddr); for( int32 ObjIndex = 1 ; ObjIndex < GetNumObjects(); ObjIndex++ ) { TempObject = GetUObject(ObjIndex); BaseAddr = ParentNode->GetValueBaseAddress( (uint8*)TempObject ); if( BaseAddr && Num != FScriptArrayHelper::Num( BaseAddr ) ) { bAllTheSame = false; } } } } } uint8* Base = GetUObject(0) ? InNode.GetValueBaseAddress( (uint8*)(GetUObject(0)) ) : NULL; if (Base) { // If the item is an array itself, return NULL if there are a different number of // items in the array in different objects, when multi-selecting. if( Cast<UArrayProperty>(InItemProperty) ) { // This flag is an override for array properties which want to display e.g. the "Clear" and "Empty" // buttons, even though the array properties may differ in the number of elements. if ( !bArrayPropertiesCanDifferInSize ) { const UObject* TempObject = GetUObject(0); int32 const Num = FScriptArrayHelper::Num(InNode.GetValueBaseAddress( (uint8*)TempObject)); for( int32 ObjIndex = 1 ; ObjIndex < GetNumObjects() ; ObjIndex++ ) { TempObject = GetUObject(ObjIndex); if( TempObject && Num != FScriptArrayHelper::Num(InNode.GetValueBaseAddress((uint8*)TempObject)) ) { bAllTheSame = false; } } } } else { if ( bComparePropertyContents || !Cast<UObjectPropertyBase>(InItemProperty) || bObjectForceCompare ) { // Make sure the value of this InItemProperty is the same in all selected objects. for( int32 ObjIndex = 1 ; ObjIndex < GetNumObjects() ; ObjIndex++ ) { const UObject* TempObject = GetUObject(ObjIndex); if( !InItemProperty->Identical( Base, InNode.GetValueBaseAddress( (uint8*)TempObject ) ) ) { bAllTheSame = false; } } } else { if ( Cast<UObjectPropertyBase>(InItemProperty) ) { // We don't want to exactly compare component properties. However, we // need to be sure that all references are either valid or invalid. // If BaseObj is NON-NULL, all other objects' properties should also be non-NULL. // If BaseObj is NULL, all other objects' properties should also be NULL. UObject* BaseObj = Cast<UObjectPropertyBase>(InItemProperty)->GetObjectPropertyValue(Base); for( int32 ObjIndex = 1 ; ObjIndex < GetNumObjects() ; ObjIndex++ ) { const UObject* TempObject = GetUObject(ObjIndex); UObject* CurObj = Cast<UObjectPropertyBase>(InItemProperty)->GetObjectPropertyValue(InNode.GetValueBaseAddress( (uint8*)TempObject )); if ( ( !BaseObj && CurObj ) // BaseObj is NULL, but this InItemProperty is non-NULL! || ( BaseObj && !CurObj ) ) // BaseObj is non-NULL, but this InItemProperty is NULL! { bAllTheSame = false; } } } } } } // Write addresses to the output. for ( int32 ObjIndex = 0 ; ObjIndex < GetNumObjects(); ++ObjIndex ) { const UObject* TempObject = GetUObject(ObjIndex); if( TempObject ) { OutAddresses.Add( TempObject, InNode.GetValueBaseAddress( (uint8*)(TempObject) ) ); } } // Everything checked out and we have usable addresses. return bAllTheSame; }