Changeset View
Changeset View
Standalone View
Standalone View
source/gui/CGUI.cpp
Show All 35 Lines | |||||
#include "ps/Globals.h" | #include "ps/Globals.h" | ||||
#include "ps/Hotkey.h" | #include "ps/Hotkey.h" | ||||
#include "ps/Profile.h" | #include "ps/Profile.h" | ||||
#include "ps/Pyrogenesis.h" | #include "ps/Pyrogenesis.h" | ||||
#include "ps/XML/Xeromyces.h" | #include "ps/XML/Xeromyces.h" | ||||
#include "renderer/Renderer.h" | #include "renderer/Renderer.h" | ||||
#include "scriptinterface/ScriptContext.h" | #include "scriptinterface/ScriptContext.h" | ||||
#include "scriptinterface/ScriptInterface.h" | #include "scriptinterface/ScriptInterface.h" | ||||
#include "simulation2/system/ParamNode.h" | |||||
vladislavbelov: It’s bad to have a simulation dependency in GUI without a reason, move the header to some… | |||||
#include <string> | #include <string> | ||||
#include <unordered_map> | #include <unordered_map> | ||||
#include <unordered_set> | #include <unordered_set> | ||||
extern int g_yres; | extern int g_yres; | ||||
const double SELECT_DBLCLICK_RATE = 0.5; | const double SELECT_DBLCLICK_RATE = 0.5; | ||||
▲ Show 20 Lines • Show All 274 Lines • ▼ Show 20 Lines | IGUIObject* CGUI::ConstructObject(const CStr& str) | ||||
std::map<CStr, ConstructObjectFunction>::iterator it = m_ObjectTypes.find(str); | std::map<CStr, ConstructObjectFunction>::iterator it = m_ObjectTypes.find(str); | ||||
if (it == m_ObjectTypes.end()) | if (it == m_ObjectTypes.end()) | ||||
return nullptr; | return nullptr; | ||||
return (*it->second)(*this); | return (*it->second)(*this); | ||||
} | } | ||||
IGUIObject* CGUI::CreateChildFromParamNode(const CParamNode& paramNode, IGUIObject* parent, std::vector<std::pair<CStr, CStr>>& nameSubst, u32 depth) | |||||
{ | |||||
std::unordered_set<VfsPath> paths; | |||||
IGUIObject* child = ReadObject(paramNode, parent, nameSubst, paths, depth); | |||||
if (!child) | |||||
return nullptr; | |||||
child->RecurseObject(nullptr, &IGUIObject::UpdateCachedSize); | |||||
SGUIMessage msg(GUIM_LOAD); | |||||
child->RecurseObject(nullptr, &IGUIObject::HandleMessage, msg); | |||||
child->ScriptEvent(EventNameLoad); | |||||
return child; | |||||
} | |||||
void CGUI::DetachAndDeleteChild(IGUIObject* child) | |||||
{ | |||||
if (!child) | |||||
return; | |||||
if (!child->m_pParent) | |||||
{ | |||||
LOGERROR("Cannot delete the root object of a page"); | |||||
return; | |||||
} | |||||
child->m_pParent->RemoveChild(*child); | |||||
m_pAllObjects.erase(child->m_Name); | |||||
delete child; | |||||
} | |||||
bool CGUI::AddObject(IGUIObject& parent, IGUIObject& child) | bool CGUI::AddObject(IGUIObject& parent, IGUIObject& child) | ||||
{ | { | ||||
if (child.m_Name.empty()) | if (child.m_Name.empty()) | ||||
{ | { | ||||
LOGERROR("Can't register an object without name!"); | LOGERROR("Can't register an object without name!"); | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | |||||
void CGUI::LoadXmlFile(const VfsPath& Filename, std::unordered_set<VfsPath>& Paths) | void CGUI::LoadXmlFile(const VfsPath& Filename, std::unordered_set<VfsPath>& Paths) | ||||
{ | { | ||||
Paths.insert(Filename); | Paths.insert(Filename); | ||||
CXeromyces XeroFile; | CXeromyces XeroFile; | ||||
if (XeroFile.Load(g_VFS, Filename, "gui") != PSRETURN_OK) | if (XeroFile.Load(g_VFS, Filename, "gui") != PSRETURN_OK) | ||||
return; | return; | ||||
CParamNode paramNode; | |||||
CParamNode::LoadXML(paramNode, XeroFile); | |||||
XMBElement node = XeroFile.GetRoot(); | XMBElement node = XeroFile.GetRoot(); | ||||
CStr root_name(XeroFile.GetElementString(node.GetNodeName())); | CStr root_name(XeroFile.GetElementString(node.GetNodeName())); | ||||
if (root_name == "objects") | if (paramNode.GetChild("objects").IsOk()) | ||||
Xeromyces_ReadRootObjects(node, &XeroFile, Paths); | ReadRootObjects(paramNode.GetChild("objects"), Paths); | ||||
else if (root_name == "sprites") | else if (paramNode.GetChild("sprites").IsOk()) | ||||
Xeromyces_ReadRootSprites(node, &XeroFile); | Xeromyces_ReadRootSprites(node, &XeroFile); | ||||
else if (root_name == "styles") | else if (paramNode.GetChild("styles").IsOk()) | ||||
Xeromyces_ReadRootStyles(node, &XeroFile); | Xeromyces_ReadRootStyles(node, &XeroFile); | ||||
else if (root_name == "setup") | else if (paramNode.GetChild("setup").IsOk()) | ||||
Xeromyces_ReadRootSetup(node, &XeroFile); | Xeromyces_ReadRootSetup(node, &XeroFile); | ||||
else | else | ||||
LOGERROR("CGUI::LoadXmlFile encountered an unknown XML root node type: %s", root_name.c_str()); | LOGERROR("CGUI::LoadXmlFile encountered an unknown XML root node type: %s", root_name.c_str()); | ||||
} | } | ||||
void CGUI::LoadedXmlFiles() | void CGUI::LoadedXmlFiles() | ||||
{ | { | ||||
m_BaseObject->RecurseObject(nullptr, &IGUIObject::UpdateCachedSize); | m_BaseObject->RecurseObject(nullptr, &IGUIObject::UpdateCachedSize); | ||||
SGUIMessage msg(GUIM_LOAD); | SGUIMessage msg(GUIM_LOAD); | ||||
m_BaseObject->RecurseObject(nullptr, &IGUIObject::HandleMessage, msg); | m_BaseObject->RecurseObject(nullptr, &IGUIObject::HandleMessage, msg); | ||||
SendEventToAll(EventNameLoad); | SendEventToAll(EventNameLoad); | ||||
} | } | ||||
//=================================================================== | //=================================================================== | ||||
// XML Reading Xeromyces Specific Sub-Routines | // XML Reading Xeromyces Specific Sub-Routines | ||||
//=================================================================== | //=================================================================== | ||||
void CGUI::Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile, std::unordered_set<VfsPath>& Paths) | void CGUI::ReadRootObjects(const CParamNode& paramNode, std::unordered_set<VfsPath>& Paths) | ||||
{ | { | ||||
int el_script = pFile->GetElementID("script"); | |||||
std::vector<std::pair<CStr, CStr> > subst; | std::vector<std::pair<CStr, CStr>> subst; | ||||
// Iterate main children | // Iterate main children. | ||||
// they should all be <object> or <script> elements | // They should all be <object> or <script> elements. | ||||
for (XMBElement child : Element.GetChildNodes()) | for (const auto& child : paramNode.GetChildren()) | ||||
{ | { | ||||
if (child.GetNodeName() == el_script) | if (child.first == "script") | ||||
// Execute the inline script | // Execute the inline script | ||||
Xeromyces_ReadScript(child, pFile, Paths); | ReadScript(child.second, Paths); | ||||
else | } | ||||
for (const auto& child : paramNode.GetChildren()) | |||||
{ | |||||
if (child.first == "object") | |||||
// Read in this whole object into the GUI | // Read in this whole object into the GUI | ||||
Xeromyces_ReadObject(child, pFile, m_BaseObject.get(), subst, Paths, 0); | ReadObject(child.second, m_BaseObject.get(), subst, Paths, 0); | ||||
} | } | ||||
} | } | ||||
void CGUI::Xeromyces_ReadRootSprites(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadRootSprites(XMBElement Element, CXeromyces* pFile) | ||||
{ | { | ||||
for (XMBElement child : Element.GetChildNodes()) | for (XMBElement child : Element.GetChildNodes()) | ||||
Xeromyces_ReadSprite(child, pFile); | Xeromyces_ReadSprite(child, pFile); | ||||
} | } | ||||
Show All 18 Lines | else if (name == "tooltip") | ||||
Xeromyces_ReadTooltip(child, pFile); | Xeromyces_ReadTooltip(child, pFile); | ||||
else if (name == "color") | else if (name == "color") | ||||
Xeromyces_ReadColor(child, pFile); | Xeromyces_ReadColor(child, pFile); | ||||
else | else | ||||
debug_warn(L"Invalid data - DTD shouldn't allow this"); | debug_warn(L"Invalid data - DTD shouldn't allow this"); | ||||
} | } | ||||
} | } | ||||
void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObject* pParent, std::vector<std::pair<CStr, CStr> >& NameSubst, std::unordered_set<VfsPath>& Paths, u32 nesting_depth) | IGUIObject* CGUI::ReadObject(const CParamNode& paramNode, IGUIObject* pParent, std::vector<std::pair<CStr, CStr> >& NameSubst, std::unordered_set<VfsPath>& Paths, u32 nesting_depth) | ||||
{ | { | ||||
ENSURE(pParent); | ENSURE(pParent); | ||||
XMBAttributeList attributes = Element.GetAttributes(); | CStr type(paramNode.GetChild("@type").ToUTF8()); | ||||
CStr type(attributes.GetNamedItem(pFile->GetAttributeID("type"))); | |||||
if (type.empty()) | if (type.empty()) | ||||
type = "empty"; | type = "empty"; | ||||
// Construct object from specified type | // Construct object from specified type | ||||
// henceforth, we need to do a rollback before aborting. | // henceforth, we need to do a rollback before aborting. | ||||
// i.e. releasing this object | // i.e. releasing this object | ||||
IGUIObject* object = ConstructObject(type); | IGUIObject* object = ConstructObject(type); | ||||
if (!object) | if (!object) | ||||
{ | { | ||||
LOGERROR("GUI: Unrecognized object type \"%s\"", type.c_str()); | LOGERROR("GUI: Unrecognized object type \"%s\"", type.c_str()); | ||||
return; | return nullptr; | ||||
} | } | ||||
// Cache some IDs for element attribute names, to avoid string comparisons | |||||
#define ELMT(x) int elmt_##x = pFile->GetElementID(#x) | |||||
#define ATTR(x) int attr_##x = pFile->GetAttributeID(#x) | |||||
ELMT(object); | |||||
ELMT(action); | |||||
ELMT(script); | |||||
ELMT(repeat); | |||||
ELMT(translatableAttribute); | |||||
ELMT(translate); | |||||
ELMT(attribute); | |||||
ELMT(keep); | |||||
ELMT(include); | |||||
ATTR(style); | |||||
ATTR(type); | |||||
ATTR(name); | |||||
ATTR(z); | |||||
ATTR(on); | |||||
ATTR(file); | |||||
ATTR(directory); | |||||
ATTR(id); | |||||
ATTR(context); | |||||
// | // | ||||
// Read Style and set defaults | // Read Style and set defaults | ||||
// | // | ||||
// If the setting "style" is set, try loading that setting. | // If the setting "style" is set, try loading that setting. | ||||
// | // | ||||
// Always load default (if it's available) first! | // Always load default (if it's available) first! | ||||
// | // | ||||
SetObjectStyle(object, "default"); | SetObjectStyle(object, "default"); | ||||
CStr argStyle(attributes.GetNamedItem(attr_style)); | CStr argStyle(paramNode.GetChild("@style").ToUTF8()); | ||||
if (!argStyle.empty()) | if (!argStyle.empty()) | ||||
SetObjectStyle(object, argStyle); | SetObjectStyle(object, argStyle); | ||||
bool NameSet = false; | bool NameSet = false; | ||||
bool ManuallySetZ = false; | bool ManuallySetZ = false; | ||||
CStrW inclusionPath; | CStrW inclusionPath; | ||||
for (XMBAttribute attr : attributes) | for (const auto& child : paramNode.GetChildren()) | ||||
{ | { | ||||
if (!child.first.attribute) | |||||
continue; | |||||
// If value is "null", then it is equivalent as never being entered | // If value is "null", then it is equivalent as never being entered | ||||
if (attr.Value == "null") | if (child.second.ToString() == L"null") | ||||
continue; | continue; | ||||
// Ignore "type" and "style", we've already checked it | // Ignore "type" and "style", we've already checked it | ||||
if (attr.Name == attr_type || attr.Name == attr_style) | if (child.first == "@type" || child.first == "@style") | ||||
continue; | continue; | ||||
if (attr.Name == attr_name) | if (child.first == "@name") | ||||
{ | { | ||||
CStr name(attr.Value); | CStr name(child.second.ToUTF8()); | ||||
for (const std::pair<CStr, CStr>& sub : NameSubst) | for (const std::pair<CStr, CStr>& sub : NameSubst) | ||||
name.Replace(sub.first, sub.second); | name.Replace(sub.first, sub.second); | ||||
object->SetName(name); | object->SetName(name); | ||||
NameSet = true; | NameSet = true; | ||||
continue; | continue; | ||||
} | } | ||||
if (attr.Name == attr_z) | if (child.first == "@z") | ||||
ManuallySetZ = true; | ManuallySetZ = true; | ||||
object->SetSettingFromString(pFile->GetAttributeString(attr.Name), attr.Value.FromUTF8(), false); | object->SetSettingFromString(child.first.name.substr(1), child.second.ToString(), false); | ||||
} | } | ||||
// Check if name isn't set, generate an internal name in that case. | // Check if name isn't set, generate an internal name in that case. | ||||
if (!NameSet) | if (!NameSet) | ||||
{ | { | ||||
object->SetName("__internal(" + CStr::FromInt(m_InternalNameNumber) + ")"); | object->SetName("__internal(" + CStr::FromInt(m_InternalNameNumber) + ")"); | ||||
++m_InternalNameNumber; | ++m_InternalNameNumber; | ||||
} | } | ||||
CStrW caption(Element.GetText().FromUTF8()); | CStrW caption(paramNode.ToString()); | ||||
if (!caption.empty()) | if (!caption.empty()) | ||||
object->SetSettingFromString("caption", caption, false); | object->SetSettingFromString("caption", caption, false); | ||||
for (XMBElement child : Element.GetChildNodes()) | for (const auto& child : paramNode.GetChildren()) | ||||
{ | { | ||||
// Check what name the elements got | if (child.first.attribute) | ||||
int element_name = child.GetNodeName(); | continue; | ||||
if (element_name == elmt_object) | if (child.first == "object") | ||||
{ | { | ||||
// Call this function on the child | // Call this function on the child | ||||
Xeromyces_ReadObject(child, pFile, object, NameSubst, Paths, nesting_depth); | ReadObject(child.second, object, NameSubst, Paths, nesting_depth); | ||||
} | } | ||||
else if (element_name == elmt_action) | else if (child.first == "action") | ||||
{ | { | ||||
// Scripted <action> element | // Scripted <action> element | ||||
// Check for a 'file' parameter | // Check for a 'file' parameter | ||||
CStrW filename(child.GetAttributes().GetNamedItem(attr_file).FromUTF8()); | CStrW filename(child.second.GetChild("@file").ToString()); | ||||
CStr code; | CStr code; | ||||
// If there is a file, open it and use it as the code | // If there is a file, open it and use it as the code | ||||
if (!filename.empty()) | if (!filename.empty()) | ||||
{ | { | ||||
Paths.insert(filename); | Paths.insert(filename); | ||||
CVFSFile scriptfile; | CVFSFile scriptfile; | ||||
if (scriptfile.Load(g_VFS, filename) != PSRETURN_OK) | if (scriptfile.Load(g_VFS, filename) != PSRETURN_OK) | ||||
{ | { | ||||
LOGERROR("Error opening GUI script action file '%s'", utf8_from_wstring(filename)); | LOGERROR("Error opening GUI script action file '%s'", utf8_from_wstring(filename)); | ||||
continue; | continue; | ||||
} | } | ||||
code = scriptfile.DecodeUTF8(); // assume it's UTF-8 | code = scriptfile.DecodeUTF8(); // assume it's UTF-8 | ||||
} | } | ||||
XMBElementList grandchildren = child.GetChildNodes(); | |||||
if (!grandchildren.empty()) // The <action> element contains <keep> and <translate> tags. | |||||
for (XMBElement grandchild : grandchildren) | |||||
{ | |||||
if (grandchild.GetNodeName() == elmt_translate) | |||||
code += g_L10n.Translate(grandchild.GetText()); | |||||
else if (grandchild.GetNodeName() == elmt_keep) | |||||
code += grandchild.GetText(); | |||||
} | |||||
else // It's pure JavaScript code. | |||||
// Read the inline code (concatenating to the file code, if both are specified) | // Read the inline code (concatenating to the file code, if both are specified) | ||||
code += CStr(child.GetText()); | code += child.second.ToUTF8(); | ||||
CStr eventName = child.GetAttributes().GetNamedItem(attr_on); | CStr eventName = child.second.GetChild("@on").ToUTF8(); | ||||
object->RegisterScriptHandler(eventName, code, *this); | object->RegisterScriptHandler(eventName, code, *this); | ||||
} | } | ||||
else if (child.GetNodeName() == elmt_script) | else if (child.first == "script") | ||||
{ | { | ||||
Xeromyces_ReadScript(child, pFile, Paths); | ReadScript(child.second, Paths); | ||||
} | } | ||||
else if (element_name == elmt_repeat) | else if (child.first == "repeat") | ||||
{ | { | ||||
Xeromyces_ReadRepeat(child, pFile, object, NameSubst, Paths, nesting_depth); | ReadRepeat(child.second, object, NameSubst, Paths, nesting_depth); | ||||
} | } | ||||
else if (element_name == elmt_translatableAttribute) | else if (child.first == "translatableAttribute") | ||||
{ | { | ||||
// This is an element in the form "<translatableAttribute id="attributeName">attributeValue</translatableAttribute>". | // This is an element in the form "<translatableAttribute id="attributeName">attributeValue</translatableAttribute>". | ||||
CStr attributeName(child.GetAttributes().GetNamedItem(attr_id)); // Read the attribute name. | CStr attributeName(child.second.GetChild("@id").ToUTF8()); // Read the attribute name. | ||||
if (attributeName.empty()) | if (attributeName.empty()) | ||||
{ | { | ||||
LOGERROR("GUI: 'translatableAttribute' XML element with empty 'id' XML attribute found. (object: %s)", object->GetPresentableName().c_str()); | LOGERROR("GUI: 'translatableAttribute' XML element with empty 'id' XML attribute found. (object: %s)", object->GetPresentableName().c_str()); | ||||
continue; | continue; | ||||
} | } | ||||
CStr value(child.GetText()); | CStr value(child.second.ToUTF8()); | ||||
if (value.empty()) | if (value.empty()) | ||||
continue; | continue; | ||||
CStr context(child.GetAttributes().GetNamedItem(attr_context)); // Read the context if any. | CStr context(child.second.GetChild("@context").ToUTF8()); // Read the context if any. | ||||
CStr translatedValue = context.empty() ? | CStr translatedValue = context.empty() ? | ||||
g_L10n.Translate(value) : | g_L10n.Translate(value) : | ||||
g_L10n.TranslateWithContext(context, value); | g_L10n.TranslateWithContext(context, value); | ||||
object->SetSettingFromString(attributeName, translatedValue.FromUTF8(), false); | object->SetSettingFromString(attributeName, translatedValue.FromUTF8(), false); | ||||
} | } | ||||
else if (element_name == elmt_attribute) | else if (child.first == "include") | ||||
{ | |||||
// This is an element in the form "<attribute id="attributeName"><keep>Don't translate this part | |||||
// </keep><translate>but translate this one.</translate></attribute>". | |||||
CStr attributeName(child.GetAttributes().GetNamedItem(attr_id)); // Read the attribute name. | |||||
if (attributeName.empty()) | |||||
{ | |||||
LOGERROR("GUI: 'attribute' XML element with empty 'id' XML attribute found. (object: %s)", object->GetPresentableName().c_str()); | |||||
continue; | |||||
} | |||||
CStr translatedValue; | |||||
for (XMBElement grandchild : child.GetChildNodes()) | |||||
{ | { | ||||
if (grandchild.GetNodeName() == elmt_translate) | CStrW filename(child.second.GetChild("@file").ToString()); | ||||
translatedValue += g_L10n.Translate(grandchild.GetText()); | CStrW directory(child.second.GetChild("@directory").ToString()); | ||||
else if (grandchild.GetNodeName() == elmt_keep) | |||||
translatedValue += grandchild.GetText(); | |||||
} | |||||
object->SetSettingFromString(attributeName, translatedValue.FromUTF8(), false); | |||||
} | |||||
else if (element_name == elmt_include) | |||||
{ | |||||
CStrW filename(child.GetAttributes().GetNamedItem(attr_file).FromUTF8()); | |||||
CStrW directory(child.GetAttributes().GetNamedItem(attr_directory).FromUTF8()); | |||||
if (!filename.empty()) | if (!filename.empty()) | ||||
{ | { | ||||
if (!directory.empty()) | if (!directory.empty()) | ||||
LOGWARNING("GUI: Include element found with file name (%s) and directory name (%s). Only the file will be processed.", utf8_from_wstring(filename), utf8_from_wstring(directory)); | LOGWARNING("GUI: Include element found with file name (%s) and directory name (%s). Only the file will be processed.", utf8_from_wstring(filename), utf8_from_wstring(directory)); | ||||
Paths.insert(filename); | Paths.insert(filename); | ||||
CXeromyces XeroIncluded; | CXeromyces XeroIncluded; | ||||
if (XeroIncluded.Load(g_VFS, filename, "gui") != PSRETURN_OK) | if (XeroIncluded.Load(g_VFS, filename, "gui") != PSRETURN_OK) | ||||
{ | { | ||||
LOGERROR("GUI: Error reading included XML: '%s'", utf8_from_wstring(filename)); | LOGERROR("GUI: Error reading included XML: '%s'", utf8_from_wstring(filename)); | ||||
continue; | continue; | ||||
} | } | ||||
XMBElement node = XeroIncluded.GetRoot(); | CParamNode included; | ||||
if (node.GetNodeName() != XeroIncluded.GetElementID("object")) | CParamNode::LoadXML(included, XeroIncluded); | ||||
if (!included.GetChild("object").IsOk()) | |||||
{ | { | ||||
LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", utf8_from_wstring(filename)); | LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", utf8_from_wstring(filename)); | ||||
continue; | continue; | ||||
} | } | ||||
if (nesting_depth+1 >= MAX_OBJECT_DEPTH) | if (nesting_depth+1 >= MAX_OBJECT_DEPTH) | ||||
{ | { | ||||
LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(filename)); | LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(filename)); | ||||
continue; | continue; | ||||
} | } | ||||
Xeromyces_ReadObject(node, &XeroIncluded, object, NameSubst, Paths, nesting_depth+1); | ReadObject(included.GetChild("object"), object, NameSubst, Paths, nesting_depth+1); | ||||
} | } | ||||
else if (!directory.empty()) | else if (!directory.empty()) | ||||
{ | { | ||||
if (nesting_depth+1 >= MAX_OBJECT_DEPTH) | if (nesting_depth+1 >= MAX_OBJECT_DEPTH) | ||||
{ | { | ||||
LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(directory)); | LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(directory)); | ||||
continue; | continue; | ||||
} | } | ||||
VfsPaths pathnames; | VfsPaths pathnames; | ||||
vfs::GetPathnames(g_VFS, directory, L"*.xml", pathnames); | vfs::GetPathnames(g_VFS, directory, L"*.xml", pathnames); | ||||
for (const VfsPath& path : pathnames) | for (const VfsPath& path : pathnames) | ||||
{ | { | ||||
// as opposed to loading scripts, don't care if it's loaded before | // as opposed to loading scripts, don't care if it's loaded before | ||||
// one might use the same parts of the GUI in different situations | // one might use the same parts of the GUI in different situations | ||||
Paths.insert(path); | Paths.insert(path); | ||||
CXeromyces XeroIncluded; | CXeromyces XeroIncluded; | ||||
if (XeroIncluded.Load(g_VFS, path, "gui") != PSRETURN_OK) | if (XeroIncluded.Load(g_VFS, path, "gui") != PSRETURN_OK) | ||||
{ | { | ||||
LOGERROR("GUI: Error reading included XML: '%s'", path.string8()); | LOGERROR("GUI: Error reading included XML: '%s'", path.string8()); | ||||
continue; | continue; | ||||
} | } | ||||
XMBElement node = XeroIncluded.GetRoot(); | CParamNode included; | ||||
if (node.GetNodeName() != XeroIncluded.GetElementID("object")) | CParamNode::LoadXML(included, XeroIncluded); | ||||
if (!included.GetChild("object").IsOk()) | |||||
{ | { | ||||
LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", path.string8()); | LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", path.string8()); | ||||
continue; | continue; | ||||
} | } | ||||
Xeromyces_ReadObject(node, &XeroIncluded, object, NameSubst, Paths, nesting_depth+1); | ReadObject(included.GetChild("object"), object, NameSubst, Paths, nesting_depth+1); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
LOGERROR("GUI: 'include' XML element must have valid 'file' or 'directory' attribute found. (object %s)", object->GetPresentableName().c_str()); | LOGERROR("GUI: 'include' XML element must have valid 'file' or 'directory' attribute found. (object %s)", object->GetPresentableName().c_str()); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
// Try making the object read the tag. | // Try making the object read the tag. | ||||
if (!object->HandleAdditionalChildren(child, pFile)) | if (!object->HandleAdditionalChildren(child.first.name, child.second)) | ||||
LOGERROR("GUI: (object: %s) Reading unknown children for its type", object->GetPresentableName().c_str()); | LOGERROR("GUI: (object: %s) Reading unknown children for its type - %s", object->GetPresentableName().c_str(), child.first.name); | ||||
} | } | ||||
} | } | ||||
object->AdditionalChildrenHandled(); | object->AdditionalChildrenHandled(); | ||||
if (!ManuallySetZ) | if (!ManuallySetZ) | ||||
{ | { | ||||
// Set it automatically to 10 plus its parents | // Set it automatically to 10 plus its parents | ||||
if (object->m_Absolute) | if (object->m_Absolute) | ||||
// If the object is absolute, we'll have to get the parent's Z buffered, | // If the object is absolute, we'll have to get the parent's Z buffered, | ||||
// and add to that! | // and add to that! | ||||
object->SetSetting<float>("z", pParent->GetBufferedZ() + 10.f, false); | object->SetSetting<float>("z", pParent->GetBufferedZ() + 10.f, false); | ||||
else | else | ||||
// If the object is relative, then we'll just store Z as "10" | // If the object is relative, then we'll just store Z as "10" | ||||
object->SetSetting<float>("z", 10.f, false); | object->SetSetting<float>("z", 10.f, false); | ||||
} | } | ||||
if (!AddObject(*pParent, *object)) | if (!AddObject(*pParent, *object)) | ||||
{ | |||||
delete object; | delete object; | ||||
return nullptr; | |||||
} | |||||
return object; | |||||
} | } | ||||
void CGUI::Xeromyces_ReadRepeat(XMBElement Element, CXeromyces* pFile, IGUIObject* pParent, std::vector<std::pair<CStr, CStr> >& NameSubst, std::unordered_set<VfsPath>& Paths, u32 nesting_depth) | void CGUI::ReadRepeat(const CParamNode& paramNode, IGUIObject* pParent, std::vector<std::pair<CStr, CStr> >& NameSubst, std::unordered_set<VfsPath>& Paths, u32 nesting_depth) | ||||
{ | { | ||||
#define ELMT(x) int elmt_##x = pFile->GetElementID(#x) | int count = paramNode.GetChild("@count").ToInt(); | ||||
#define ATTR(x) int attr_##x = pFile->GetAttributeID(#x) | CStr var("["+paramNode.GetChild("@var").ToUTF8()+"]"); | ||||
ELMT(object); | |||||
ATTR(count); | |||||
ATTR(var); | |||||
XMBAttributeList attributes = Element.GetAttributes(); | |||||
int count = CStr(attributes.GetNamedItem(attr_count)).ToInt(); | |||||
CStr var("["+attributes.GetNamedItem(attr_var)+"]"); | |||||
if (var.size() < 3) | if (var.size() < 3) | ||||
var = "[n]"; | var = "[n]"; | ||||
for (int n = 0; n < count; ++n) | for (int n = 0; n < count; ++n) | ||||
{ | { | ||||
NameSubst.emplace_back(var, "[" + CStr::FromInt(n) + "]"); | NameSubst.emplace_back(var, "[" + CStr::FromInt(n) + "]"); | ||||
XERO_ITER_EL(Element, child) | for (const auto& child : paramNode.GetChildren()) | ||||
{ | { | ||||
if (child.GetNodeName() == elmt_object) | if (child.first == "object") | ||||
Xeromyces_ReadObject(child, pFile, pParent, NameSubst, Paths, nesting_depth); | ReadObject(child.second, pParent, NameSubst, Paths, nesting_depth); | ||||
} | } | ||||
NameSubst.pop_back(); | NameSubst.pop_back(); | ||||
} | } | ||||
} | } | ||||
void CGUI::Xeromyces_ReadScript(XMBElement Element, CXeromyces* pFile, std::unordered_set<VfsPath>& Paths) | void CGUI::ReadScript(const CParamNode& paramNode, std::unordered_set<VfsPath>& Paths) | ||||
{ | { | ||||
// Check for a 'file' parameter | // Check for a 'file' parameter | ||||
CStrW file(Element.GetAttributes().GetNamedItem(pFile->GetAttributeID("file")).FromUTF8()); | CStrW file(paramNode.GetChild("@file").ToString()); | ||||
// If there is a file specified, open and execute it | // If there is a file specified, open and execute it | ||||
if (!file.empty()) | if (!file.empty()) | ||||
{ | { | ||||
if (!VfsPath(file).IsDirectory()) | if (!VfsPath(file).IsDirectory()) | ||||
{ | { | ||||
Paths.insert(file); | Paths.insert(file); | ||||
m_ScriptInterface->LoadGlobalScriptFile(file); | m_ScriptInterface->LoadGlobalScriptFile(file); | ||||
} | } | ||||
else | else | ||||
LOGERROR("GUI: Script path %s is not a file path", file.ToUTF8().c_str()); | LOGERROR("GUI: Script path %s is not a file path", file.ToUTF8().c_str()); | ||||
} | } | ||||
// If it has a directory attribute, read all JS files in that directory | // If it has a directory attribute, read all JS files in that directory | ||||
CStrW directory(Element.GetAttributes().GetNamedItem(pFile->GetAttributeID("directory")).FromUTF8()); | CStrW directory(paramNode.GetChild("@directory").ToString()); | ||||
if (!directory.empty()) | if (!directory.empty()) | ||||
{ | { | ||||
if (VfsPath(directory).IsDirectory()) | if (VfsPath(directory).IsDirectory()) | ||||
{ | { | ||||
VfsPaths pathnames; | VfsPaths pathnames; | ||||
vfs::GetPathnames(g_VFS, directory, L"*.js", pathnames); | vfs::GetPathnames(g_VFS, directory, L"*.js", pathnames); | ||||
for (const VfsPath& path : pathnames) | for (const VfsPath& path : pathnames) | ||||
{ | { | ||||
// Only load new files (so when the insert succeeds) | // Only load new files (so when the insert succeeds) | ||||
if (Paths.insert(path).second) | if (Paths.insert(path).second) | ||||
m_ScriptInterface->LoadGlobalScriptFile(path); | m_ScriptInterface->LoadGlobalScriptFile(path); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
LOGERROR("GUI: Script path %s is not a directory path", directory.ToUTF8().c_str()); | LOGERROR("GUI: Script path %s is not a directory path", directory.ToUTF8().c_str()); | ||||
} | } | ||||
CStr code(Element.GetText()); | CStr code(paramNode.ToUTF8()); | ||||
if (!code.empty()) | if (!code.empty()) | ||||
m_ScriptInterface->LoadGlobalScript(L"Some XML file", code); | m_ScriptInterface->LoadGlobalScript(L"Some XML file", code); | ||||
} | } | ||||
void CGUI::Xeromyces_ReadSprite(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadSprite(XMBElement Element, CXeromyces* pFile) | ||||
{ | { | ||||
CGUISprite* Sprite = new CGUISprite; | CGUISprite* Sprite = new CGUISprite; | ||||
▲ Show 20 Lines • Show All 345 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
It’s bad to have a simulation dependency in GUI without a reason, move the header to some common place.