コード例 #1
0
/**
 * Finds a child property node from the provided parent node (does not recurse into grandchildren)
 *
 * @param InParentNode	The parent node to locate the child from
 * @param PropertyName	The property name to find
 * @param Index			The index of the property if its in an array
 */
static TSharedPtr<FPropertyNode> FindChildPropertyNode( FPropertyNode& InParentNode, const FString& PropertyName, int32 Index )
{
	TSharedPtr<FPropertyNode> FoundNode(NULL);

	// search each child for a property with the provided name
	for( int32 ChildIndex = 0; ChildIndex < InParentNode.GetNumChildNodes(); ++ChildIndex )
	{
		TSharedPtr<FPropertyNode>& ChildNode = InParentNode.GetChildNode(ChildIndex);
		UProperty* Property = ChildNode->GetProperty();
		if( Property && Property->GetFName() == *PropertyName )
		{
			FoundNode = ChildNode;
			break;
		}
	}

	// Find the array element.
	if( FoundNode.IsValid() && Index != INDEX_NONE )
	{
		// The found node is the top array so get its child which is the actual node
		FoundNode = FoundNode->GetChildNode( Index );
	}

	return FoundNode;
}
コード例 #2
0
/**
* Determines whether or not a property should be visible in the default generated detail layout
*
* @param PropertyNode	The property node to check
* @param ParentNode	The parent property node to check
* @return true if the property should be visible
*/
static bool IsVisibleStandaloneProperty(const FPropertyNode& PropertyNode, const FPropertyNode& ParentNode)
{
	const UProperty* Property = PropertyNode.GetProperty();
	const UArrayProperty* ParentArrayProperty = Cast<const UArrayProperty>(ParentNode.GetProperty());
	bool bIsVisibleStandalone = false;
	if (Property)
	{
		if (Property->IsA(UObjectPropertyBase::StaticClass()))
		{
			// Do not add this child node to the current map if its a single object property in a category (serves no purpose for UI)
			bIsVisibleStandalone = !ParentArrayProperty && (PropertyNode.GetNumChildNodes() == 0 || PropertyNode.GetNumChildNodes() > 1);
		}
		else if (Property->IsA(UArrayProperty::StaticClass()) || (Property->ArrayDim > 1 && PropertyNode.GetArrayIndex() == INDEX_NONE))
		{
			// Base array properties are always visible
			bIsVisibleStandalone = true;
		}
		else
		{
			bIsVisibleStandalone = true;
		}

	}

	return bIsVisibleStandalone;
}
コード例 #3
0
/**
 * fills in the OutAddresses array with the addresses of all of the available objects.
 * @param InItem		The property to get objects from.
 * @param OutAddresses	Storage array for all of the objects' addresses.
 */
bool FObjectPropertyNode::GetReadAddressUncached( FPropertyNode& InNode, FReadAddressListData& OutAddresses ) 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;
	}


	// Write addresses to the output.
	for ( int32 ObjectIdx = 0 ; ObjectIdx < GetNumObjects() ; ++ObjectIdx )
	{
		const UObject* TempObject = GetUObject(ObjectIdx);

		if( TempObject != NULL )
		{
			OutAddresses.Add( TempObject, InNode.GetValueBaseAddress( (uint8*)(TempObject) ) );
		}
	}

	// Everything checked out and we have usable addresses.
	return true;
}
コード例 #4
0
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;
}
コード例 #5
0
	FValueCache( TSharedRef<FPropertyNode> InPropertyNode, UObject* InOwnerObject )
		: PropertyNode( InPropertyNode )
	{
		PropertyValueRoot.OwnerObject = InOwnerObject;

		UProperty* Property = InPropertyNode->GetProperty();

		check(Property);
		check(PropertyValueRoot.OwnerObject);

		FPropertyNode* ParentNode		= InPropertyNode->GetParentNode();


		//UArrayProperty* ArrayProp = Cast<UArrayProperty>(Property);
		UArrayProperty* OuterArrayProp = Cast<UArrayProperty>(Property->GetOuter());

		// calculate the values for the current object
		
		PropertyValueBaseAddress = OuterArrayProp == NULL
			? InPropertyNode->GetValueBaseAddress(PropertyValueRoot.ValueAddress)
			: ParentNode->GetValueBaseAddress(PropertyValueRoot.ValueAddress);

		PropertyValueAddress = InPropertyNode->GetValueAddress(PropertyValueRoot.ValueAddress);
	}
