2023-01-02 13:17:18 -06:00
|
|
|
#include "ui/CSimpleHTML.hpp"
|
|
|
|
|
#include "ui/CSimpleFontedFrameFont.hpp"
|
|
|
|
|
#include "ui/CSimpleFontString.hpp"
|
|
|
|
|
#include "ui/CSimpleHTMLScript.hpp"
|
|
|
|
|
#include "ui/CSimpleTop.hpp"
|
|
|
|
|
#include "util/CStatus.hpp"
|
|
|
|
|
#include "util/SFile.hpp"
|
|
|
|
|
#include "util/StringTo.hpp"
|
|
|
|
|
#include <common/XML.hpp>
|
|
|
|
|
|
|
|
|
|
int32_t CSimpleHTML::s_metatable;
|
|
|
|
|
int32_t CSimpleHTML::s_objectType;
|
|
|
|
|
|
|
|
|
|
CONTENTNODE::~CONTENTNODE() {
|
|
|
|
|
if (this->string) {
|
|
|
|
|
delete string;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CSimpleHTML::CreateScriptMetaTable() {
|
|
|
|
|
lua_State* L = FrameScript_GetContext();
|
|
|
|
|
int32_t ref = FrameScript_Object::CreateScriptMetaTable(L, &CSimpleHTML::RegisterScriptMethods);
|
|
|
|
|
CSimpleHTML::s_metatable = ref;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t CSimpleHTML::GetObjectType() {
|
|
|
|
|
if (!CSimpleHTML::s_objectType) {
|
|
|
|
|
CSimpleHTML::s_objectType = ++FrameScript_Object::s_objectTypes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CSimpleHTML::s_objectType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CSimpleHTML::RegisterScriptMethods(lua_State* L) {
|
|
|
|
|
CSimpleFrame::RegisterScriptMethods(L);
|
|
|
|
|
FrameScript_Object::FillScriptMethodTable(L, SimpleHTMLMethods, NUM_SIMPLE_HTML_SCRIPT_METHODS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CSimpleHTML::CSimpleHTML(CSimpleFrame* parent) : CSimpleHyperlinkedFrame(parent) {
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
|
|
for (int32_t i = 0; i < NUM_HTML_TEXT_TYPES; i++) {
|
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleFontedFrameFont), __FILE__, __LINE__, 0x0);
|
|
|
|
|
auto font = new (m) CSimpleFontedFrameFont(this);
|
|
|
|
|
this->m_fonts[i] = font;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CSimpleHTML::AddText(const char* text, HTML_TEXT_TYPE type, uint32_t justify) {
|
2023-01-04 21:00:29 -06:00
|
|
|
// TODO auto stringMem = CDataAllocator::GetData(CSimpleFontString::s_allocator, 0x0, __FILE__, __LINE__);
|
|
|
|
|
auto stringMem = SMemAlloc(sizeof(CSimpleFontString), __FILE__, __LINE__, 0x0);
|
|
|
|
|
auto string = new (stringMem) CSimpleFontString(this, DRAWLAYER_ARTWORK, 1);
|
2023-01-02 13:17:18 -06:00
|
|
|
|
|
|
|
|
if (this->m_layoutAnchor) {
|
|
|
|
|
string->SetPoint(
|
|
|
|
|
FRAMEPOINT_TOPLEFT,
|
|
|
|
|
this->m_layoutAnchor,
|
|
|
|
|
FRAMEPOINT_BOTTOMLEFT,
|
|
|
|
|
0.0f,
|
|
|
|
|
this->m_layoutOffset,
|
|
|
|
|
1
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
string->SetPoint(
|
|
|
|
|
FRAMEPOINT_TOPLEFT,
|
|
|
|
|
this,
|
|
|
|
|
FRAMEPOINT_TOPLEFT,
|
|
|
|
|
0.0f,
|
|
|
|
|
0.0f,
|
|
|
|
|
1
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string->SetWidth(this->GetWidth());
|
|
|
|
|
|
|
|
|
|
auto font = this->m_fonts[type];
|
|
|
|
|
if (!font->m_attributes.m_font.GetString()) {
|
|
|
|
|
font = this->m_fonts[0];
|
|
|
|
|
}
|
|
|
|
|
string->SetFontObject(font);
|
|
|
|
|
|
|
|
|
|
string->SetJustifyH(justify);
|
|
|
|
|
// TODO something with style flag 0x1000?
|
|
|
|
|
|
|
|
|
|
string->SetText(text, 1);
|
|
|
|
|
|
|
|
|
|
this->m_layoutAnchor = string;
|
|
|
|
|
this->m_layoutOffset = -CSimpleTop::RoundToPixelHeight(string->m_spacing);
|
|
|
|
|
|
|
|
|
|
auto contentNode = this->m_content.NewNode(2, 0, 0x0);
|
|
|
|
|
contentNode->string = string;
|
|
|
|
|
// TODO contentNode->dword8 = SStrStr(text, "|H") != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CSimpleHTML::ClearContent() {
|
2023-03-27 17:46:26 -05:00
|
|
|
this->m_content.Clear();
|
2023-01-02 13:17:18 -06:00
|
|
|
|
|
|
|
|
this->m_layoutAnchor = nullptr;
|
|
|
|
|
this->m_layoutOffset = 0.0f;
|
|
|
|
|
|
|
|
|
|
// TODO clear hyperlinks
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CSimpleHTML::FontUpdated(CSimpleFontedFrameFont* font, int32_t a3) {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t CSimpleHTML::GetScriptMetaTable() {
|
|
|
|
|
return CSimpleHTML::s_metatable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CSimpleHTML::IsA(int32_t type) {
|
|
|
|
|
return type == CSimpleHTML::s_objectType
|
|
|
|
|
|| type == CSimpleFrame::s_objectType
|
|
|
|
|
|| type == CScriptRegion::s_objectType
|
|
|
|
|
|| type == CScriptObject::s_objectType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CSimpleHTML::LoadXML(XMLNode* node, CStatus* status) {
|
|
|
|
|
CSimpleFrame::LoadXML(node, status);
|
|
|
|
|
|
|
|
|
|
const char* fontAttr = node->GetAttributeByName("font");
|
|
|
|
|
if (fontAttr && *fontAttr) {
|
|
|
|
|
auto font = CSimpleFont::GetFont(fontAttr, 0);
|
|
|
|
|
|
|
|
|
|
if (font) {
|
|
|
|
|
for (int32_t i = 0; i < NUM_HTML_TEXT_TYPES; i++) {
|
|
|
|
|
this->m_fonts[i]->SetFontObject(font);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
status->Add(
|
|
|
|
|
STATUS_WARNING,
|
|
|
|
|
"%s %s: Couldn't find font object named %s",
|
|
|
|
|
this->GetObjectTypeName(),
|
|
|
|
|
this->GetDisplayName(),
|
|
|
|
|
fontAttr
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (XMLNode* child = node->m_child; child; child = child->m_next) {
|
|
|
|
|
if (!SStrCmpI(child->GetName(), "FontString", STORM_MAX_STR)) {
|
|
|
|
|
this->m_fonts[HTML_TEXT_NORMAL]->LoadXML(child, status);
|
|
|
|
|
} else if (!SStrCmpI(child->GetName(), "FontStringHeader1", STORM_MAX_STR)) {
|
|
|
|
|
this->m_fonts[HTML_TEXT_HEADER1]->LoadXML(child, status);
|
|
|
|
|
} else if (!SStrCmpI(child->GetName(), "FontStringHeader2", STORM_MAX_STR)) {
|
|
|
|
|
this->m_fonts[HTML_TEXT_HEADER2]->LoadXML(child, status);
|
|
|
|
|
} else if (!SStrCmpI(child->GetName(), "FontStringHeader3", STORM_MAX_STR)) {
|
|
|
|
|
this->m_fonts[HTML_TEXT_HEADER3]->LoadXML(child, status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* fileAttr = node->GetAttributeByName("file");
|
|
|
|
|
void* fileData;
|
|
|
|
|
if (fileAttr && SFile::Load(nullptr, fileAttr, &fileData, nullptr, 1, 0x1, nullptr)) {
|
|
|
|
|
this->SetText(static_cast<char*>(fileData), nullptr);
|
|
|
|
|
SFile::Unload(fileData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO hyperlinkFormat
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CSimpleHTML::ParseBODY(XMLNode* node, CStatus* status) {
|
|
|
|
|
for (auto child = node->m_child; child; child = child->m_next) {
|
|
|
|
|
if (!SStrCmpI(child->GetName(), "H1", STORM_MAX_STR)) {
|
|
|
|
|
this->ParseP(child, HTML_TEXT_HEADER1, status);
|
|
|
|
|
|
|
|
|
|
} else if (!SStrCmpI(child->GetName(), "H2", STORM_MAX_STR)) {
|
|
|
|
|
this->ParseP(child, HTML_TEXT_HEADER2, status);
|
|
|
|
|
|
|
|
|
|
} else if (!SStrCmpI(child->GetName(), "H3", STORM_MAX_STR)) {
|
|
|
|
|
this->ParseP(child, HTML_TEXT_HEADER3, status);
|
|
|
|
|
|
|
|
|
|
} else if (!SStrCmpI(child->GetName(), "P", STORM_MAX_STR)) {
|
|
|
|
|
this->ParseP(child, HTML_TEXT_NORMAL, status);
|
|
|
|
|
|
|
|
|
|
} else if (!SStrCmpI(child->GetName(), "BR", STORM_MAX_STR)) {
|
|
|
|
|
this->AddText("\n", HTML_TEXT_NORMAL, 1);
|
|
|
|
|
|
|
|
|
|
} else if (!SStrCmpI(child->GetName(), "IMG", STORM_MAX_STR)) {
|
|
|
|
|
this->ParseIMG(child, status);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
status->Add(
|
|
|
|
|
STATUS_WARNING,
|
|
|
|
|
"Frame %s: Unknown element type: %s",
|
|
|
|
|
this->GetDisplayName(),
|
|
|
|
|
child->GetName()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CSimpleHTML::ParseIMG(XMLNode* node, CStatus* status) {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CSimpleHTML::ParseP(XMLNode* node, HTML_TEXT_TYPE type, CStatus* status) {
|
|
|
|
|
uint32_t justify = 1;
|
|
|
|
|
auto justifyAttr = node->GetAttributeByName("align");
|
|
|
|
|
if (justifyAttr && *justifyAttr) {
|
|
|
|
|
StringToJustify(justifyAttr, justify);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto body = node->GetBody();
|
|
|
|
|
if (!body) {
|
|
|
|
|
body = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto text = SStrDupA(body, __FILE__, __LINE__);
|
|
|
|
|
|
|
|
|
|
uint32_t offset = 0;
|
|
|
|
|
for (auto child = node->m_child; child; child = child->m_next) {
|
|
|
|
|
if (!SStrCmpI(child->GetName(), "BR", STORM_MAX_STR)) {
|
|
|
|
|
char* newText = static_cast<char*>(SMemAlloc(SStrLen(text) + 3, __FILE__, __LINE__, 0x0));
|
|
|
|
|
SStrCopy(newText, text, child->m_offset + offset + 1);
|
|
|
|
|
SStrPack(newText, "|n", STORM_MAX_STR);
|
|
|
|
|
SStrPack(newText, &text[child->m_offset + offset], STORM_MAX_STR);
|
|
|
|
|
SMemFree(text, __FILE__, __LINE__, 0x0);
|
|
|
|
|
text = newText;
|
|
|
|
|
|
|
|
|
|
offset += 2;
|
|
|
|
|
} else if (!SStrCmpI(child->GetName(), "A", STORM_MAX_STR)) {
|
|
|
|
|
// TODO handle anchor child
|
|
|
|
|
} else {
|
|
|
|
|
status->Add(
|
|
|
|
|
STATUS_WARNING,
|
|
|
|
|
"Frame %s: Unknown element type: %s",
|
|
|
|
|
this->GetDisplayName(),
|
|
|
|
|
child->GetName()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t v22 = 0;
|
|
|
|
|
int32_t v36 = 1;
|
|
|
|
|
auto v23 = text;
|
|
|
|
|
auto v24 = text;
|
|
|
|
|
|
|
|
|
|
if (*text) {
|
|
|
|
|
do {
|
|
|
|
|
switch (*v23) {
|
|
|
|
|
case '\t':
|
|
|
|
|
case '\n':
|
|
|
|
|
case '\r':
|
|
|
|
|
case ' ': {
|
|
|
|
|
if (!v36 && !v22) {
|
|
|
|
|
v22 = 1;
|
|
|
|
|
*v24 = ' ';
|
|
|
|
|
v24++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '|': {
|
|
|
|
|
if (v23[1] != 'n') {
|
|
|
|
|
v22 = 0;
|
|
|
|
|
v36 = 0;
|
|
|
|
|
|
|
|
|
|
*v24 = *v23;
|
|
|
|
|
v24++;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (v24 > text && v22) {
|
|
|
|
|
v24--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*v24 = '|';
|
|
|
|
|
v24++;
|
|
|
|
|
*v24 = 'n';
|
|
|
|
|
v24++;
|
|
|
|
|
|
|
|
|
|
v22 = 0;
|
|
|
|
|
v36 = 1;
|
|
|
|
|
|
|
|
|
|
v23++;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default: {
|
|
|
|
|
v22 = 0;
|
|
|
|
|
v36 = 0;
|
|
|
|
|
|
|
|
|
|
*v24 = *v23;
|
|
|
|
|
v24++;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v23++;
|
|
|
|
|
} while (*v23);
|
|
|
|
|
|
|
|
|
|
if (v24 > text && v22) {
|
|
|
|
|
v24--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*v24 = '\0';
|
|
|
|
|
|
|
|
|
|
this->AddText(text, type, justify);
|
|
|
|
|
|
|
|
|
|
SMemFree(text, __FILE__, __LINE__, 0x0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CSimpleHTML::SetText(const char* text, CStatus* status) {
|
|
|
|
|
static auto s_nullStatus = CStatus();
|
|
|
|
|
|
|
|
|
|
if (!status) {
|
|
|
|
|
status = &s_nullStatus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!text) {
|
|
|
|
|
text = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO some sort of guard condition
|
|
|
|
|
|
|
|
|
|
this->ClearContent();
|
|
|
|
|
|
|
|
|
|
auto textLength = SStrLen(text);
|
|
|
|
|
auto tree = XMLTree_Load(text, textLength);
|
|
|
|
|
bool success = false;
|
|
|
|
|
|
|
|
|
|
if (tree) {
|
|
|
|
|
auto node = XMLTree_GetRoot(tree);
|
|
|
|
|
|
|
|
|
|
if (!SStrCmpI(node->GetName(), "HTML", STORM_MAX_STR)) {
|
|
|
|
|
for (auto child = node->m_child; child; child = child->m_next) {
|
|
|
|
|
if (!SStrCmpI(child->GetName(), "BODY", STORM_MAX_STR)) {
|
|
|
|
|
success = true;
|
|
|
|
|
this->ParseBODY(child, status);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status->Add(
|
|
|
|
|
STATUS_WARNING,
|
|
|
|
|
"Frame %s: Unknown element type: %s (expected BODY)",
|
|
|
|
|
this->GetDisplayName(),
|
|
|
|
|
node->GetName()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
status->Add(
|
|
|
|
|
STATUS_WARNING,
|
|
|
|
|
"Frame %s: Unknown element type: %s (expected HTML)",
|
|
|
|
|
this->GetDisplayName(),
|
|
|
|
|
node->GetName()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XMLTree_Free(tree);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
|
// TODO set flag
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->AddText(text, HTML_TEXT_NORMAL, 1);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|