225 lines
7.8 KiB
C++
225 lines
7.8 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "UObject/TextProperty.h"
|
|
#include "Internationalization/ITextData.h"
|
|
#include "UObject/PropertyPortFlags.h"
|
|
#include "UObject/Package.h"
|
|
#include "Internationalization/TextNamespaceUtil.h"
|
|
#include "Internationalization/TextPackageNamespaceUtil.h"
|
|
#include "Internationalization/StringTableRegistry.h"
|
|
#include "Internationalization/StringTableCore.h"
|
|
#include "Serialization/ArchiveUObjectFromStructuredArchive.h"
|
|
|
|
IMPLEMENT_FIELD(FTextProperty)
|
|
|
|
EConvertFromTypeResult FTextProperty::ConvertFromType(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot, uint8* Data, UStruct* DefaultsStruct)
|
|
{
|
|
// Convert serialized string to text.
|
|
if (Tag.Type == NAME_StrProperty)
|
|
{
|
|
FString str;
|
|
Slot << str;
|
|
FText Text = FText::FromString(str);
|
|
Text.TextData->PersistText();
|
|
Text.Flags |= ETextFlag::ConvertedProperty;
|
|
SetPropertyValue_InContainer(Data, Text, Tag.ArrayIndex);
|
|
return EConvertFromTypeResult::Converted;
|
|
}
|
|
|
|
// Convert serialized name to text.
|
|
if (Tag.Type == NAME_NameProperty)
|
|
{
|
|
FName Name;
|
|
Slot << Name;
|
|
FText Text = FText::FromName(Name);
|
|
Text.Flags |= ETextFlag::ConvertedProperty;
|
|
SetPropertyValue_InContainer(Data, Text, Tag.ArrayIndex);
|
|
return EConvertFromTypeResult::Converted;
|
|
}
|
|
|
|
return EConvertFromTypeResult::UseSerializeItem;
|
|
}
|
|
|
|
bool FTextProperty::Identical_Implementation(const FText& ValueA, const FText& ValueB, uint32 PortFlags)
|
|
{
|
|
// A culture variant text is never equal to a culture invariant text
|
|
// A transient text is never equal to a non-transient text
|
|
// An empty text is never equal to a non-empty text
|
|
if (ValueA.IsCultureInvariant() != ValueB.IsCultureInvariant() || ValueA.IsTransient() != ValueB.IsTransient() || ValueA.IsEmpty() != ValueB.IsEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// If both texts are empty (see the above check), then they must be equal
|
|
if (ValueA.IsEmpty())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// If both texts share the same pointer, then they must be equal
|
|
if (ValueA.IdenticalTo(ValueB))
|
|
{
|
|
// Placeholder string table entries will have the same pointer, but should only be considered equal if they're using the same string table and key
|
|
if (ValueA.IsFromStringTable() && FTextInspector::GetSourceString(ValueA) == &FStringTableEntry::GetPlaceholderSourceString())
|
|
{
|
|
FName ValueAStringTableId;
|
|
FString ValueAStringTableEntryKey;
|
|
FTextInspector::GetTableIdAndKey(ValueA, ValueAStringTableId, ValueAStringTableEntryKey);
|
|
|
|
FName ValueBStringTableId;
|
|
FString ValueBStringTableEntryKey;
|
|
FTextInspector::GetTableIdAndKey(ValueB, ValueBStringTableId, ValueBStringTableEntryKey);
|
|
|
|
return ValueAStringTableId == ValueBStringTableId && ValueAStringTableEntryKey.Equals(ValueBStringTableEntryKey, ESearchCase::CaseSensitive);
|
|
}
|
|
|
|
// Otherwise they're equal
|
|
return true;
|
|
}
|
|
|
|
// We compare the display strings in editor (as we author in the native language)
|
|
// We compare the display string for culture invariant and transient texts as they don't have an identity
|
|
if (GIsEditor || ValueA.IsCultureInvariant() || ValueA.IsTransient())
|
|
{
|
|
return FTextInspector::GetDisplayString(ValueA).Equals(FTextInspector::GetDisplayString(ValueB), ESearchCase::CaseSensitive);
|
|
}
|
|
|
|
// If we got this far then the texts don't share the same pointer, which means that they can't share the same identity
|
|
return false;
|
|
}
|
|
|
|
bool FTextProperty::Identical(const void* A, const void* B, uint32 PortFlags) const
|
|
{
|
|
const TCppType ValueA = GetPropertyValue(A);
|
|
if (B)
|
|
{
|
|
const TCppType ValueB = GetPropertyValue(B);
|
|
return Identical_Implementation(ValueA, ValueB, PortFlags);
|
|
}
|
|
|
|
return FTextInspector::GetDisplayString(ValueA).IsEmpty();
|
|
}
|
|
|
|
void FTextProperty::SerializeItem(FStructuredArchive::FSlot Slot, void* Value, void const* Defaults) const
|
|
{
|
|
TCppType* TextPtr = GetPropertyValuePtr(Value);
|
|
Slot << *TextPtr;
|
|
}
|
|
|
|
void FTextProperty::ExportTextItem(FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope) const
|
|
{
|
|
const FText& TextValue = GetPropertyValue(PropertyValue);
|
|
|
|
if (PortFlags & PPF_ExportCpp)
|
|
{
|
|
ValueStr += GenerateCppCodeForTextValue(TextValue, FString());
|
|
}
|
|
else if (PortFlags & PPF_PropertyWindow)
|
|
{
|
|
if (PortFlags & PPF_Delimited)
|
|
{
|
|
ValueStr += TEXT("\"");
|
|
ValueStr += TextValue.ToString();
|
|
ValueStr += TEXT("\"");
|
|
}
|
|
else
|
|
{
|
|
ValueStr += TextValue.ToString();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FTextStringHelper::WriteToBuffer(ValueStr, TextValue, !!(PortFlags & PPF_Delimited));
|
|
}
|
|
}
|
|
|
|
const TCHAR* FTextProperty::ImportText_Internal(const TCHAR* Buffer, void* Data, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText) const
|
|
{
|
|
FText* TextPtr = GetPropertyValuePtr(Data);
|
|
|
|
FString TextNamespace;
|
|
if (Parent && HasAnyPropertyFlags(CPF_Config))
|
|
{
|
|
const bool bPerObject = UsesPerObjectConfig(Parent);
|
|
if (bPerObject)
|
|
{
|
|
FString PathNameString;
|
|
UPackage* ParentOutermost = Parent->GetOutermost();
|
|
if (ParentOutermost == GetTransientPackage())
|
|
{
|
|
PathNameString = Parent->GetName();
|
|
}
|
|
else
|
|
{
|
|
PathNameString = Parent->GetPathName(ParentOutermost);
|
|
}
|
|
TextNamespace = PathNameString + TEXT(" ") + Parent->GetClass()->GetName();
|
|
|
|
Parent->OverridePerObjectConfigSection(TextNamespace);
|
|
}
|
|
else
|
|
{
|
|
const bool bGlobalConfig = HasAnyPropertyFlags(CPF_GlobalConfig);
|
|
UClass* ConfigClass = bGlobalConfig ? GetOwnerClass() : Parent->GetClass();
|
|
TextNamespace = ConfigClass->GetPathName();
|
|
}
|
|
}
|
|
|
|
FString PackageNamespace;
|
|
#if USE_STABLE_LOCALIZATION_KEYS
|
|
if (GIsEditor && !(PortFlags & (PPF_DuplicateVerbatim | PPF_DuplicateForPIE)))
|
|
{
|
|
PackageNamespace = TextNamespaceUtil::EnsurePackageNamespace(Parent);
|
|
}
|
|
#endif // USE_STABLE_LOCALIZATION_KEYS
|
|
|
|
return FTextStringHelper::ReadFromBuffer(Buffer, *TextPtr, *TextNamespace, *PackageNamespace, !!(PortFlags & PPF_Delimited));
|
|
}
|
|
|
|
FString FTextProperty::GenerateCppCodeForTextValue(const FText& InValue, const FString& Indent)
|
|
{
|
|
FString CppCode;
|
|
|
|
if (InValue.IsEmpty())
|
|
{
|
|
CppCode += TEXT("FText::GetEmpty()");
|
|
}
|
|
else if (InValue.IsCultureInvariant())
|
|
{
|
|
const FString& StringValue = FTextInspector::GetDisplayString(InValue);
|
|
|
|
// Produces FText::AsCultureInvariant(TEXT("..."))
|
|
CppCode += TEXT("FText::AsCultureInvariant(\n");
|
|
CppCode += FStrProperty::ExportCppHardcodedText(StringValue, Indent + TEXT("\t\t"));
|
|
CppCode += TEXT("\t)");
|
|
}
|
|
else
|
|
{
|
|
FString ExportedText;
|
|
FTextStringHelper::WriteToBuffer(ExportedText, InValue);
|
|
|
|
if (FTextStringHelper::IsComplexText(*ExportedText))
|
|
{
|
|
// Produces FTextStringHelper::CreateFromBuffer(TEXT("..."))
|
|
CppCode += TEXT("FTextStringHelper::CreateFromBuffer(\n");
|
|
CppCode += FStrProperty::ExportCppHardcodedText(ExportedText, Indent + TEXT("\t\t"));
|
|
CppCode += Indent;
|
|
CppCode += TEXT("\t)");
|
|
}
|
|
else
|
|
{
|
|
// Produces FText::FromString(TEXT("..."))
|
|
CppCode += TEXT("FText::FromString(\n");
|
|
CppCode += FStrProperty::ExportCppHardcodedText(ExportedText, Indent + TEXT("\t\t"));
|
|
CppCode += TEXT("\t)");
|
|
}
|
|
}
|
|
|
|
return CppCode;
|
|
}
|
|
|
|
FString FTextProperty::GetCPPTypeForwardDeclaration() const
|
|
{
|
|
return FString();
|
|
}
|