コード例 #6
0
void SDetailsViewBase::UpdatePropertyMapRecursive(FPropertyNode& InNode, FDetailLayoutBuilderImpl& InDetailLayout, FName CurCategory, FComplexPropertyNode* CurObjectNode)
{
	UProperty* ParentProperty = InNode.GetProperty();
	UStructProperty* ParentStructProp = Cast<UStructProperty>(ParentProperty);

	for (int32 ChildIndex = 0; ChildIndex < InNode.GetNumChildNodes(); ++ChildIndex)
	{
		TSharedPtr<FPropertyNode> ChildNodePtr = InNode.GetChildNode(ChildIndex);
		FPropertyNode& ChildNode = *ChildNodePtr;
		UProperty* Property = ChildNode.GetProperty();

		{
			FObjectPropertyNode* ObjNode = ChildNode.AsObjectNode();
			FCategoryPropertyNode* CategoryNode = ChildNode.AsCategoryNode();
			if (ObjNode)
			{
				// Currently object property nodes do not provide any useful information other than being a container for its children.  We do not draw anything for them.
				// When we encounter object property nodes, add their children instead of adding them to the tree.
				UpdatePropertyMapRecursive(ChildNode, InDetailLayout, CurCategory, ObjNode);
			}
			else if (CategoryNode)
			{
				// For category nodes, we just set the current category and recurse through the children
				UpdatePropertyMapRecursive(ChildNode, InDetailLayout, CategoryNode->GetCategoryName(), CurObjectNode);
			}
			else
			{
				// Whether or not the property can be visible in the default detail layout
				bool bVisibleByDefault = IsVisibleStandaloneProperty(ChildNode, InNode);

				// Whether or not the property is a struct
				UStructProperty* StructProperty = Cast<UStructProperty>(Property);

				bool bIsStruct = StructProperty != NULL;

				static FName ShowOnlyInners("ShowOnlyInnerProperties");

				bool bIsChildOfCustomizedStruct = false;
				bool bIsCustomizedStruct = false;

				const UStruct* Struct = StructProperty ? StructProperty->Struct : NULL;
				const UStruct* ParentStruct = ParentStructProp ? ParentStructProp->Struct : NULL;
				if (Struct || ParentStruct)
				{
					FPropertyEditorModule& ParentPlugin = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
					if (Struct)
					{
						bIsCustomizedStruct = ParentPlugin.IsCustomizedStruct(Struct, SharedThis( this ) );
					}

					if (ParentStruct)
					{
						bIsChildOfCustomizedStruct = ParentPlugin.IsCustomizedStruct(ParentStruct, SharedThis( this ) );
					}
				}

				// Whether or not to push out struct properties to their own categories or show them inside an expandable struct 
				bool bPushOutStructProps = bIsStruct && !bIsCustomizedStruct && !ParentStructProp && Property->HasMetaData(ShowOnlyInners);

				// Is the property edit inline new 
				const bool bIsEditInlineNew = SPropertyEditorEditInline::Supports(&ChildNode, ChildNode.GetArrayIndex());

				// Is this a property of an array
				bool bIsChildOfArray = PropertyEditorHelpers::IsChildOfArray(ChildNode);

				// Edit inline new properties should be visible by default
				bVisibleByDefault |= bIsEditInlineNew;

				// Children of arrays are not visible directly,
				bVisibleByDefault &= !bIsChildOfArray;

				FPropertyAndParent PropertyAndParent(*Property, ParentProperty);
				const bool bIsUserVisible = IsPropertyVisible(PropertyAndParent);

				// Inners of customized in structs should not be taken into consideration for customizing.  They are not designed to be individually customized when their parent is already customized
				if (!bIsChildOfCustomizedStruct)
				{
					// Add any object classes with properties so we can ask them for custom property layouts later
					ClassesWithProperties.Add(Property->GetOwnerStruct());
				}

				// If there is no outer object then the class is the object root and there is only one instance
				FName InstanceName = NAME_None;
				if (CurObjectNode && CurObjectNode->GetParentNode())
				{
					InstanceName = CurObjectNode->GetParentNode()->GetProperty()->GetFName();
				}
				else if (ParentStructProp)
				{
					InstanceName = ParentStructProp->GetFName();
				}

				// Do not add children of customized in struct properties or arrays
				if (!bIsChildOfCustomizedStruct && !bIsChildOfArray)
				{
					// Get the class property map
					FClassInstanceToPropertyMap& ClassInstanceMap = ClassToPropertyMap.FindOrAdd(Property->GetOwnerStruct()->GetFName());

					FPropertyNodeMap& PropertyNodeMap = ClassInstanceMap.FindOrAdd(InstanceName);

					if (!PropertyNodeMap.ParentProperty)
					{
						PropertyNodeMap.ParentProperty = CurObjectNode;
					}
					else
					{
						ensure(PropertyNodeMap.ParentProperty == CurObjectNode);
					}

					checkSlow(!PropertyNodeMap.Contains(Property->GetFName()));

					PropertyNodeMap.Add(Property->GetFName(), ChildNodePtr);
				}

				if (bVisibleByDefault && bIsUserVisible && !bPushOutStructProps)
				{
					FName CategoryName = CurCategory;

					// For properties inside a struct, add them to their own category unless they just take the name of the parent struct.  
					// In that case push them to the parent category
					FName PropertyCatagoryName = FObjectEditorUtils::GetCategoryFName(Property);
					if (!ParentStructProp || (PropertyCatagoryName != ParentStructProp->Struct->GetFName()))
					{
						CategoryName = PropertyCatagoryName;
					}

					if (IsPropertyReadOnly(PropertyAndParent))
					{
						ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true);
					}

					// Add a property to the default category
					FDetailCategoryImpl& CategoryImpl = InDetailLayout.DefaultCategory(CategoryName);
					CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName);
				}

				bool bRecurseIntoChildren =
					!bIsChildOfCustomizedStruct // Don't recurse into built in struct children, we already know what they are and how to display them
					&&  !bIsCustomizedStruct // Don't recurse into customized structs
					&&	!bIsChildOfArray // Do not recurse into arrays, the children are drawn by the array property parent
					&&	!bIsEditInlineNew // Edit inline new children are not supported for customization yet
					&&	bIsUserVisible // Properties must be allowed to be visible by a user if they are not then their children are not visible either
					&& (!bIsStruct || bPushOutStructProps); //  Only recurse into struct properties if they are going to be displayed as standalone properties in categories instead of inside an expandable area inside a category

				if (bRecurseIntoChildren)
				{
					// Built in struct properties or children of arras 
					UpdatePropertyMapRecursive(ChildNode, InDetailLayout, CurCategory, CurObjectNode);
				}
			}
		}
	}
}
コード例 #7
0
void SDetailsViewBase::UpdateSinglePropertyMapRecursive(FPropertyNode& InNode, FDetailLayoutData& LayoutData, FName CurCategory, FComplexPropertyNode* CurObjectNode, bool bEnableFavoriteSystem, bool bUpdateFavoriteSystemOnly)
{
	FDetailLayoutBuilderImpl& DetailLayout = *LayoutData.DetailLayout;

	UProperty* ParentProperty = InNode.GetProperty();
	UStructProperty* ParentStructProp = Cast<UStructProperty>(ParentProperty);
	for(int32 ChildIndex = 0; ChildIndex < InNode.GetNumChildNodes(); ++ChildIndex)
	{
		//Use the original value for each child
		bool LocalUpdateFavoriteSystemOnly = bUpdateFavoriteSystemOnly;

		TSharedPtr<FPropertyNode> ChildNodePtr = InNode.GetChildNode(ChildIndex);
		FPropertyNode& ChildNode = *ChildNodePtr;
		UProperty* Property = ChildNode.GetProperty();

		{
			FObjectPropertyNode* ObjNode = ChildNode.AsObjectNode();
			FCategoryPropertyNode* CategoryNode = ChildNode.AsCategoryNode();
			if(ObjNode)
			{
				// Currently object property nodes do not provide any useful information other than being a container for its children.  We do not draw anything for them.
				// When we encounter object property nodes, add their children instead of adding them to the tree.
				UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CurCategory, ObjNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly);
			}
			else if(CategoryNode)
			{
				if(!LocalUpdateFavoriteSystemOnly)
				{
					FName InstanceName = NAME_None;
					FName CategoryName = CurCategory;
					FString CategoryDelimiterString;
					CategoryDelimiterString.AppendChar(FPropertyNodeConstants::CategoryDelimiterChar);
					if(CurCategory != NAME_None && CategoryNode->GetCategoryName().ToString().Contains(CategoryDelimiterString))
					{
						// This property is child of another property so add it to the parent detail category
						FDetailCategoryImpl& CategoryImpl = DetailLayout.DefaultCategory(CategoryName);
						CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName);
					}
				}

				// For category nodes, we just set the current category and recurse through the children
				UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CategoryNode->GetCategoryName(), CurObjectNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly);
			}
			else
			{
				// Whether or not the property can be visible in the default detail layout
				bool bVisibleByDefault = IsVisibleStandaloneProperty(ChildNode, InNode);

				// Whether or not the property is a struct
				UStructProperty* StructProperty = Cast<UStructProperty>(Property);

				bool bIsStruct = StructProperty != NULL;

				static FName ShowOnlyInners("ShowOnlyInnerProperties");

				bool bIsChildOfCustomizedStruct = false;
				bool bIsCustomizedStruct = false;

				const UStruct* Struct = StructProperty ? StructProperty->Struct : NULL;
				const UStruct* ParentStruct = ParentStructProp ? ParentStructProp->Struct : NULL;
				if(Struct || ParentStruct)
				{
					FPropertyEditorModule& ParentPlugin = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
					if(Struct)
					{
						bIsCustomizedStruct = ParentPlugin.IsCustomizedStruct(Struct, SharedThis(this));
					}

					if(ParentStruct)
					{
						bIsChildOfCustomizedStruct = ParentPlugin.IsCustomizedStruct(ParentStruct, SharedThis(this));
					}
				}

				// Whether or not to push out struct properties to their own categories or show them inside an expandable struct 
				bool bPushOutStructProps = bIsStruct && !bIsCustomizedStruct && !ParentStructProp && Property->HasMetaData(ShowOnlyInners);

				// Is the property edit inline new 
				const bool bIsEditInlineNew = ChildNode.HasNodeFlags(EPropertyNodeFlags::ShowInnerObjectProperties) || SPropertyEditorEditInline::Supports(&ChildNode, ChildNode.GetArrayIndex());

				// Is this a property of a container property
				bool bIsChildOfContainer = PropertyEditorHelpers::IsChildOfArray(ChildNode) || PropertyEditorHelpers::IsChildOfSet(ChildNode) || PropertyEditorHelpers::IsChildOfMap(ChildNode);

				// Edit inline new properties should be visible by default
				bVisibleByDefault |= bIsEditInlineNew;

				// Children of arrays are not visible directly,
				bVisibleByDefault &= !bIsChildOfContainer;

				FPropertyAndParent PropertyAndParent(*Property, ParentProperty);
				const bool bIsUserVisible = IsPropertyVisible(PropertyAndParent);

				// Inners of customized in structs should not be taken into consideration for customizing.  They are not designed to be individually customized when their parent is already customized
				if(!bIsChildOfCustomizedStruct && !LocalUpdateFavoriteSystemOnly)
				{
					// Add any object classes with properties so we can ask them for custom property layouts later
					LayoutData.ClassesWithProperties.Add(Property->GetOwnerStruct());
				}

				// If there is no outer object then the class is the object root and there is only one instance
				FName InstanceName = NAME_None;
				if(CurObjectNode && CurObjectNode->GetParentNode())
				{
					InstanceName = CurObjectNode->GetParentNode()->GetProperty()->GetFName();
				}
				else if(ParentStructProp)
				{
					InstanceName = ParentStructProp->GetFName();
				}

				// Do not add children of customized in struct properties or arrays
				if(!bIsChildOfCustomizedStruct && !bIsChildOfContainer && !LocalUpdateFavoriteSystemOnly)
				{
					// Get the class property map
					FClassInstanceToPropertyMap& ClassInstanceMap = LayoutData.ClassToPropertyMap.FindOrAdd(Property->GetOwnerStruct()->GetFName());

					FPropertyNodeMap& PropertyNodeMap = ClassInstanceMap.FindOrAdd(InstanceName);

					if(!PropertyNodeMap.ParentProperty)
					{
						PropertyNodeMap.ParentProperty = CurObjectNode;
					}
					else
					{
						ensure(PropertyNodeMap.ParentProperty == CurObjectNode);
					}

					checkSlow(!PropertyNodeMap.Contains(Property->GetFName()));

					PropertyNodeMap.Add(Property->GetFName(), ChildNodePtr);
				}
				bool bCanDisplayFavorite = false;
				if(bVisibleByDefault && bIsUserVisible && !bPushOutStructProps)
				{
					FName CategoryName = CurCategory;
					// For properties inside a struct, add them to their own category unless they just take the name of the parent struct.  
					// In that case push them to the parent category
					FName PropertyCatagoryName = FObjectEditorUtils::GetCategoryFName(Property);
					if(!ParentStructProp || (PropertyCatagoryName != ParentStructProp->Struct->GetFName()))
					{
						CategoryName = PropertyCatagoryName;
					}

					if(!LocalUpdateFavoriteSystemOnly)
					{
						if(IsPropertyReadOnly(PropertyAndParent))
						{
							ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true);
						}

						// Add a property to the default category
						FDetailCategoryImpl& CategoryImpl = DetailLayout.DefaultCategory(CategoryName);
						CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName);
					}

					bCanDisplayFavorite = true;
					if(bEnableFavoriteSystem)
					{
						if(bIsCustomizedStruct)
						{
							bCanDisplayFavorite = false;
							//CustomizedStruct child are not categorize since they are under an object but we have to put them in favorite category if the user want to favorite them
							LocalUpdateFavoriteSystemOnly = true;
						}
						else if(ChildNodePtr->IsFavorite())
						{
							//Find or create the favorite category, we have to duplicate favorite property row under this category
							FString CategoryFavoritesName = TEXT("Favorites");
							FName CatFavName = *CategoryFavoritesName;
							FDetailCategoryImpl& CategoryFavImpl = DetailLayout.DefaultCategory(CatFavName);
							CategoryFavImpl.SetSortOrder(0);
							CategoryFavImpl.SetCategoryAsSpecialFavorite();

							//Add the property to the favorite
							FObjectPropertyNode *RootObjectParent = ChildNodePtr->FindRootObjectItemParent();
							FName RootInstanceName = NAME_None;
							if(RootObjectParent != nullptr)
							{
								RootInstanceName = RootObjectParent->GetObjectBaseClass()->GetFName();
							}

							if(LocalUpdateFavoriteSystemOnly)
							{
								if(IsPropertyReadOnly(PropertyAndParent))
								{
									ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true);
								}
								else
								{
									//If the parent has a condition that is not met, make the child as readonly
									FDetailLayoutCustomization ParentTmpCustomization;
									ParentTmpCustomization.PropertyRow = MakeShareable(new FDetailPropertyRow(InNode.AsShared(), CategoryFavImpl.AsShared()));
									if(ParentTmpCustomization.PropertyRow->GetPropertyEditor()->IsPropertyEditingEnabled() == false)
									{
										ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true);
									}
								}
							}

							//Duplicate the row
							CategoryFavImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), RootInstanceName);
						}

						if(bIsStruct)
						{
							LocalUpdateFavoriteSystemOnly = true;
						}
					}
				}
				ChildNodePtr->SetCanDisplayFavorite(bCanDisplayFavorite);

				bool bRecurseIntoChildren =
					!bIsChildOfCustomizedStruct // Don't recurse into built in struct children, we already know what they are and how to display them
					&&  !bIsCustomizedStruct // Don't recurse into customized structs
					&&	!bIsChildOfContainer // Do not recurse into containers, the children are drawn by the container property parent
					&&	!bIsEditInlineNew // Edit inline new children are not supported for customization yet
					&&	bIsUserVisible // Properties must be allowed to be visible by a user if they are not then their children are not visible either
					&& (!bIsStruct || bPushOutStructProps); //  Only recurse into struct properties if they are going to be displayed as standalone properties in categories instead of inside an expandable area inside a category

				if(bRecurseIntoChildren || LocalUpdateFavoriteSystemOnly)
				{
					// Built in struct properties or children of arras 
					UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CurCategory, CurObjectNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly);
				}
			}
		}
	}
}
コード例 #8
0
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;
}