/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "SettingsManager.h" #include "SettingsManagerDialog.h" #include "ToolBox.h" #include "QtViewPaneManager.h" #include "MainWindow.h" #include #define TOOLBOX_FILE "Editor/ToolBox.xml" #define TOOLBOX_NODE "ToolBox" CSettingsManager::CSettingsManager(EditorSettingsManagerType managerType) { if (managerType < eSettingsManagerLast) { m_managerType = managerType; } else { managerType = eSettingsManagerMemoryStorage; } if (m_managerType == eSettingsManagerMemoryStorage) { m_pSettingsManagerMemoryNode = XmlHelpers::CreateXmlNode(EDITOR_SETTINGS_ROOT_NODE); } // Main frame layouts must be processed as first AddToolName(QStringLiteral(MAINFRM_LAYOUT_NORMAL), QStringLiteral("Sandbox Layout")); AddToolName(QStringLiteral(MAINFRM_LAYOUT_PREVIEW), QStringLiteral("Sandbox Preview Layout")); CreateDefaultLayoutSettingsFile(); QDateTime theTime = QDateTime::currentDateTimeUtc(); SaveLogEventSetting("EditorStart", "time", theTime.toString(LOG_DATETIME_FORMAT)); } CSettingsManager::~CSettingsManager() { } void CSettingsManager::SaveLayoutSettings(const QByteArray& layout, const QString& toolName) { if (m_managerType == eSettingsManagerMemoryStorage) { XmlNodeRef rootLayoutNode = m_pSettingsManagerMemoryNode->findChild(EDITOR_LAYOUT_ROOT_NODE); if (rootLayoutNode) { XmlNodeRef xmlDockingLayoutNode = rootLayoutNode->findChild(EDITOR_LAYOUT_NODE); if (!xmlDockingLayoutNode) { return; } XmlNodeRef toolNode = xmlDockingLayoutNode->findChild(toolName.toUtf8().data()); if (toolNode) { xmlDockingLayoutNode->removeChild(toolNode); } toolNode = XmlHelpers::CreateXmlNode(toolName.toUtf8().data()); xmlDockingLayoutNode->addChild(toolNode); XmlNodeRef windowStateNode = XmlHelpers::CreateXmlNode("WindowState"); toolNode->addChild(windowStateNode); windowStateNode->setContent(layout.toHex().data()); } } } bool CSettingsManager::CreateDefaultLayoutSettingsFile() { XmlNodeRef pLayoutRootNode = XmlHelpers::CreateXmlNode(EDITOR_LAYOUT_ROOT_NODE); XmlNodeRef pEditorLayoutNode = XmlHelpers::CreateXmlNode(EDITOR_LAYOUT_NODE); pLayoutRootNode->addChild(pEditorLayoutNode); if (m_pSettingsManagerMemoryNode->findChild(EDITOR_LAYOUT_ROOT_NODE)) { m_pSettingsManagerMemoryNode->removeChild(m_pSettingsManagerMemoryNode->findChild(EDITOR_LAYOUT_ROOT_NODE)); } m_pSettingsManagerMemoryNode->addChild(pLayoutRootNode); return true; } AZStd::vector CSettingsManager::BuildSettingsList() { XmlNodeRef root = NULL; root = m_pSettingsManagerMemoryNode; XmlNodeRef tmpNode = root->findChild(EDITOR_SETTINGS_CONTENT_NODE); if (!tmpNode) { root->addChild(root->createNode(EDITOR_SETTINGS_CONTENT_NODE)); tmpNode = root->findChild(EDITOR_SETTINGS_CONTENT_NODE); if (!tmpNode) { AZ_Warning("Settings Manager", false, "Failed to build the settings list - could not find the root xml node."); return AZStd::vector(); } } AZStd::vector result; BuildSettingsList_Helper(tmpNode, "", result); return result; } void CSettingsManager::BuildSettingsList_Helper(const XmlNodeRef& node, const AZStd::string_view& pathToNode, AZStd::vector& result) { for (int i = 0; i < node->getNumAttributes(); ++i) { const char* key = NULL; const char* value = NULL; node->getAttributeByIndex(i, &key, &value); if (!pathToNode.empty()) { result.push_back(AZStd::string(pathToNode)); } } if (node->getChildCount() > 0) { // Explore Children int numChildren = node->getChildCount(); for (int i = 0; i < numChildren; ++i) { if (pathToNode.empty()) { BuildSettingsList_Helper( node->getChild(i), node->getChild(i)->getTag(), result ); } else { BuildSettingsList_Helper( node->getChild(i), AZStd::string(pathToNode) + "|" + node->getChild(i)->getTag(), result ); } } } } ////////////////////////////////////////////////////////////////////////// void CSettingsManager::SaveSetting(const QString& path, const QString& attr, const QString& val) { QStringList strNodes; QString fixedPath = path; // Make sure it ends with "\" before extraction of tokens QString slashChar = fixedPath.right(fixedPath.length() - 1); if (slashChar != "\\") { fixedPath += "\\"; } Path::GetDirectoryQueue(fixedPath, strNodes); QString writeAttr = attr; // Spaces in node names not allowed writeAttr.replace(" ", ""); XmlNodeRef root = NULL; root = m_pSettingsManagerMemoryNode; XmlNodeRef tmpNode = root->findChild(EDITOR_SETTINGS_CONTENT_NODE); if (!tmpNode) { root->addChild(root->createNode(EDITOR_SETTINGS_CONTENT_NODE)); tmpNode = root->findChild(EDITOR_SETTINGS_CONTENT_NODE); if (!tmpNode) { return; } } for (int i = 0; i < strNodes.size(); ++i) { if (!tmpNode->findChild(strNodes[i].toUtf8().data())) { tmpNode->addChild(tmpNode->createNode(strNodes[i].toUtf8().data())); tmpNode = tmpNode->findChild(strNodes[i].toUtf8().data()); } else { tmpNode = tmpNode->findChild(strNodes[i].toUtf8().data()); } } if (!tmpNode->findChild(writeAttr.toUtf8().data())) { tmpNode->addChild(tmpNode->createNode(writeAttr.toUtf8().data())); tmpNode = tmpNode->findChild(writeAttr.toUtf8().data()); } else { tmpNode = tmpNode->findChild(writeAttr.toUtf8().data()); } if (!tmpNode) { return; } tmpNode->setAttr(EDITOR_SETTINGS_ATTRIB_NAME, val.toUtf8().data()); } void CSettingsManager::SaveSetting(const QString& path, const QString& attr, bool bVal) { SaveSetting(path, attr, QString ::number(bVal)); } void CSettingsManager::SaveSetting(const QString& path, const QString& attr, float fVal) { SaveSetting(path, attr, QString::number(fVal)); } void CSettingsManager::SaveSetting(const QString& path, const QString& attr, int iVal) { SaveSetting(path, attr, QString::number(iVal)); } void CSettingsManager::SaveSetting(const QString& path, const QString& attr, QColor color) { SaveSetting(path, attr, color.name()); } ////////////////////////////////////////////////////////////////////////// XmlNodeRef CSettingsManager::LoadSetting(const QString& path, const QString& attr, QString& val) { // Make sure it ends with "\" before extraction of tokens QString fixedPath = path; QString slashChar = fixedPath.right(fixedPath.length() - 1); if (slashChar != "\\") { fixedPath += "\\"; } QStringList strNodes; Path::GetDirectoryQueue(fixedPath, strNodes); QString readAttr = attr; // Spaces in node names not allowed readAttr.replace(" ", ""); XmlNodeRef root = NULL; root = m_pSettingsManagerMemoryNode; XmlNodeRef tmpNode = NULL; if (NeedSettingsNode(path)) { tmpNode = root->findChild(EDITOR_SETTINGS_CONTENT_NODE); } else { tmpNode = root; } if (!tmpNode) { return 0; } for (int i = 0; i < strNodes.size(); ++i) { if (tmpNode->findChild(strNodes[i].toUtf8().data())) { tmpNode = tmpNode->findChild(strNodes[i].toUtf8().data()); } else { return 0; } } if (!tmpNode->findChild(readAttr.toUtf8().data())) { return 0; } else { tmpNode = tmpNode->findChild(readAttr.toUtf8().data()); } if (tmpNode->haveAttr(EDITOR_SETTINGS_ATTRIB_NAME)) { tmpNode->getAttr(EDITOR_SETTINGS_ATTRIB_NAME, val); } return tmpNode; } void CSettingsManager::LoadSetting(const QString& path, const QString& attr, bool& bVal) { QString defaultVal = QString::number(bVal); LoadSetting(path, attr, defaultVal); bVal = defaultVal.toInt() != 0; } void CSettingsManager::LoadSetting(const QString& path, const QString& attr, int& iVal) { QString defaultVal = QString::number(iVal); LoadSetting(path, attr, defaultVal); iVal = defaultVal.toInt(); } void CSettingsManager::LoadSetting(const QString& path, const QString& attr, float& fVal) { QString defaultVal = QString::number(fVal, 'g'); LoadSetting(path, attr, defaultVal); fVal = defaultVal.toFloat(); } void CSettingsManager::LoadSetting(const QString& path, const QString& attr, QColor &val) { QString defaultVal = val.name(); LoadSetting(path, attr, defaultVal); val = QColor(defaultVal); } void CSettingsManager::AddToolVersion(const QString& toolName, const QString& toolVersion) { if (toolName.isEmpty()) { return; } if (stl::find_in_map(m_toolNames, toolName, NULL) == "") { if (!toolVersion.isEmpty()) { m_toolVersions[toolName] = toolVersion; } else { m_toolVersions[toolName] = ""; } } }; void CSettingsManager::AddToolName(const QString& toolName, const QString& humanReadableName) { if (toolName == "") { return; } if (stl::find_in_map(m_toolNames, toolName, NULL) == "") { if (!humanReadableName.isEmpty()) { m_toolNames[toolName] = humanReadableName; } else { m_toolNames[toolName] = toolName; } } }; void CSettingsManager::AddSettingsNode(XmlNodeRef newNode) { QString nodeStr = newNode->getTag(); XmlNodeRef oldNode = m_pSettingsManagerMemoryNode->findChild(nodeStr.toUtf8().data()); if (oldNode) { m_pSettingsManagerMemoryNode->removeChild(oldNode); } m_pSettingsManagerMemoryNode->addChild(newNode); } void CSettingsManager::ExportSettings(XmlNodeRef exportNode, QString fileName) { exportNode->saveToFile(fileName.toUtf8().data()); } void CSettingsManager::Export() { // Feed in-memory node of CSettingsManager with global Sandbox settings gSettings.Load(); if (m_ExportFilePath.isEmpty()) { return; } // Update to the latest layout UpdateLayoutNode(); // Save CVars SerializeCVars(m_pSettingsManagerMemoryNode, false); m_pSettingsManagerMemoryNode->saveToFile(m_ExportFilePath.toUtf8().data()); GetIEditor()->SetStatusText("Export Successful"); } void CSettingsManager::UpdateLayoutNode() { QtViewPaneManager::instance()->SaveLayout(); XmlNodeRef rootLayoutNode = m_pSettingsManagerMemoryNode->findChild(EDITOR_LAYOUT_ROOT_NODE); if (!rootLayoutNode) { return; } XmlNodeRef xmlDockingLayoutNode = rootLayoutNode->findChild(EDITOR_LAYOUT_NODE); if (!xmlDockingLayoutNode) { return; } xmlDockingLayoutNode->removeAllChilds(); // serialize layout for main window XmlNodeRef xmlMainFrameLayoutNode = XmlHelpers::CreateXmlNode(MAINFRM_LAYOUT_NORMAL); xmlDockingLayoutNode->addChild(xmlMainFrameLayoutNode); QtViewPaneManager::instance()->SerializeLayout(xmlMainFrameLayoutNode); // serialize layout for panes TToolNamesMap::const_iterator tIt; for (tIt = m_toolNames.begin(); tIt != m_toolNames.end(); ++tIt) { const QString& toolName = tIt->first; QMainWindow* pane = FindViewPane(tIt->second); if (pane) { SaveLayoutSettings(pane->saveState(), toolName); } } } void CSettingsManager::GetMatchingLayoutNames(TToolNamesMap& foundTools, XmlNodeRef& resultNode, QString file) { // Need to populate in-memory node with available layouts. UpdateLayoutNode(); XmlNodeRef root = XmlHelpers::LoadXmlFromFile(file.toUtf8().data()); if (!root) { return; } root = root->findChild(EDITOR_LAYOUT_ROOT_NODE); if (!root) { return; } root = root->findChild(EDITOR_LAYOUT_NODE); if (!root) { return; } TToolNamesMap* toolNames = NULL; if (!foundTools.empty()) { toolNames = &foundTools; } else { toolNames = GetToolNames(); } if (!toolNames) { return; } if (toolNames->empty()) { return; } // toolIT->first is tool layout node name in XML file TToolNamesMap::const_iterator toolIT = toolNames->begin(); for (; toolIT != toolNames->end(); ++toolIT) { if (root->findChild(toolIT->first.toUtf8().data())) { foundTools[toolIT->first] = toolIT->second; if (resultNode) { resultNode->addChild(root->findChild(toolIT->first.toUtf8().data())); } } } } void CSettingsManager::ImportSettings(QString file) { XmlNodeRef root = XmlHelpers::LoadXmlFromFile(file.toUtf8().data()); if (!root) { return; } XmlNodeRef importedSettingsContentNode = root->findChild(EDITOR_SETTINGS_CONTENT_NODE); if (!importedSettingsContentNode) { return; } // Remove old settings node XmlNodeRef oldSettingsContentNode = m_pSettingsManagerMemoryNode->findChild(EDITOR_SETTINGS_CONTENT_NODE); m_pSettingsManagerMemoryNode->removeChild(oldSettingsContentNode); // Add new, imported settings node m_pSettingsManagerMemoryNode->addChild(importedSettingsContentNode); // Force global settings to reload from memory node instead of registry gSettings.bSettingsManagerMode = true; gSettings.Load(); gSettings.bSettingsManagerMode = false; // Dump ToolBox node on disk, replacing the old one XmlNodeRef toolBoxNode = root->findChild(TOOLBOX_NODE); if (toolBoxNode) { toolBoxNode->saveToFile(TOOLBOX_FILE); } // Dump UserTools node on disk, replacing the old one XmlNodeRef userToolsNode = root->findChild(TOOLBOXMACROS_NODE); if (userToolsNode) { QString macroFilePath; GetIEditor()->GetToolBoxManager()->GetSaveFilePath(macroFilePath); userToolsNode->saveToFile(macroFilePath.toUtf8().data()); GetIEditor()->GetToolBoxManager()->Load(); } // Get and update CVars SerializeCVars(root, true); GetIEditor()->SetStatusText("Import Successful"); } bool CSettingsManager::NeedSettingsNode(const QString& path) { if ((path != EDITOR_LAYOUT_ROOT_NODE) && (path != TOOLBOX_NODE) && (path != TOOLBOXMACROS_NODE)) { return TRUE; } else { return FALSE; } } void CSettingsManager::SerializeCVars(XmlNodeRef& node, bool bLoad) { int nNumberOfVariables(0); int nCurrentVariable(0); IConsole* piConsole(NULL); ICVar* piVariable(NULL); std::vector cszVariableNames; char* szKey(NULL); char* szValue(NULL); ICVar* piCVar(NULL); piConsole = gEnv->pConsole; if (!piConsole) { return; } if (bLoad) { XmlNodeRef readNode = NULL; XmlNodeRef inputCVarsNode = node->findChild(CVARS_NODE); if (!inputCVarsNode) { return; } // Remove Entities with ID`s from the list. for (int childNo = 0; childNo < inputCVarsNode->getChildCount(); ++childNo) { readNode = inputCVarsNode->getChild(childNo); for (int i = 0; i < readNode->getNumAttributes(); ++i) { readNode->getAttributeByIndex(i, (const char**)&szKey, (const char**)&szValue); piCVar = piConsole->GetCVar(szKey); if (!piCVar) { continue; } piCVar->Set(szValue); } } } else { XmlNodeRef newCVarNode = NULL; XmlNodeRef oldCVarsNode = node->findChild(CVARS_NODE); if (oldCVarsNode) { node->removeChild(oldCVarsNode); } XmlNodeRef cvarsNode = XmlHelpers::CreateXmlNode(CVARS_NODE); nNumberOfVariables = piConsole->GetNumVisibleVars(); cszVariableNames.resize(nNumberOfVariables, NULL); if (piConsole->GetSortedVars((const char**)&cszVariableNames.front(), nNumberOfVariables, NULL) != nNumberOfVariables) { assert(false); return; } for (nCurrentVariable = 0; nCurrentVariable < cszVariableNames.size(); ++nCurrentVariable) { if (_stricmp(cszVariableNames[nCurrentVariable], "_TestFormatMessage") == 0) { continue; } piVariable = piConsole->GetCVar(cszVariableNames[nCurrentVariable]); if (!piVariable) { assert(false); continue; } newCVarNode = XmlHelpers::CreateXmlNode(CVAR_NODE); newCVarNode->setAttr(cszVariableNames[nCurrentVariable], piVariable->GetString()); cvarsNode->addChild(newCVarNode); } node->addChild(cvarsNode); } } void CSettingsManager::ReadValueStr(XmlNodeRef& sourceNode, const QString& path, const QString& attr, QString& val) { // Make sure the path has "\" at the end QString fixedPath = path; // Make sure it ends with "\" before extraction of tokens QString slashChar = fixedPath.right(fixedPath.length() - 1); if (slashChar != "\\") { fixedPath += "\\"; } QStringList strNodes; Path::GetDirectoryQueue(fixedPath, strNodes); QString readAttr = attr; // Spaces in node names not allowed readAttr.replace(" ", ""); XmlNodeRef root = NULL; XmlNodeRef tmpNode = NULL; if (NeedSettingsNode(path)) { tmpNode = sourceNode->findChild(EDITOR_SETTINGS_CONTENT_NODE); } else { tmpNode = sourceNode; } if (!tmpNode) { return; } for (int i = 0; i < strNodes.size(); ++i) { if (tmpNode->findChild(strNodes[i].toUtf8().data())) { tmpNode = tmpNode->findChild(strNodes[i].toUtf8().data()); } else { return; } } if (!tmpNode->findChild(readAttr.toUtf8().data())) { return; } else { tmpNode = tmpNode->findChild(readAttr.toUtf8().data()); } if (tmpNode->haveAttr(EDITOR_SETTINGS_ATTRIB_NAME)) { tmpNode->getAttr(EDITOR_SETTINGS_ATTRIB_NAME, val); } return; } void CSettingsManager::RegisterEvent(const SEventLog& event) { if (event.m_eventName.isEmpty()) { return; } QString eventName = event.m_eventName; QString path(eventName); path.replace(" ", ""); SaveLogEventSetting(path, EVENT_LOG_EVENT_NAME, event.m_eventName); QString subPath = path + "\\" + EVENT_LOG_EVENT_NAME; SaveLogEventSetting(subPath, EVENT_LOG_CALLER_VERSION, event.m_callerVersion); SaveLogEventSetting(subPath, "state", event.m_eventState); QDateTime theTime = QDateTime::currentDateTimeUtc(); SaveLogEventSetting(subPath, "time", theTime.toString(LOG_DATETIME_FORMAT)); } void CSettingsManager::UnregisterEvent(const SEventLog& event) { XmlNodeRef root = XmlHelpers::LoadXmlFromFile(EDITOR_EVENT_LOG_FILE_PATH); if (!root) { return; } QString eventName = event.m_eventName; QString path(eventName); path.replace(" ", ""); QString subPath = path + "\\" + EVENT_LOG_EVENT_NAME; XmlNodeRef resNode = LoadLogEventSetting(subPath, EDITOR_EVENT_LOG_ATTRIB_NAME, eventName, root); if (!resNode) { return; } root->removeChild(resNode->getParent()); root->saveToFile(EDITOR_EVENT_LOG_FILE_PATH); } bool CSettingsManager::IsEventSafe(const SEventLog& event) { XmlNodeRef root = XmlHelpers::LoadXmlFromFile(EDITOR_EVENT_LOG_FILE_PATH); if (!root) { return TRUE; } QString eventName = event.m_eventName; QString path(eventName); path += "\\"; path += EVENT_LOG_EVENT_NAME; path.replace(" ", ""); XmlNodeRef resNode = LoadLogEventSetting(path, EDITOR_EVENT_LOG_ATTRIB_NAME, eventName, root); // Log entry not found, so it is safe to start if (!resNode) { return TRUE; } XmlNodeRef callerVersion = resNode->findChild(EVENT_LOG_CALLER_VERSION); if (callerVersion) { QString callerVersionStr; if (callerVersion->haveAttr(EDITOR_EVENT_LOG_ATTRIB_NAME)) { callerVersionStr = callerVersion->getAttr(EDITOR_EVENT_LOG_ATTRIB_NAME); } if (!callerVersionStr.isEmpty()) { if (callerVersionStr != GetToolVersion(eventName)) { return TRUE; } } // The same version of tool/level found return FALSE; } return TRUE; } ////////////////////////////////////////////////////////////////////////// void CSettingsManager::SaveLogEventSetting(const QString& path, const QString& attr, const QString& val) { QStringList strNodes; QString fixedPath = path; // Make sure it ends with "\" before extraction of tokens QString slashChar = fixedPath.right(fixedPath.length() - 1); if (slashChar != "\\") { fixedPath += "\\"; } Path::GetDirectoryQueue(fixedPath, strNodes); QString writeAttr = attr; // Simple cleanup of node names - remove all characters except letters, digits, // underscores, colons, periods, and hyphens. // If this ever needs to be 100% compliant, the following rules would need to // be added: // - name must only *start* with a letter or underscore (not a digit, colon, period, or hyphen) // - name must not start with "xml" in any case combination const QRegularExpression xmlNodeCleanupRegex("[^a-zA-Z0-9_:.\\-]*"); writeAttr.remove(xmlNodeCleanupRegex); for (auto& node : strNodes) { node.remove(xmlNodeCleanupRegex); } XmlNodeRef root = XmlHelpers::LoadXmlFromFile(EDITOR_EVENT_LOG_FILE_PATH); if (!root) { root = XmlHelpers::CreateXmlNode(EDITOR_EVENT_LOG_ROOT_NODE); root->saveToFile(EDITOR_LAYOUT_FILE_PATH); } XmlNodeRef tmpNode = root; for (int i = 0; i < strNodes.size(); ++i) { if (!tmpNode->findChild(strNodes[i].toUtf8().data())) { tmpNode->addChild(tmpNode->createNode(strNodes[i].toUtf8().data())); tmpNode = tmpNode->findChild(strNodes[i].toUtf8().data()); } else { tmpNode = tmpNode->findChild(strNodes[i].toUtf8().data()); } } if (!tmpNode->findChild(writeAttr.toUtf8().data())) { tmpNode->addChild(tmpNode->createNode(writeAttr.toUtf8().data())); tmpNode = tmpNode->findChild(writeAttr.toUtf8().data()); } else { tmpNode = tmpNode->findChild(writeAttr.toUtf8().data()); } if (!tmpNode) { return; } tmpNode->setAttr(EDITOR_EVENT_LOG_ATTRIB_NAME, val.toUtf8().data()); root->saveToFile(EDITOR_EVENT_LOG_FILE_PATH); } XmlNodeRef CSettingsManager::LoadLogEventSetting(const QString& path, const QString& attr, QString& val, XmlNodeRef& root) { // Make sure it ends with "\" before extraction of tokens QString fixedPath = path; QString slashChar = fixedPath.right(fixedPath.length() - 1); if (slashChar != "\\") { fixedPath += "\\"; } QStringList strNodes; Path::GetDirectoryQueue(fixedPath, strNodes); QString readAttr = attr; // Spaces in node names not allowed readAttr.replace(" ", ""); if (!root) { return 0; } XmlNodeRef tmpNode = NULL; tmpNode = root; if (!tmpNode) { return 0; } for (int i = 0; i < strNodes.size(); ++i) { if (tmpNode->findChild(strNodes[i].toUtf8().data())) { tmpNode = tmpNode->findChild(strNodes[i].toUtf8().data()); } else { return 0; } } if (tmpNode->haveAttr(readAttr.toUtf8().data())) { return tmpNode; } return 0; } QString CSettingsManager::GenerateContentHash(XmlNodeRef& node, QString sourceName) { QString hashStr(""); if (node->getChildCount() == 0) { return sourceName; } uint32 hash = CCrc32::ComputeLowercase(node->getXML(0)); hashStr = QString::number(hash); return hashStr; }