text: /*
   * Copyright (c) Contributors to the Open 3D Engine Project.
   * For complete copyright and license terms please see the LICENSE at the root of this distribution.
   *
   * SPDX-License-Identifier: Apache-2.0 OR MIT
   *
   */
  
  
  #include "EditorDefs.h"
  
  #include "SettingsManager.h"
  
  // Qt
  #include <QDateTime>
  
  // Editor
  #include "Settings.h"
  #include "MainWindow.h"
  #include "ToolBox.h"
  #include "QtViewPaneManager.h"
  
  
  #define TOOLBOX_FILE "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<AZStd::string> 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::string>();
          }
      }
  
      AZStd::vector<AZStd::string> result;
      BuildSettingsList_Helper(tmpNode, "", result);
  
      return result;
  }
  
  void CSettingsManager::BuildSettingsList_Helper(const XmlNodeRef& node, const AZStd::string_view& pathToNode, AZStd::vector<AZStd::string>& 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<QMainWindow>(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<char*>  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, [[maybe_unused]] 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;
  }
  