<commit_before>// Copyright (C) 2010 - 2015 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and The University
// of Manchester.
// All rights reserved.

// Copyright (C) 2008 - 2009 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2006 - 2007 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc. and EML Research, gGmbH.
// All rights reserved.

#include "copasi.h"

#include "CConfigurationFile.h"
#include "COptions.h"

#include "CopasiDataModel/CCopasiDataModel.h"
#include "utilities/CVersion.h"
#include "utilities/utility.h"
#include "utilities/CDirEntry.h"
#include "xml/CCopasiXMLParser.h"
#include "MIRIAM/CConstants.h"

CRecentFiles::CRecentFiles(const std::string & name,
                           const CCopasiContainer * pParent):
  CCopasiParameterGroup(name, pParent),
  mpMaxFiles(NULL),
  mpRecentFiles(NULL)
{initializeParameter();}

CRecentFiles::CRecentFiles(const CRecentFiles & src,
                           const CCopasiContainer * pParent):
  CCopasiParameterGroup(src, pParent),
  mpMaxFiles(NULL),
  mpRecentFiles(NULL)
{initializeParameter();}

CRecentFiles::CRecentFiles(const CCopasiParameterGroup & group,
                           const CCopasiContainer * pParent):
  CCopasiParameterGroup(group, pParent),
  mpMaxFiles(NULL),
  mpRecentFiles(NULL)
{initializeParameter();}

CRecentFiles::~CRecentFiles()
{}

void CRecentFiles::initializeParameter()
{
  mpMaxFiles =
    assertParameter("MaxFiles", CCopasiParameter::UINT, (unsigned C_INT32) 5)->getValue().pUINT;
  mpRecentFiles = assertGroup("Recent Files");
}

void CRecentFiles::addFile(const std::string & file)
{
  std::string FileName = CDirEntry::normalize(file);

#ifdef WIN32
  std::string::size_type pos = FileName.find('\\');

  while (pos != std::string::npos)
    {
      FileName[pos] = '/';
      pos = FileName.find('\\', pos);
    }

#endif

  std::string PWD;
  COptions::getValue("PWD", PWD);

  if (CDirEntry::isRelativePath(FileName) &&
      !CDirEntry::makePathAbsolute(FileName, PWD))
    FileName = CDirEntry::fileName(FileName);

  CCopasiParameterGroup::index_iterator it = mpRecentFiles->beginIndex();
  CCopasiParameterGroup::index_iterator end = mpRecentFiles->endIndex();

  std::string NewFile = FileName;
  std::string ExistingFile;

  for (; it != end; ++it)
    {
      ExistingFile = *(*it)->getValue().pSTRING;
      (*it)->setValue(NewFile);

      if (ExistingFile == FileName) return;

      NewFile = ExistingFile;
    }

  if (mpRecentFiles->size() < *mpMaxFiles)
    mpRecentFiles->addParameter("File", CCopasiParameter::STRING, NewFile);

  return;
}

CConfigurationFile::CConfigurationFile(const std::string & name,
                                       const CCopasiContainer * pParent):
  CCopasiParameterGroup(name, pParent),
  mpRecentFiles(NULL),
  mpRecentSBMLFiles(NULL),
  mpRecentSEDMLFiles(NULL),
  mpRecentMIRIAMResources(NULL),
  mpApplicationFont(NULL),
  mpValidateUnits(NULL),
  mpUseOpenGL(NULL),
  mpUseAdvancedSliders(NULL),
  mpUseAdvancedEditing(NULL),
  mpNormalizePerExperiment(NULL),
  mpWorkingDirectory(NULL)
{initializeParameter();}

CConfigurationFile::CConfigurationFile(const CConfigurationFile & src,
                                       const CCopasiContainer * pParent):
  CCopasiParameterGroup(src, pParent),
  mpRecentFiles(NULL),
  mpRecentSBMLFiles(NULL),
  mpRecentSEDMLFiles(NULL),
  mpRecentMIRIAMResources(NULL),
  mpApplicationFont(NULL),
  mpValidateUnits(NULL),
  mpUseOpenGL(NULL),
  mpUseAdvancedSliders(NULL),
  mpUseAdvancedEditing(NULL),
  mpNormalizePerExperiment(NULL),
  mpWorkingDirectory(NULL)
{initializeParameter();}

CConfigurationFile::CConfigurationFile(const CCopasiParameterGroup & group,
                                       const CCopasiContainer * pParent):
  CCopasiParameterGroup(group, pParent),
  mpRecentFiles(NULL),
  mpRecentSBMLFiles(NULL),
  mpRecentSEDMLFiles(NULL),
  mpRecentMIRIAMResources(NULL),
  mpApplicationFont(NULL),
  mpValidateUnits(NULL),
  mpUseOpenGL(NULL),
  mpUseAdvancedSliders(NULL),
  mpUseAdvancedEditing(NULL),
  mpNormalizePerExperiment(NULL),
  mpWorkingDirectory(NULL)
{initializeParameter();}

CConfigurationFile::~CConfigurationFile()
{}

bool CConfigurationFile::elevateChildren()
{
  bool success = true;

  mpRecentFiles =
    elevate<CRecentFiles, CCopasiParameterGroup>(getGroup("Recent Files"));

  if (!mpRecentFiles) success = false;

  mpRecentSBMLFiles =
    elevate<CRecentFiles, CCopasiParameterGroup>(getGroup("Recent SBML Files"));

  if (!mpRecentSBMLFiles) success = false;

  mpRecentSEDMLFiles =
    elevate<CRecentFiles, CCopasiParameterGroup>(getGroup("Recent SEDML Files"));

  if (!mpRecentSEDMLFiles) success = false;

  mpRecentMIRIAMResources =
    elevate<CMIRIAMResources, CCopasiParameterGroup>(getGroup("MIRIAM Resources"));
  CMIRIAMResourceObject::setMIRIAMResources(mpRecentMIRIAMResources);

  if (!mpRecentMIRIAMResources) success = false;

  return success;
}

void CConfigurationFile::initializeParameter()
{
  assertGroup("Recent Files");
  assertGroup("Recent SBML Files");
  assertGroup("Recent SEDML Files");

  mpApplicationFont =
    assertParameter("Application Font", CCopasiParameter::STRING, std::string(""))->getValue().pSTRING;

  assertGroup("MIRIAM Resources");

  mpValidateUnits = assertParameter("Validate Units", CCopasiParameter::BOOL, false)->getValue().pBOOL;
  mpUseOpenGL = assertParameter("Use OpenGL", CCopasiParameter::BOOL, false)->getValue().pBOOL;
  mpUseAdvancedSliders = assertParameter("Use Advanced Sliders", CCopasiParameter::BOOL, true)->getValue().pBOOL;
  mpUseAdvancedEditing = assertParameter("Use Advanced Editing", CCopasiParameter::BOOL, false)->getValue().pBOOL;
  mpNormalizePerExperiment = assertParameter("Normalize Weights per Experiment", CCopasiParameter::BOOL, false)->getValue().pBOOL;
  mpWorkingDirectory = assertParameter("Working Directory", CCopasiParameter::STRING, std::string(""))->getValue().pSTRING;

  elevateChildren();
}

bool CConfigurationFile::save()
{
  std::string ConfigFile;
  COptions::getValue("ConfigFile", ConfigFile);

  CConfigurationFile::CXML XML;

  XML.setConfiguration(*this);

  bool success = XML.CCopasiXMLInterface::save(ConfigFile, ConfigFile);

  return success;
}

bool CConfigurationFile::load()
{
  std::string ConfigFile;
  COptions::getValue("ConfigFile", ConfigFile);

  CConfigurationFile::CXML XML;

  bool success = XML.CCopasiXMLInterface::load(ConfigFile, ConfigFile);

  if (success)
    {
      *this = XML.getConfiguration();
      initializeParameter();
    }

  if (mpRecentMIRIAMResources->getResourceList().size() == 0)
    {
      // We load the default MIRIAM resources, which are part of the COPASI installation.
      std::string MIRIAMResourceFile;
      COptions::getValue("DefaultConfigDir", MIRIAMResourceFile);
      MIRIAMResourceFile += CDirEntry::Separator + "MIRIAMResources.xml";

      CConfigurationFile::CXML XMLMIRIAMResource;

      if (XMLMIRIAMResource.CCopasiXMLInterface::load(MIRIAMResourceFile,
          MIRIAMResourceFile))
        {
          const CCopasiParameterGroup* group = XMLMIRIAMResource.getConfiguration().getGroup("MIRIAM Resoures");

          if (group == NULL) return false;

          *mpRecentMIRIAMResources = *group;
          mpRecentMIRIAMResources->initializeParameter();
        }
      else
        success = false;
    }

  return success;
}

CRecentFiles & CConfigurationFile::getRecentFiles()
{return *mpRecentFiles;}

CRecentFiles & CConfigurationFile::getRecentSBMLFiles()
{return *mpRecentSBMLFiles;}

//TODO SEDML
CRecentFiles & CConfigurationFile::getRecentSEDMLFiles()
{return *mpRecentSEDMLFiles;}

CMIRIAMResources & CConfigurationFile::getRecentMIRIAMResources()
{return *mpRecentMIRIAMResources;}

void CConfigurationFile::setRecentMIRIAMResources(const CMIRIAMResources & miriamResources)
{* mpRecentMIRIAMResources = miriamResources;}

const std::string CConfigurationFile::getApplicationFont() const
{
  return *mpApplicationFont;
}

void CConfigurationFile::setApplicationFont(const std::string & applicationFont)
{
  *mpApplicationFont = applicationFont;
}

bool CConfigurationFile::useOpenGL() const
{
  return *mpUseOpenGL;
}

void CConfigurationFile::setUseOpenGL(bool useOpenGL)
{
  *mpUseOpenGL = useOpenGL;
}

bool CConfigurationFile::useAdvancedSliders() const
{
  return *mpUseAdvancedSliders;
}

void CConfigurationFile::setUseAdvancedSliders(bool useAdvancedSliders)
{
  *mpUseAdvancedSliders = useAdvancedSliders;
}

bool CConfigurationFile::useAdvancedEditing() const
{
  return *mpUseAdvancedEditing;
}

void CConfigurationFile::setUseAdvancedEditing(bool useAdvancedEditing)
{
  *mpUseAdvancedEditing = useAdvancedEditing;
}

bool CConfigurationFile::normalizePerExperiment() const
{
  return *mpNormalizePerExperiment;
}

void CConfigurationFile::setNormalizePerExperiment(bool flag)
{
  *mpNormalizePerExperiment = flag;
}

bool CConfigurationFile::validateUnits() const
{
  return *mpValidateUnits;
}

const std::string CConfigurationFile::getWorkingDirectory() const
{
  return *mpWorkingDirectory;
}

void CConfigurationFile::setWorkingDirectory(const std::string & workingDirectory)
{
  *mpWorkingDirectory = workingDirectory;
}

void CConfigurationFile::setValidateUnits(bool validateUnits)
{
  *mpValidateUnits = validateUnits;
}

CConfigurationFile::CXML::CXML():
  CCopasiXMLInterface(),
  mConfiguration("Configuration")
{
  mConfiguration.assertGroup("Recent Files");
  mConfiguration.assertGroup("Recent SBML Files");
  mConfiguration.assertGroup("MIRIAM Resources");
  mConfiguration.assertGroup("Recent SEDML Files");
}

CConfigurationFile::CXML::~CXML()
{}

bool CConfigurationFile::CXML::save(std::ostream & os,
                                    const std::string & relativeTo)
{
  mPWD = relativeTo;

  os.imbue(std::locale::classic());
  os.precision(16);

  mpOstream = &os;

  *mpOstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
             << std::endl;

  *mpOstream << "<!-- generated with COPASI "
             << CVersion::VERSION.getVersion()
             << " (http://www.copasi.org) at "
             << UTCTimeStamp()
             << " UTC -->"
             << std::endl;

  saveParameter(mConfiguration);

  return true;
}

bool CConfigurationFile::CXML::load(std::istream & is,
                                    const std::string & relativeTo)
{
  mPWD = relativeTo;

  is.imbue(std::locale::classic());
  is.precision(16);

  mpIstream = &is;
  bool success = true;
  bool done = false;

  CVersion Version;
  CCopasiXMLParser Parser(Version);

#define BUFFER_SIZE 0xfffe
  char * pBuffer = new char[BUFFER_SIZE + 1];

  while (!done)
    {
      mpIstream->get(pBuffer, BUFFER_SIZE, 0);

      if (mpIstream->eof()) done = true;

      if (mpIstream->fail() && !done)
        {
          std::string ConfigFile;
          COptions::getValue("ConfigFile", ConfigFile);
          CCopasiMessage Message(CCopasiMessage::WARNING, MCConfiguration + 2, ConfigFile.c_str());

          done = true;
          success = false;
        }

      if (!Parser.parse(pBuffer, -1, done))
        {
          CCopasiMessage Message(CCopasiMessage::RAW, MCXML + 2,
                                 Parser.getCurrentLineNumber(),
                                 Parser.getCurrentColumnNumber(),
                                 Parser.getErrorString());
          done = true;
          success = false;
        }
    }

  delete [] pBuffer;
#undef BUFFER_SIZE

  if (success && Parser.getCurrentGroup() != NULL)
    {
      mConfiguration = * Parser.getCurrentGroup();
      mConfiguration.setObjectName("Configuration");

      delete Parser.getCurrentGroup();
    }
  else
    mConfiguration.clear();

  return success;
}

void CConfigurationFile::CXML::setConfiguration(const CCopasiParameterGroup & configuration)
{mConfiguration = configuration;}

const CCopasiParameterGroup & CConfigurationFile::CXML::getConfiguration() const
{return mConfiguration;}
<commit_msg>Issue 2126: The default for the default of Normalize Weight per Experiment is now true, which is the existing behavior.<commit_after>// Copyright (C) 2010 - 2015 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and The University
// of Manchester.
// All rights reserved.

// Copyright (C) 2008 - 2009 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2006 - 2007 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc. and EML Research, gGmbH.
// All rights reserved.

#include "copasi.h"

#include "CConfigurationFile.h"
#include "COptions.h"

#include "CopasiDataModel/CCopasiDataModel.h"
#include "utilities/CVersion.h"
#include "utilities/utility.h"
#include "utilities/CDirEntry.h"
#include "xml/CCopasiXMLParser.h"
#include "MIRIAM/CConstants.h"

CRecentFiles::CRecentFiles(const std::string & name,
                           const CCopasiContainer * pParent):
  CCopasiParameterGroup(name, pParent),
  mpMaxFiles(NULL),
  mpRecentFiles(NULL)
{initializeParameter();}

CRecentFiles::CRecentFiles(const CRecentFiles & src,
                           const CCopasiContainer * pParent):
  CCopasiParameterGroup(src, pParent),
  mpMaxFiles(NULL),
  mpRecentFiles(NULL)
{initializeParameter();}

CRecentFiles::CRecentFiles(const CCopasiParameterGroup & group,
                           const CCopasiContainer * pParent):
  CCopasiParameterGroup(group, pParent),
  mpMaxFiles(NULL),
  mpRecentFiles(NULL)
{initializeParameter();}

CRecentFiles::~CRecentFiles()
{}

void CRecentFiles::initializeParameter()
{
  mpMaxFiles =
    assertParameter("MaxFiles", CCopasiParameter::UINT, (unsigned C_INT32) 5)->getValue().pUINT;
  mpRecentFiles = assertGroup("Recent Files");
}

void CRecentFiles::addFile(const std::string & file)
{
  std::string FileName = CDirEntry::normalize(file);

#ifdef WIN32
  std::string::size_type pos = FileName.find('\\');

  while (pos != std::string::npos)
    {
      FileName[pos] = '/';
      pos = FileName.find('\\', pos);
    }

#endif

  std::string PWD;
  COptions::getValue("PWD", PWD);

  if (CDirEntry::isRelativePath(FileName) &&
      !CDirEntry::makePathAbsolute(FileName, PWD))
    FileName = CDirEntry::fileName(FileName);

  CCopasiParameterGroup::index_iterator it = mpRecentFiles->beginIndex();
  CCopasiParameterGroup::index_iterator end = mpRecentFiles->endIndex();

  std::string NewFile = FileName;
  std::string ExistingFile;

  for (; it != end; ++it)
    {
      ExistingFile = *(*it)->getValue().pSTRING;
      (*it)->setValue(NewFile);

      if (ExistingFile == FileName) return;

      NewFile = ExistingFile;
    }

  if (mpRecentFiles->size() < *mpMaxFiles)
    mpRecentFiles->addParameter("File", CCopasiParameter::STRING, NewFile);

  return;
}

CConfigurationFile::CConfigurationFile(const std::string & name,
                                       const CCopasiContainer * pParent):
  CCopasiParameterGroup(name, pParent),
  mpRecentFiles(NULL),
  mpRecentSBMLFiles(NULL),
  mpRecentSEDMLFiles(NULL),
  mpRecentMIRIAMResources(NULL),
  mpApplicationFont(NULL),
  mpValidateUnits(NULL),
  mpUseOpenGL(NULL),
  mpUseAdvancedSliders(NULL),
  mpUseAdvancedEditing(NULL),
  mpNormalizePerExperiment(NULL),
  mpWorkingDirectory(NULL)
{initializeParameter();}

CConfigurationFile::CConfigurationFile(const CConfigurationFile & src,
                                       const CCopasiContainer * pParent):
  CCopasiParameterGroup(src, pParent),
  mpRecentFiles(NULL),
  mpRecentSBMLFiles(NULL),
  mpRecentSEDMLFiles(NULL),
  mpRecentMIRIAMResources(NULL),
  mpApplicationFont(NULL),
  mpValidateUnits(NULL),
  mpUseOpenGL(NULL),
  mpUseAdvancedSliders(NULL),
  mpUseAdvancedEditing(NULL),
  mpNormalizePerExperiment(NULL),
  mpWorkingDirectory(NULL)
{initializeParameter();}

CConfigurationFile::CConfigurationFile(const CCopasiParameterGroup & group,
                                       const CCopasiContainer * pParent):
  CCopasiParameterGroup(group, pParent),
  mpRecentFiles(NULL),
  mpRecentSBMLFiles(NULL),
  mpRecentSEDMLFiles(NULL),
  mpRecentMIRIAMResources(NULL),
  mpApplicationFont(NULL),
  mpValidateUnits(NULL),
  mpUseOpenGL(NULL),
  mpUseAdvancedSliders(NULL),
  mpUseAdvancedEditing(NULL),
  mpNormalizePerExperiment(NULL),
  mpWorkingDirectory(NULL)
{initializeParameter();}

CConfigurationFile::~CConfigurationFile()
{}

bool CConfigurationFile::elevateChildren()
{
  bool success = true;

  mpRecentFiles =
    elevate<CRecentFiles, CCopasiParameterGroup>(getGroup("Recent Files"));

  if (!mpRecentFiles) success = false;

  mpRecentSBMLFiles =
    elevate<CRecentFiles, CCopasiParameterGroup>(getGroup("Recent SBML Files"));

  if (!mpRecentSBMLFiles) success = false;

  mpRecentSEDMLFiles =
    elevate<CRecentFiles, CCopasiParameterGroup>(getGroup("Recent SEDML Files"));

  if (!mpRecentSEDMLFiles) success = false;

  mpRecentMIRIAMResources =
    elevate<CMIRIAMResources, CCopasiParameterGroup>(getGroup("MIRIAM Resources"));
  CMIRIAMResourceObject::setMIRIAMResources(mpRecentMIRIAMResources);

  if (!mpRecentMIRIAMResources) success = false;

  return success;
}

void CConfigurationFile::initializeParameter()
{
  assertGroup("Recent Files");
  assertGroup("Recent SBML Files");
  assertGroup("Recent SEDML Files");

  mpApplicationFont =
    assertParameter("Application Font", CCopasiParameter::STRING, std::string(""))->getValue().pSTRING;

  assertGroup("MIRIAM Resources");

  mpValidateUnits = assertParameter("Validate Units", CCopasiParameter::BOOL, false)->getValue().pBOOL;
  mpUseOpenGL = assertParameter("Use OpenGL", CCopasiParameter::BOOL, false)->getValue().pBOOL;
  mpUseAdvancedSliders = assertParameter("Use Advanced Sliders", CCopasiParameter::BOOL, true)->getValue().pBOOL;
  mpUseAdvancedEditing = assertParameter("Use Advanced Editing", CCopasiParameter::BOOL, false)->getValue().pBOOL;
  mpNormalizePerExperiment = assertParameter("Normalize Weights per Experiment", CCopasiParameter::BOOL, true)->getValue().pBOOL;
  mpWorkingDirectory = assertParameter("Working Directory", CCopasiParameter::STRING, std::string(""))->getValue().pSTRING;

  elevateChildren();
}

bool CConfigurationFile::save()
{
  std::string ConfigFile;
  COptions::getValue("ConfigFile", ConfigFile);

  CConfigurationFile::CXML XML;

  XML.setConfiguration(*this);

  bool success = XML.CCopasiXMLInterface::save(ConfigFile, ConfigFile);

  return success;
}

bool CConfigurationFile::load()
{
  std::string ConfigFile;
  COptions::getValue("ConfigFile", ConfigFile);

  CConfigurationFile::CXML XML;

  bool success = XML.CCopasiXMLInterface::load(ConfigFile, ConfigFile);

  if (success)
    {
      *this = XML.getConfiguration();
      initializeParameter();
    }

  if (mpRecentMIRIAMResources->getResourceList().size() == 0)
    {
      // We load the default MIRIAM resources, which are part of the COPASI installation.
      std::string MIRIAMResourceFile;
      COptions::getValue("DefaultConfigDir", MIRIAMResourceFile);
      MIRIAMResourceFile += CDirEntry::Separator + "MIRIAMResources.xml";

      CConfigurationFile::CXML XMLMIRIAMResource;

      if (XMLMIRIAMResource.CCopasiXMLInterface::load(MIRIAMResourceFile,
          MIRIAMResourceFile))
        {
          const CCopasiParameterGroup* group = XMLMIRIAMResource.getConfiguration().getGroup("MIRIAM Resoures");

          if (group == NULL) return false;

          *mpRecentMIRIAMResources = *group;
          mpRecentMIRIAMResources->initializeParameter();
        }
      else
        success = false;
    }

  return success;
}

CRecentFiles & CConfigurationFile::getRecentFiles()
{return *mpRecentFiles;}

CRecentFiles & CConfigurationFile::getRecentSBMLFiles()
{return *mpRecentSBMLFiles;}

//TODO SEDML
CRecentFiles & CConfigurationFile::getRecentSEDMLFiles()
{return *mpRecentSEDMLFiles;}

CMIRIAMResources & CConfigurationFile::getRecentMIRIAMResources()
{return *mpRecentMIRIAMResources;}

void CConfigurationFile::setRecentMIRIAMResources(const CMIRIAMResources & miriamResources)
{* mpRecentMIRIAMResources = miriamResources;}

const std::string CConfigurationFile::getApplicationFont() const
{
  return *mpApplicationFont;
}

void CConfigurationFile::setApplicationFont(const std::string & applicationFont)
{
  *mpApplicationFont = applicationFont;
}

bool CConfigurationFile::useOpenGL() const
{
  return *mpUseOpenGL;
}

void CConfigurationFile::setUseOpenGL(bool useOpenGL)
{
  *mpUseOpenGL = useOpenGL;
}

bool CConfigurationFile::useAdvancedSliders() const
{
  return *mpUseAdvancedSliders;
}

void CConfigurationFile::setUseAdvancedSliders(bool useAdvancedSliders)
{
  *mpUseAdvancedSliders = useAdvancedSliders;
}

bool CConfigurationFile::useAdvancedEditing() const
{
  return *mpUseAdvancedEditing;
}

void CConfigurationFile::setUseAdvancedEditing(bool useAdvancedEditing)
{
  *mpUseAdvancedEditing = useAdvancedEditing;
}

bool CConfigurationFile::normalizePerExperiment() const
{
  return *mpNormalizePerExperiment;
}

void CConfigurationFile::setNormalizePerExperiment(bool flag)
{
  *mpNormalizePerExperiment = flag;
}

bool CConfigurationFile::validateUnits() const
{
  return *mpValidateUnits;
}

const std::string CConfigurationFile::getWorkingDirectory() const
{
  return *mpWorkingDirectory;
}

void CConfigurationFile::setWorkingDirectory(const std::string & workingDirectory)
{
  *mpWorkingDirectory = workingDirectory;
}

void CConfigurationFile::setValidateUnits(bool validateUnits)
{
  *mpValidateUnits = validateUnits;
}

CConfigurationFile::CXML::CXML():
  CCopasiXMLInterface(),
  mConfiguration("Configuration")
{
  mConfiguration.assertGroup("Recent Files");
  mConfiguration.assertGroup("Recent SBML Files");
  mConfiguration.assertGroup("MIRIAM Resources");
  mConfiguration.assertGroup("Recent SEDML Files");
}

CConfigurationFile::CXML::~CXML()
{}

bool CConfigurationFile::CXML::save(std::ostream & os,
                                    const std::string & relativeTo)
{
  mPWD = relativeTo;

  os.imbue(std::locale::classic());
  os.precision(16);

  mpOstream = &os;

  *mpOstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
             << std::endl;

  *mpOstream << "<!-- generated with COPASI "
             << CVersion::VERSION.getVersion()
             << " (http://www.copasi.org) at "
             << UTCTimeStamp()
             << " UTC -->"
             << std::endl;

  saveParameter(mConfiguration);

  return true;
}

bool CConfigurationFile::CXML::load(std::istream & is,
                                    const std::string & relativeTo)
{
  mPWD = relativeTo;

  is.imbue(std::locale::classic());
  is.precision(16);

  mpIstream = &is;
  bool success = true;
  bool done = false;

  CVersion Version;
  CCopasiXMLParser Parser(Version);

#define BUFFER_SIZE 0xfffe
  char * pBuffer = new char[BUFFER_SIZE + 1];

  while (!done)
    {
      mpIstream->get(pBuffer, BUFFER_SIZE, 0);

      if (mpIstream->eof()) done = true;

      if (mpIstream->fail() && !done)
        {
          std::string ConfigFile;
          COptions::getValue("ConfigFile", ConfigFile);
          CCopasiMessage Message(CCopasiMessage::WARNING, MCConfiguration + 2, ConfigFile.c_str());

          done = true;
          success = false;
        }

      if (!Parser.parse(pBuffer, -1, done))
        {
          CCopasiMessage Message(CCopasiMessage::RAW, MCXML + 2,
                                 Parser.getCurrentLineNumber(),
                                 Parser.getCurrentColumnNumber(),
                                 Parser.getErrorString());
          done = true;
          success = false;
        }
    }

  delete [] pBuffer;
#undef BUFFER_SIZE

  if (success && Parser.getCurrentGroup() != NULL)
    {
      mConfiguration = * Parser.getCurrentGroup();
      mConfiguration.setObjectName("Configuration");

      delete Parser.getCurrentGroup();
    }
  else
    mConfiguration.clear();

  return success;
}

void CConfigurationFile::CXML::setConfiguration(const CCopasiParameterGroup & configuration)
{mConfiguration = configuration;}

const CCopasiParameterGroup & CConfigurationFile::CXML::getConfiguration() const
{return mConfiguration;}
<|endoftext|>


<commit_before>#ifndef slic3r_PrintBase_hpp_
#define slic3r_PrintBase_hpp_

#include "libslic3r.h"
#include <atomic>
#include <set>
#include <vector>
#include <string>
#include <functional>

#include "tbb/atomic.h"
// tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros.
#ifndef NOMINMAX
    #define NOMINMAX
#endif
#include "tbb/mutex.h"

#include "Model.hpp"
#include "PrintConfig.hpp"

namespace Slic3r {

class CanceledException : public std::exception {
public:
   const char* what() const throw() { return "Background processing has been canceled"; }
};

// To be instantiated over PrintStep or PrintObjectStep enums.
template <class StepType, size_t COUNT>
class PrintState
{
public:
    PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i].store(INVALID, std::memory_order_relaxed); }

    enum State {
        INVALID,
        STARTED,
        DONE,
    };

    // With full memory barrier.
    bool is_done(StepType step) const { return m_state[step] == DONE; }

    // Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being
    // modified by the UI thread.
    // This is necessary to block until the Print::apply_config() updates its state, which may
    // influence the processing step being entered.
    void set_started(StepType step, tbb::mutex &mtx) {
        mtx.lock();
        m_state[step].store(STARTED, std::memory_order_relaxed);
        mtx.unlock();
    }

    // Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being
    // modified by the UI thread.
    void set_done(StepType step, tbb::mutex &mtx) {
        mtx.lock();
        m_state[step].store(DONE, std::memory_order_relaxed);
        mtx.unlock();
    }

    // Make the step invalid.
    // The provided mutex should be locked at this point, guarding access to m_state.
    // In case the step has already been entered or finished, cancel the background
    // processing by calling the cancel callback.
    template<typename CancelationCallback>
    bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback cancel) {
        bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID;
        if (invalidated) {
#if 0
            if (mtx.state != mtx.HELD) {
                printf("Not held!\n");
            }
#endif
            // Raise the mutex, so that the following cancel() callback could cancel
            // the background processing.
            mtx.unlock();
            cancel();
            m_state[step] = INVALID;
            mtx.lock();
        }
        return invalidated;
    }

    template<typename CancelationCallback, typename StepTypeIterator>
    bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, tbb::mutex &mtx, CancelationCallback cancel) {
        bool invalidated = false;
        for (StepTypeIterator it = step_begin; ! invalidated && it != step_end; ++ it)
            invalidated = m_state[*it].load(std::memory_order_relaxed) != INVALID;
        if (invalidated) {
#if 0
            if (mtx.state != mtx.HELD) {
                printf("Not held!\n");
            }
#endif
            // Raise the mutex, so that the following cancel() callback could cancel
            // the background processing.
            mtx.unlock();
            cancel();
            for (StepTypeIterator it = step_begin; it != step_end; ++ it)
                m_state[*it] = INVALID;
            mtx.lock();
        }
        return invalidated;
    }

    // Make all steps invalid.
    // The provided mutex should be locked at this point, guarding access to m_state.
    // In case any step has already been entered or finished, cancel the background
    // processing by calling the cancel callback.
    template<typename CancelationCallback>
    bool invalidate_all(tbb::mutex &mtx, CancelationCallback cancel) {
        bool invalidated = false;
        for (size_t i = 0; i < COUNT; ++ i)
            if (m_state[i].load(std::memory_order_relaxed) != INVALID) {
                invalidated = true;
                break;
            }
        if (invalidated) {
            mtx.unlock();
            cancel();
            for (size_t i = 0; i < COUNT; ++ i)
                m_state[i].store(INVALID, std::memory_order_relaxed);
            mtx.lock();
        }
        return invalidated;
    }

private:
    std::atomic<State>          m_state[COUNT];
};

class PrintBase;

class PrintObjectBase
{
protected:
    virtual ~PrintObjectBase() {}
    // Declared here to allow access from PrintBase through friendship.
	static tbb::mutex&            cancel_mutex(PrintBase *print);
	static std::function<void()>  cancel_callback(PrintBase *print);
};

/**
 * @brief Printing involves slicing and export of device dependent instructions.
 *
 * Every technology has a potentially different set of requirements for
 * slicing, support structures and output print instructions. The pipeline
 * however remains roughly the same:
 *      slice -> convert to instructions -> send to printer
 *
 * The PrintBase class will abstract this flow for different technologies.
 *
 */
class PrintBase
{
public:
	PrintBase() { this->restart(); }
    inline virtual ~PrintBase() {}

    virtual PrinterTechnology technology() const noexcept = 0;

    // Reset the print status including the copy of the Model / ModelObject hierarchy.
    virtual void            clear() = 0;

    enum ApplyStatus {
        // No change after the Print::apply() call.
        APPLY_STATUS_UNCHANGED,
        // Some of the Print / PrintObject / PrintObjectInstance data was changed,
        // but no result was invalidated (only data influencing not yet calculated results were changed).
        APPLY_STATUS_CHANGED,
        // Some data was changed, which in turn invalidated already calculated steps.
        APPLY_STATUS_INVALIDATED,
    };
    virtual ApplyStatus     apply(const Model &model, const DynamicPrintConfig &config) = 0;

    virtual void            process() = 0;

    typedef std::function<void(int, const std::string&)>  status_callback_type;
    // Default status console print out in the form of percent => message.
    void                    set_status_default() { m_status_callback = nullptr; }
    // No status output or callback whatsoever, useful mostly for automatic tests.
    void                    set_status_silent() { m_status_callback = [](int, const std::string&){}; }
    // Register a custom status callback.
    void                    set_status_callback(status_callback_type cb) { m_status_callback = cb; }
    // Calls a registered callback to update the status, or print out the default message.
    void                    set_status(int percent, const std::string &message) {
        if (m_status_callback) m_status_callback(percent, message);
        else printf("%d => %s\n", percent, message.c_str());
    }

    typedef std::function<void()>  cancel_callback_type;
    // Various methods will call this callback to stop the background processing (the Print::process() call)
    // in case a successive change of the Print / PrintObject / PrintRegion instances changed
    // the state of the finished or running calculations.
    void                    set_cancel_callback(cancel_callback_type cancel_callback) { m_cancel_callback = cancel_callback; }
    // Has the calculation been canceled?
	enum CancelStatus {
		// No cancelation, background processing should run.
		NOT_CANCELED = 0,
		// Canceled by user from the user interface (user pressed the "Cancel" button or user closed the application).
		CANCELED_BY_USER = 1,
		// Canceled internally from Print::apply() through the Print/PrintObject::invalidate_step() or ::invalidate_all_steps().
		CANCELED_INTERNAL = 2
	};
    CancelStatus            cancel_status() const { return m_cancel_status; }
    // Has the calculation been canceled?
	bool                   canceled() const { return m_cancel_status != NOT_CANCELED; }
    // Cancel the running computation. Stop execution of all the background threads.
	void                   cancel() { m_cancel_status = CANCELED_BY_USER; }
	void                   cancel_internal() { m_cancel_status = CANCELED_INTERNAL; }
    // Cancel the running computation. Stop execution of all the background threads.
	void                   restart() { m_cancel_status = NOT_CANCELED; }

protected:
	friend class PrintObjectBase;

    tbb::mutex&            cancel_mutex() { return m_cancel_mutex; }
    std::function<void()>  cancel_callback() { return m_cancel_callback; }
	void				   call_cancell_callback() { m_cancel_callback(); }

    // If the background processing stop was requested, throw CanceledException.
    // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
    void                   throw_if_canceled() const { if (m_cancel_status) throw CanceledException(); }

private:
    tbb::atomic<CancelStatus>               m_cancel_status;
    // Callback to be evoked regularly to update state of the UI thread.
    status_callback_type                    m_status_callback;

    // Callback to be evoked to stop the background processing before a state is updated.
    cancel_callback_type                    m_cancel_callback = [](){};

    // Mutex used for synchronization of the worker thread with the UI thread:
    // The mutex will be used to guard the worker thread against entering a stage
    // while the data influencing the stage is modified.
    mutable tbb::mutex                      m_cancel_mutex;
};

template<typename PrintStepEnum, const size_t COUNT>
class PrintBaseWithState : public PrintBase
{
public:
    bool            is_step_done(PrintStepEnum step) const { return m_state.is_done(step); }

protected:
    void            set_started(PrintStepEnum step) { m_state.set_started(step, this->cancel_mutex()); throw_if_canceled(); }
    void            set_done(PrintStepEnum step) { m_state.set_done(step, this->cancel_mutex()); throw_if_canceled(); }
    bool            invalidate_step(PrintStepEnum step)
		{ return m_state.invalidate(step, this->cancel_mutex(), this->cancel_callback()); }
    template<typename StepTypeIterator>
    bool            invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
        { return m_state.invalidate_multiple(step_begin, step_end, this->cancel_mutex(), this->cancel_callback()); }
    bool            invalidate_steps(std::initializer_list<PrintStepEnum> il)
        { return m_state.invalidate_multiple(il.begin(), il.end(), this->cancel_mutex(), this->cancel_callback()); }
    bool            invalidate_all_steps()
        { return m_state.invalidate_all(this->cancel_mutex(), this->cancel_callback()); }

private:
    PrintState<PrintStepEnum, COUNT> m_state;
};

template<typename PrintType, typename PrintObjectStepEnum, const size_t COUNT>
class PrintObjectBaseWithState : public PrintObjectBase
{
public:
    Print*          print()         { return m_print; }
    const Print*    print() const   { return m_print; }

    bool            is_step_done(PrintObjectStepEnum step) const { return m_state.is_done(step); }

protected:
	PrintObjectBaseWithState(PrintType *print) : m_print(print) {}

    void            set_started(PrintObjectStepEnum step) { m_state.set_started(step, PrintObjectBase::cancel_mutex(m_print)); }
    void            set_done(PrintObjectStepEnum step) { m_state.set_done(step, PrintObjectBase::cancel_mutex(m_print)); }

    bool            invalidate_step(PrintObjectStepEnum step)
        { return m_state.invalidate(step, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
    template<typename StepTypeIterator>
    bool            invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
        { return m_state.invalidate_multiple(step_begin, step_end, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
    bool            invalidate_steps(std::initializer_list<PrintObjectStepEnum> il)
        { return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
    bool            invalidate_all_steps() { return m_state.invalidate_all(PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }

protected:
    friend typename PrintType;
    PrintType                               *m_print;

private:
    PrintState<PrintObjectStepEnum, COUNT>   m_state;
};

} // namespace Slic3r

#endif /* slic3r_PrintBase_hpp_ */
<commit_msg>Fix of friend declaration on a template parameter, according to C++11<commit_after>#ifndef slic3r_PrintBase_hpp_
#define slic3r_PrintBase_hpp_

#include "libslic3r.h"
#include <atomic>
#include <set>
#include <vector>
#include <string>
#include <functional>

#include "tbb/atomic.h"
// tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros.
#ifndef NOMINMAX
    #define NOMINMAX
#endif
#include "tbb/mutex.h"

#include "Model.hpp"
#include "PrintConfig.hpp"

namespace Slic3r {

class CanceledException : public std::exception {
public:
   const char* what() const throw() { return "Background processing has been canceled"; }
};

// To be instantiated over PrintStep or PrintObjectStep enums.
template <class StepType, size_t COUNT>
class PrintState
{
public:
    PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i].store(INVALID, std::memory_order_relaxed); }

    enum State {
        INVALID,
        STARTED,
        DONE,
    };

    // With full memory barrier.
    bool is_done(StepType step) const { return m_state[step] == DONE; }

    // Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being
    // modified by the UI thread.
    // This is necessary to block until the Print::apply_config() updates its state, which may
    // influence the processing step being entered.
    void set_started(StepType step, tbb::mutex &mtx) {
        mtx.lock();
        m_state[step].store(STARTED, std::memory_order_relaxed);
        mtx.unlock();
    }

    // Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being
    // modified by the UI thread.
    void set_done(StepType step, tbb::mutex &mtx) {
        mtx.lock();
        m_state[step].store(DONE, std::memory_order_relaxed);
        mtx.unlock();
    }

    // Make the step invalid.
    // The provided mutex should be locked at this point, guarding access to m_state.
    // In case the step has already been entered or finished, cancel the background
    // processing by calling the cancel callback.
    template<typename CancelationCallback>
    bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback cancel) {
        bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID;
        if (invalidated) {
#if 0
            if (mtx.state != mtx.HELD) {
                printf("Not held!\n");
            }
#endif
            // Raise the mutex, so that the following cancel() callback could cancel
            // the background processing.
            mtx.unlock();
            cancel();
            m_state[step] = INVALID;
            mtx.lock();
        }
        return invalidated;
    }

    template<typename CancelationCallback, typename StepTypeIterator>
    bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, tbb::mutex &mtx, CancelationCallback cancel) {
        bool invalidated = false;
        for (StepTypeIterator it = step_begin; ! invalidated && it != step_end; ++ it)
            invalidated = m_state[*it].load(std::memory_order_relaxed) != INVALID;
        if (invalidated) {
#if 0
            if (mtx.state != mtx.HELD) {
                printf("Not held!\n");
            }
#endif
            // Raise the mutex, so that the following cancel() callback could cancel
            // the background processing.
            mtx.unlock();
            cancel();
            for (StepTypeIterator it = step_begin; it != step_end; ++ it)
                m_state[*it] = INVALID;
            mtx.lock();
        }
        return invalidated;
    }

    // Make all steps invalid.
    // The provided mutex should be locked at this point, guarding access to m_state.
    // In case any step has already been entered or finished, cancel the background
    // processing by calling the cancel callback.
    template<typename CancelationCallback>
    bool invalidate_all(tbb::mutex &mtx, CancelationCallback cancel) {
        bool invalidated = false;
        for (size_t i = 0; i < COUNT; ++ i)
            if (m_state[i].load(std::memory_order_relaxed) != INVALID) {
                invalidated = true;
                break;
            }
        if (invalidated) {
            mtx.unlock();
            cancel();
            for (size_t i = 0; i < COUNT; ++ i)
                m_state[i].store(INVALID, std::memory_order_relaxed);
            mtx.lock();
        }
        return invalidated;
    }

private:
    std::atomic<State>          m_state[COUNT];
};

class PrintBase;

class PrintObjectBase
{
protected:
    virtual ~PrintObjectBase() {}
    // Declared here to allow access from PrintBase through friendship.
	static tbb::mutex&            cancel_mutex(PrintBase *print);
	static std::function<void()>  cancel_callback(PrintBase *print);
};

/**
 * @brief Printing involves slicing and export of device dependent instructions.
 *
 * Every technology has a potentially different set of requirements for
 * slicing, support structures and output print instructions. The pipeline
 * however remains roughly the same:
 *      slice -> convert to instructions -> send to printer
 *
 * The PrintBase class will abstract this flow for different technologies.
 *
 */
class PrintBase
{
public:
	PrintBase() { this->restart(); }
    inline virtual ~PrintBase() {}

    virtual PrinterTechnology technology() const noexcept = 0;

    // Reset the print status including the copy of the Model / ModelObject hierarchy.
    virtual void            clear() = 0;

    enum ApplyStatus {
        // No change after the Print::apply() call.
        APPLY_STATUS_UNCHANGED,
        // Some of the Print / PrintObject / PrintObjectInstance data was changed,
        // but no result was invalidated (only data influencing not yet calculated results were changed).
        APPLY_STATUS_CHANGED,
        // Some data was changed, which in turn invalidated already calculated steps.
        APPLY_STATUS_INVALIDATED,
    };
    virtual ApplyStatus     apply(const Model &model, const DynamicPrintConfig &config) = 0;

    virtual void            process() = 0;

    typedef std::function<void(int, const std::string&)>  status_callback_type;
    // Default status console print out in the form of percent => message.
    void                    set_status_default() { m_status_callback = nullptr; }
    // No status output or callback whatsoever, useful mostly for automatic tests.
    void                    set_status_silent() { m_status_callback = [](int, const std::string&){}; }
    // Register a custom status callback.
    void                    set_status_callback(status_callback_type cb) { m_status_callback = cb; }
    // Calls a registered callback to update the status, or print out the default message.
    void                    set_status(int percent, const std::string &message) {
        if (m_status_callback) m_status_callback(percent, message);
        else printf("%d => %s\n", percent, message.c_str());
    }

    typedef std::function<void()>  cancel_callback_type;
    // Various methods will call this callback to stop the background processing (the Print::process() call)
    // in case a successive change of the Print / PrintObject / PrintRegion instances changed
    // the state of the finished or running calculations.
    void                    set_cancel_callback(cancel_callback_type cancel_callback) { m_cancel_callback = cancel_callback; }
    // Has the calculation been canceled?
	enum CancelStatus {
		// No cancelation, background processing should run.
		NOT_CANCELED = 0,
		// Canceled by user from the user interface (user pressed the "Cancel" button or user closed the application).
		CANCELED_BY_USER = 1,
		// Canceled internally from Print::apply() through the Print/PrintObject::invalidate_step() or ::invalidate_all_steps().
		CANCELED_INTERNAL = 2
	};
    CancelStatus            cancel_status() const { return m_cancel_status; }
    // Has the calculation been canceled?
	bool                   canceled() const { return m_cancel_status != NOT_CANCELED; }
    // Cancel the running computation. Stop execution of all the background threads.
	void                   cancel() { m_cancel_status = CANCELED_BY_USER; }
	void                   cancel_internal() { m_cancel_status = CANCELED_INTERNAL; }
    // Cancel the running computation. Stop execution of all the background threads.
	void                   restart() { m_cancel_status = NOT_CANCELED; }

protected:
	friend class PrintObjectBase;

    tbb::mutex&            cancel_mutex() { return m_cancel_mutex; }
    std::function<void()>  cancel_callback() { return m_cancel_callback; }
	void				   call_cancell_callback() { m_cancel_callback(); }

    // If the background processing stop was requested, throw CanceledException.
    // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
    void                   throw_if_canceled() const { if (m_cancel_status) throw CanceledException(); }

private:
    tbb::atomic<CancelStatus>               m_cancel_status;
    // Callback to be evoked regularly to update state of the UI thread.
    status_callback_type                    m_status_callback;

    // Callback to be evoked to stop the background processing before a state is updated.
    cancel_callback_type                    m_cancel_callback = [](){};

    // Mutex used for synchronization of the worker thread with the UI thread:
    // The mutex will be used to guard the worker thread against entering a stage
    // while the data influencing the stage is modified.
    mutable tbb::mutex                      m_cancel_mutex;
};

template<typename PrintStepEnum, const size_t COUNT>
class PrintBaseWithState : public PrintBase
{
public:
    bool            is_step_done(PrintStepEnum step) const { return m_state.is_done(step); }

protected:
    void            set_started(PrintStepEnum step) { m_state.set_started(step, this->cancel_mutex()); throw_if_canceled(); }
    void            set_done(PrintStepEnum step) { m_state.set_done(step, this->cancel_mutex()); throw_if_canceled(); }
    bool            invalidate_step(PrintStepEnum step)
		{ return m_state.invalidate(step, this->cancel_mutex(), this->cancel_callback()); }
    template<typename StepTypeIterator>
    bool            invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
        { return m_state.invalidate_multiple(step_begin, step_end, this->cancel_mutex(), this->cancel_callback()); }
    bool            invalidate_steps(std::initializer_list<PrintStepEnum> il)
        { return m_state.invalidate_multiple(il.begin(), il.end(), this->cancel_mutex(), this->cancel_callback()); }
    bool            invalidate_all_steps()
        { return m_state.invalidate_all(this->cancel_mutex(), this->cancel_callback()); }

private:
    PrintState<PrintStepEnum, COUNT> m_state;
};

template<typename PrintType, typename PrintObjectStepEnum, const size_t COUNT>
class PrintObjectBaseWithState : public PrintObjectBase
{
public:
    Print*          print()         { return m_print; }
    const Print*    print() const   { return m_print; }

    bool            is_step_done(PrintObjectStepEnum step) const { return m_state.is_done(step); }

protected:
	PrintObjectBaseWithState(PrintType *print) : m_print(print) {}

    void            set_started(PrintObjectStepEnum step) { m_state.set_started(step, PrintObjectBase::cancel_mutex(m_print)); }
    void            set_done(PrintObjectStepEnum step) { m_state.set_done(step, PrintObjectBase::cancel_mutex(m_print)); }

    bool            invalidate_step(PrintObjectStepEnum step)
        { return m_state.invalidate(step, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
    template<typename StepTypeIterator>
    bool            invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
        { return m_state.invalidate_multiple(step_begin, step_end, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
    bool            invalidate_steps(std::initializer_list<PrintObjectStepEnum> il)
        { return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
    bool            invalidate_all_steps() { return m_state.invalidate_all(PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }

protected:
    friend PrintType;
    PrintType                               *m_print;

private:
    PrintState<PrintObjectStepEnum, COUNT>   m_state;
};

} // namespace Slic3r

#endif /* slic3r_PrintBase_hpp_ */
<|endoftext|>


<commit_before>#include <string>
#include <vector>
#include <unistd.h>
#include <gtest/gtest.h>
#include <scxcorelib/scxstrencodingconv.h>
#include "getusername.h"

class GetUserNameTest : public ::testing::Test {
protected:
	DWORD lpnSize;
	std::vector<WCHAR_T> lpBuffer;
	BOOL result;
	std::string userName;

	GetUserNameTest(): userName(std::string(getlogin())) {}

	void TestWithSize(DWORD size) {
		lpnSize = size;
		// allocate a WCHAR_T buffer to receive username
		lpBuffer.assign(lpnSize, '\0');
		result = GetUserName(&lpBuffer[0], &lpnSize);
	}

	void TestSuccess() {
		SCOPED_TRACE("");
		// returns 1 on success
		EXPECT_EQ(1, result);

		// sets lpnSize to length of username + null
		ASSERT_EQ(userName.size()+1, lpnSize);

		// copy UTF-16 bytes (excluding null) from lpBuffer to vector for conversion
		unsigned char *begin = reinterpret_cast<unsigned char *>(&lpBuffer[0]);
		// -1 to skip null; *2 because UTF-16 encodes two bytes per character
		unsigned char *end = begin + (lpnSize-1)*2;
		std::vector<unsigned char> input(begin, end);
		// convert to UTF-8 for comparison
		std::string output;
		SCXCoreLib::Utf16leToUtf8(input, output);

		EXPECT_EQ(userName, output);
	}

	void TestInvalidParameter() {
		SCOPED_TRACE("");

		// returns 0 on failure
		EXPECT_EQ(0, result);

		// sets errno to ERROR_INVALID_PARAMETER when lpBuffer is null
		// (which is the case for an empty vector)
		EXPECT_EQ(errno, ERROR_INVALID_PARAMETER);
	}

	void TestInsufficientBuffer() {
		SCOPED_TRACE("");

		// returns 0 on failure
		EXPECT_EQ(0, result);

		// sets errno to ERROR_INSUFFICIENT_BUFFER
		EXPECT_EQ(errno, ERROR_INSUFFICIENT_BUFFER);

		// sets lpnSize to length of username + null
		EXPECT_EQ(userName.size()+1, lpnSize);
	}
};

TEST_F(GetUserNameTest, BufferAsNullButNotBufferSize) {
	lpnSize = 1;
	result = GetUserName(NULL, &lpnSize);

	TestInvalidParameter();
	// does not reset lpnSize
	EXPECT_EQ(1, lpnSize);
}

TEST_F(GetUserNameTest, BufferSizeAsNullButNotBuffer) {
	lpBuffer.push_back('\0');
	result = GetUserName(&lpBuffer[0], NULL);

	TestInvalidParameter();
}

TEST_F(GetUserNameTest, BufferSizeAsZero) {
	TestWithSize(0);
	TestInvalidParameter();
	// does not reset lpnSize
	EXPECT_EQ(0, lpnSize);
}

TEST_F(GetUserNameTest, BufferSizeAsOne) {
	// theoretically this should never fail because any non-empty
	// username length will be >1 with trailing null
	TestWithSize(1);
	TestInsufficientBuffer();
}

TEST_F(GetUserNameTest, BufferSizeAsUserName) {
	// the buffer is too small because this does not include null
	TestWithSize(userName.size());
	TestInsufficientBuffer();
}

TEST_F(GetUserNameTest, BufferSizeAsUserNameMinusOne) {
	// the buffer is also too small
	TestWithSize(userName.size()-1);
	TestInsufficientBuffer();
}

TEST_F(GetUserNameTest, BufferSizeAsUserNamePlusOne) {
	// the buffer is exactly big enough will null
	TestWithSize(userName.size()+1);
	TestSuccess();
}

TEST_F(GetUserNameTest, BufferSizeAsLoginNameMax) {
	// LoginNameMax is big enough to hold any username
	TestWithSize(LOGIN_NAME_MAX);
	TestSuccess();
}
<commit_msg>Rename userName in test to expectedUsername<commit_after>#include <string>
#include <vector>
#include <unistd.h>
#include <gtest/gtest.h>
#include <scxcorelib/scxstrencodingconv.h>
#include "getusername.h"

class GetUserNameTest : public ::testing::Test {
protected:
	DWORD lpnSize;
	std::vector<WCHAR_T> lpBuffer;
	BOOL result;
	std::string expectedUsername;

	GetUserNameTest(): expectedUsername(std::string(getlogin())) {}

	void TestWithSize(DWORD size) {
		lpnSize = size;
		// allocate a WCHAR_T buffer to receive username
		lpBuffer.assign(lpnSize, '\0');
		result = GetUserName(&lpBuffer[0], &lpnSize);
	}

	void TestSuccess() {
		SCOPED_TRACE("");
		// returns 1 on success
		EXPECT_EQ(1, result);

		// sets lpnSize to length of username + null
		ASSERT_EQ(expectedUsername.size()+1, lpnSize);

		// copy UTF-16 bytes (excluding null) from lpBuffer to vector for conversion
		unsigned char *begin = reinterpret_cast<unsigned char *>(&lpBuffer[0]);
		// -1 to skip null; *2 because UTF-16 encodes two bytes per character
		unsigned char *end = begin + (lpnSize-1)*2;
		std::vector<unsigned char> input(begin, end);
		// convert to UTF-8 for comparison
		std::string output;
		SCXCoreLib::Utf16leToUtf8(input, output);

		EXPECT_EQ(expectedUsername, output);
	}

	void TestInvalidParameter() {
		SCOPED_TRACE("");

		// returns 0 on failure
		EXPECT_EQ(0, result);

		// sets errno to ERROR_INVALID_PARAMETER when lpBuffer is null
		// (which is the case for an empty vector)
		EXPECT_EQ(errno, ERROR_INVALID_PARAMETER);
	}

	void TestInsufficientBuffer() {
		SCOPED_TRACE("");

		// returns 0 on failure
		EXPECT_EQ(0, result);

		// sets errno to ERROR_INSUFFICIENT_BUFFER
		EXPECT_EQ(errno, ERROR_INSUFFICIENT_BUFFER);

		// sets lpnSize to length of username + null
		EXPECT_EQ(expectedUsername.size()+1, lpnSize);
	}
};

TEST_F(GetUserNameTest, BufferAsNullButNotBufferSize) {
	lpnSize = 1;
	result = GetUserName(NULL, &lpnSize);

	TestInvalidParameter();
	// does not reset lpnSize
	EXPECT_EQ(1, lpnSize);
}

TEST_F(GetUserNameTest, BufferSizeAsNullButNotBuffer) {
	lpBuffer.push_back('\0');
	result = GetUserName(&lpBuffer[0], NULL);

	TestInvalidParameter();
}

TEST_F(GetUserNameTest, BufferSizeAsZero) {
	TestWithSize(0);
	TestInvalidParameter();
	// does not reset lpnSize
	EXPECT_EQ(0, lpnSize);
}

TEST_F(GetUserNameTest, BufferSizeAsOne) {
	// theoretically this should never fail because any non-empty
	// username length will be >1 with trailing null
	TestWithSize(1);
	TestInsufficientBuffer();
}

TEST_F(GetUserNameTest, BufferSizeAsUserName) {
	// the buffer is too small because this does not include null
	TestWithSize(expectedUsername.size());
	TestInsufficientBuffer();
}

TEST_F(GetUserNameTest, BufferSizeAsUserNameMinusOne) {
	// the buffer is also too small
	TestWithSize(expectedUsername.size()-1);
	TestInsufficientBuffer();
}

TEST_F(GetUserNameTest, BufferSizeAsUserNamePlusOne) {
	// the buffer is exactly big enough will null
	TestWithSize(expectedUsername.size()+1);
	TestSuccess();
}

TEST_F(GetUserNameTest, BufferSizeAsLoginNameMax) {
	// LoginNameMax is big enough to hold any username
	TestWithSize(LOGIN_NAME_MAX);
	TestSuccess();
}
<|endoftext|>


<commit_before>//! @file test-getusername.cpp
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
//! @brief Unit tests for GetUserName

#include <string>
#include <vector>
#include <unistd.h>
#include <gtest/gtest.h>
#include <unicode/utypes.h>
#include <unicode/ucnv.h>
#include <unicode/ustring.h>
#include <unicode/uchar.h>
#include "getusername.h"

//! Test fixture for GetUserNameW
class GetUserNameTest : public ::testing::Test
{
protected:
    DWORD lpnSize;
    std::vector<WCHAR_T> lpBuffer;
    BOOL result;
    std::string expectedUsername;
    DWORD expectedSize;

    GetUserNameTest(): expectedUsername(std::string(getlogin())),
                       expectedSize(expectedUsername.length()+1)
    {
    }

    //! Invokes GetUserNameW with lpnSize and lpBuffer, saves result.
    //!
    //! @param size Assigns to lpnSize and allocates lpBuffer with
    //! size number of null characters.
    void TestWithSize(DWORD size)
    {
        lpnSize = size;
        // allocate a WCHAR_T buffer to receive username
        lpBuffer.assign(lpnSize, 0);
        result = GetUserNameW(&lpBuffer[0], &lpnSize);
    }

    //! Checks the effects of GetUserNameW for success.
    void TestSuccess()
    {
        SCOPED_TRACE("");

        //! Returns TRUE on success.
        EXPECT_EQ(TRUE, result);

        //! Sets lpnSize to number of WCHARs including null.
        ASSERT_EQ(expectedSize, lpnSize);

        // setup for conversion from UTF-16LE
        const char* begin = reinterpret_cast<char*>(&lpBuffer[0]);
        // multiply to get number of bytes
        icu::UnicodeString username16(begin, lpnSize*sizeof(char16_t), "UTF-16LE");
        // username16 length includes null and is number of characters
        ASSERT_EQ(expectedSize, username16.length());

        // convert (minus null) to UTF-8 for comparison
        std::string username(lpnSize-1, 0);
        ASSERT_EQ(expectedUsername.length(), username.length());
        username16.extract(0, username.length(),
                           reinterpret_cast<char*>(&username[0]), "UTF-8");

        //! Returned username (after conversion) is what was expected.
        EXPECT_EQ(expectedUsername, username);
    }

    //! Checks the effects of GetUserNameW on failure with invalid parameters.
    void TestInvalidParameter()
    {
        SCOPED_TRACE("");

        //! Returns FALSE on failure.
        EXPECT_EQ(FALSE, result);

        //! Sets errno to ERROR_INVALID_PARAMETER when lpBuffer is null
        //! (which is the case for an empty vector).
        EXPECT_EQ(errno, ERROR_INVALID_PARAMETER);
    }

    //! Checks the effects of GetUserNameW on failure with a buffer that is too small.
    void TestInsufficientBuffer()
    {
        SCOPED_TRACE("");

        //! Returns FALSE on failure.
        EXPECT_EQ(FALSE, result);

        //! Sets errno to ERROR_INSUFFICIENT_BUFFER.
        EXPECT_EQ(errno, ERROR_INSUFFICIENT_BUFFER);

        //! Sets lpnSize to length of username plus null.
        EXPECT_EQ(expectedSize, lpnSize);
    }
};

TEST_F(GetUserNameTest, BufferAsNullButNotBufferSize)
{
    lpnSize = 1;
    result = GetUserNameW(NULL, &lpnSize);

    TestInvalidParameter();
    // does not reset lpnSize
    EXPECT_EQ(1, lpnSize);
}

TEST_F(GetUserNameTest, BufferSizeAsNullButNotBuffer)
{
    lpBuffer.push_back('\0');
    result = GetUserNameW(&lpBuffer[0], NULL);

    TestInvalidParameter();
}

TEST_F(GetUserNameTest, BufferSizeAsZero)
{
    TestWithSize(0);
    TestInvalidParameter();
    // does not reset lpnSize
    EXPECT_EQ(0, lpnSize);
}

TEST_F(GetUserNameTest, BufferSizeAsOne)
{
    // theoretically this should never fail because any non-empty
    // username length will be >1 with trailing null
    TestWithSize(1);
    TestInsufficientBuffer();
}

TEST_F(GetUserNameTest, BufferSizeAsUsername)
{
    // the buffer is too small because it does not account for null
    TestWithSize(expectedUsername.length());
    TestInsufficientBuffer();
}

TEST_F(GetUserNameTest, BufferSizeAsUsernamePlusOne)
{
    // includes null and so should be sufficient
    TestWithSize(expectedUsername.length()+1);
    TestSuccess();
}

TEST_F(GetUserNameTest, BufferSizeAsExpectedSize)
{
    // expectedSize is the same as username.size()+1
    TestWithSize(expectedSize);
    TestSuccess();
}

TEST_F(GetUserNameTest, BufferSizeAsLoginNameMax)
{
    // LoginNameMax is big enough to hold any username, including null
    TestWithSize(LOGIN_NAME_MAX);
    TestSuccess();
}
<commit_msg>Use libicu properly in tests<commit_after>//! @file test-getusername.cpp
//! @author Andrew Schwartzmeyer <andschwa@microsoft.com>
//! @brief Unit tests for GetUserName

#include <string>
#include <vector>
#include <unistd.h>
#include <gtest/gtest.h>
#include <unicode/unistr.h>
#include "getusername.h"

//! Test fixture for GetUserNameW
class GetUserNameTest : public ::testing::Test
{
protected:
    DWORD lpnSize;
    std::vector<WCHAR_T> lpBuffer;
    BOOL result;
    std::string expectedUsername;
    DWORD expectedSize;

    GetUserNameTest(): expectedUsername(std::string(getlogin())),
                       expectedSize(expectedUsername.length()+1)
    {
    }

    //! Invokes GetUserNameW with lpnSize and lpBuffer, saves result.
    //!
    //! @param size Assigns to lpnSize and allocates lpBuffer with
    //! size number of null characters.
    void TestWithSize(DWORD size)
    {
        lpnSize = size;
        // allocate a WCHAR_T buffer to receive username
        lpBuffer.assign(lpnSize, 0);
        result = GetUserNameW(&lpBuffer[0], &lpnSize);
    }

    //! Checks the effects of GetUserNameW for success.
    void TestSuccess()
    {
        SCOPED_TRACE("");

        //! Returns TRUE on success.
        EXPECT_EQ(TRUE, result);

        //! Sets lpnSize to number of WCHARs including null.
        ASSERT_EQ(expectedSize, lpnSize);

        // Read lpBuffer into UnicodeString (without null)
        const char* begin = reinterpret_cast<char*>(&lpBuffer[0]);
        icu::UnicodeString username16(begin, (lpnSize-1)*sizeof(UChar), "UTF-16LE");
        ASSERT_EQ(expectedUsername.length(), username16.length());
        // Convert to UTF-8 for comparison
        std::string username;
        username16.toUTF8String(username);
        ASSERT_EQ(expectedUsername.length(), username.length());

        //! Returned username (after conversion) is what was expected.
        EXPECT_EQ(expectedUsername, username);
    }

    //! Checks the effects of GetUserNameW on failure with invalid parameters.
    void TestInvalidParameter()
    {
        SCOPED_TRACE("");

        //! Returns FALSE on failure.
        EXPECT_EQ(FALSE, result);

        //! Sets errno to ERROR_INVALID_PARAMETER when lpBuffer is null
        //! (which is the case for an empty vector).
        EXPECT_EQ(errno, ERROR_INVALID_PARAMETER);
    }

    //! Checks the effects of GetUserNameW on failure with a buffer that is too small.
    void TestInsufficientBuffer()
    {
        SCOPED_TRACE("");

        //! Returns FALSE on failure.
        EXPECT_EQ(FALSE, result);

        //! Sets errno to ERROR_INSUFFICIENT_BUFFER.
        EXPECT_EQ(errno, ERROR_INSUFFICIENT_BUFFER);

        //! Sets lpnSize to length of username plus null.
        EXPECT_EQ(expectedSize, lpnSize);
    }
};

TEST_F(GetUserNameTest, BufferAsNullButNotBufferSize)
{
    lpnSize = 1;
    result = GetUserNameW(NULL, &lpnSize);

    TestInvalidParameter();
    // does not reset lpnSize
    EXPECT_EQ(1, lpnSize);
}

TEST_F(GetUserNameTest, BufferSizeAsNullButNotBuffer)
{
    lpBuffer.push_back('\0');
    result = GetUserNameW(&lpBuffer[0], NULL);

    TestInvalidParameter();
}

TEST_F(GetUserNameTest, BufferSizeAsZero)
{
    TestWithSize(0);
    TestInvalidParameter();
    // does not reset lpnSize
    EXPECT_EQ(0, lpnSize);
}

TEST_F(GetUserNameTest, BufferSizeAsOne)
{
    // theoretically this should never fail because any non-empty
    // username length will be >1 with trailing null
    TestWithSize(1);
    TestInsufficientBuffer();
}

TEST_F(GetUserNameTest, BufferSizeAsUsername)
{
    // the buffer is too small because it does not account for null
    TestWithSize(expectedUsername.length());
    TestInsufficientBuffer();
}

TEST_F(GetUserNameTest, BufferSizeAsUsernamePlusOne)
{
    // includes null and so should be sufficient
    TestWithSize(expectedUsername.length()+1);
    TestSuccess();
}

TEST_F(GetUserNameTest, BufferSizeAsExpectedSize)
{
    // expectedSize is the same as username.size()+1
    TestWithSize(expectedSize);
    TestSuccess();
}

TEST_F(GetUserNameTest, BufferSizeAsLoginNameMax)
{
    // LoginNameMax is big enough to hold any username, including null
    TestWithSize(LOGIN_NAME_MAX);
    TestSuccess();
}
<|endoftext|>


<commit_before>#include <node.h>
#include <windows.h>
#include "appjs.h"
#include "windows/mainwindow.h"
#include "includes/cef.h"
#include "includes/util.h"
#include "includes/cef_handler.h"

#define MAX_LOADSTRING 100

extern CefRefPtr<ClientHandler> g_handler;

namespace appjs {

using namespace v8;

TCHAR szWindowClass[MAX_LOADSTRING];
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM MyRegisterClass(HINSTANCE hInstance);
char* URL;

MainWindow::MainWindow (char* url, Settings* settings) {
  Cef::Init();

  //TODO Take settings into account.
  int width = settings->getNumber("width",800);
  int height = settings->getNumber("height",600);
  double opacity = settings->getNumber("opacity",1);
  bool show_chrome = settings->getBoolean("showChrome",true);
  bool resizable = settings->getBoolean("resizable",true);
  bool show_resize_grip = settings->getBoolean("showResizeGrip",false);
  bool auto_resize = settings->getBoolean("autoResize",true);
  bool fullscreen = settings->getBoolean("fullscreen",false);

  HINSTANCE hInstance = GetModuleHandle(NULL);
  strcpy(szWindowClass,"MainWindowClass");
  URL = url;

  if(!MyRegisterClass(hInstance)){
	  //TODO send error to node
	  fprintf(stderr,"Error occurred: ");
	  fprintf(stderr,"%d\n",GetLastError());
  };

  // Perform application initialization
  HWND hWnd = CreateWindowEx(NULL, szWindowClass,"",
                      WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT,CW_USEDEFAULT,width,
                      height, NULL, NULL, hInstance, NULL);

  if (!hWnd) {
	  //TODO send error to node
	  fprintf(stderr,"Error occurred: ");
	  fprintf(stderr,"%d\n",GetLastError());
	  return;
  }

  UpdateWindow(hWnd);

  Cef::Run();
};

void MainWindow::show() {
  if (!g_handler.get() || !g_handler->GetBrowserHwnd())
    NODE_ERROR("Browser window not available or not ready.");

  ShowWindow(g_handler->GetMainHwnd(), SW_SHOW);
};

void MainWindow::hide() {
  if (!g_handler.get() || !g_handler->GetBrowserHwnd())
    NODE_ERROR("Browser window not available or not ready.");

  ShowWindow(g_handler->GetMainHwnd(), SW_HIDE);
};

void MainWindow::destroy() {
 if (!g_handler.get() || !g_handler->GetBrowserHwnd())
    NODE_ERROR("Browser window not available or not ready.");

  g_handler->GetBrowser()->CloseBrowser();
};

// Register Class
ATOM MyRegisterClass(HINSTANCE hInstance) {
  WNDCLASSEX wcex;

  wcex.cbSize = sizeof(WNDCLASSEX);

  wcex.style         = CS_HREDRAW | CS_VREDRAW;
  wcex.lpfnWndProc   = WndProc;
  wcex.cbClsExtra    = 0;
  wcex.cbWndExtra    = 0;
  wcex.hInstance     = hInstance;
  wcex.hIcon         = LoadIcon(NULL, IDI_APPLICATION );
  wcex.hIconSm       = LoadIcon(NULL, IDI_APPLICATION );
  wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  wcex.lpszMenuName  = NULL;
  wcex.lpszClassName = szWindowClass;

  return RegisterClassEx(&wcex);
}

// Processes messages for the main window.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
                         LPARAM lParam) {
  int wmId, wmEvent;
  PAINTSTRUCT ps;
  HDC hdc;
  // Callback for the main window
  switch (message) {
    case WM_CREATE: {
      // Create the single static handler class instance
      g_handler = new ClientHandler();
      g_handler->SetMainHwnd(hWnd);

      // Create the child windows used for navigation
	  RECT rect;
	  int x = 0;

	  GetClientRect(hWnd, &rect);

      // Initialize window info to the defaults for a child window
	  Cef::AddWebView(hWnd, rect,URL);

      return 0;
    }

    case WM_COMMAND: {
      CefRefPtr<CefBrowser> browser;
      if (g_handler.get())
        browser = g_handler->GetBrowser();

      wmId    = LOWORD(wParam);
      wmEvent = HIWORD(wParam);

      break;
    }

    case WM_PAINT:
      hdc = BeginPaint(hWnd, &ps);
      EndPaint(hWnd, &ps);
      return 0;

    case WM_SETFOCUS:
      if (g_handler.get() && g_handler->GetBrowserHwnd()) {
        // Pass focus to the browser window
        PostMessage(g_handler->GetBrowserHwnd(), WM_SETFOCUS, wParam, NULL);
      }
      return 0;

    case WM_SIZE:
      if (g_handler.get() && g_handler->GetBrowserHwnd()) {
        // Resize the browser window and address bar to match the new frame
        // window size
        RECT rect;
        GetClientRect(hWnd, &rect);

        HDWP hdwp = BeginDeferWindowPos(1);
        hdwp = DeferWindowPos(hdwp, g_handler->GetBrowserHwnd(), NULL,
          rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
          SWP_NOZORDER);
        EndDeferWindowPos(hdwp);
      }
      break;

    case WM_ERASEBKGND:
      if (g_handler.get() && g_handler->GetBrowserHwnd()) {
        // Dont erase the background if the browser window has been loaded
        // (this avoids flashing)
        return 0;
      }
      break;

    case WM_CLOSE:
      if (g_handler.get()) {
        CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
        if (browser.get()) {
          // Let the browser window know we are about to destroy it.
          browser->ParentWindowWillClose();
        }
      }
      break;

    case WM_DESTROY:
      // The frame window has exited
      PostQuitMessage(0);
      return 0;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
  }

} /* appjs */<commit_msg>Fix CRLF<commit_after>#include <node.h>
#include <windows.h>
#include "appjs.h"
#include "windows/mainwindow.h"
#include "includes/cef.h"
#include "includes/util.h"
#include "includes/cef_handler.h"

#define MAX_LOADSTRING 100

extern CefRefPtr<ClientHandler> g_handler;

namespace appjs {

using namespace v8;

TCHAR szWindowClass[MAX_LOADSTRING];
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM MyRegisterClass(HINSTANCE hInstance);
char* URL;

MainWindow::MainWindow (char* url, Settings* settings) {
  Cef::Init();

  //TODO Take settings into account.
  int width = settings->getNumber("width",800);
  int height = settings->getNumber("height",600);
  double opacity = settings->getNumber("opacity",1);
  bool show_chrome = settings->getBoolean("showChrome",true);
  bool resizable = settings->getBoolean("resizable",true);
  bool show_resize_grip = settings->getBoolean("showResizeGrip",false);
  bool auto_resize = settings->getBoolean("autoResize",true);
  bool fullscreen = settings->getBoolean("fullscreen",false);

  HINSTANCE hInstance = GetModuleHandle(NULL);
  strcpy(szWindowClass,"MainWindowClass");
  URL = url;

  if(!MyRegisterClass(hInstance)){
	  //TODO send error to node
	  fprintf(stderr,"Error occurred: ");
	  fprintf(stderr,"%d\n",GetLastError());
  };

  // Perform application initialization
  HWND hWnd = CreateWindowEx(NULL, szWindowClass,"",
                      WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT,CW_USEDEFAULT,width,
                      height, NULL, NULL, hInstance, NULL);

  if (!hWnd) {
	  //TODO send error to node
	  fprintf(stderr,"Error occurred: ");
	  fprintf(stderr,"%d\n",GetLastError());
	  return;
  }

  UpdateWindow(hWnd);

  Cef::Run();
};

void MainWindow::show() {
  if (!g_handler.get() || !g_handler->GetBrowserHwnd())
    NODE_ERROR("Browser window not available or not ready.");

  ShowWindow(g_handler->GetMainHwnd(), SW_SHOW);
};

void MainWindow::hide() {
  if (!g_handler.get() || !g_handler->GetBrowserHwnd())
    NODE_ERROR("Browser window not available or not ready.");

  ShowWindow(g_handler->GetMainHwnd(), SW_HIDE);
};

void MainWindow::destroy() {
 if (!g_handler.get() || !g_handler->GetBrowserHwnd())
    NODE_ERROR("Browser window not available or not ready.");

  g_handler->GetBrowser()->CloseBrowser();
};

// Register Class
ATOM MyRegisterClass(HINSTANCE hInstance) {
  WNDCLASSEX wcex;

  wcex.cbSize = sizeof(WNDCLASSEX);

  wcex.style         = CS_HREDRAW | CS_VREDRAW;
  wcex.lpfnWndProc   = WndProc;
  wcex.cbClsExtra    = 0;
  wcex.cbWndExtra    = 0;
  wcex.hInstance     = hInstance;
  wcex.hIcon         = LoadIcon(NULL, IDI_APPLICATION );
  wcex.hIconSm       = LoadIcon(NULL, IDI_APPLICATION );
  wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  wcex.lpszMenuName  = NULL;
  wcex.lpszClassName = szWindowClass;

  return RegisterClassEx(&wcex);
}

// Processes messages for the main window.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
                         LPARAM lParam) {
  int wmId, wmEvent;
  PAINTSTRUCT ps;
  HDC hdc;
  // Callback for the main window
  switch (message) {
    case WM_CREATE: {
      // Create the single static handler class instance
      g_handler = new ClientHandler();
      g_handler->SetMainHwnd(hWnd);

      // Create the child windows used for navigation
	  RECT rect;
	  int x = 0;

	  GetClientRect(hWnd, &rect);

      // Initialize window info to the defaults for a child window
	  Cef::AddWebView(hWnd, rect,URL);

      return 0;
    }

    case WM_COMMAND: {
      CefRefPtr<CefBrowser> browser;
      if (g_handler.get())
        browser = g_handler->GetBrowser();

      wmId    = LOWORD(wParam);
      wmEvent = HIWORD(wParam);

      break;
    }

    case WM_PAINT:
      hdc = BeginPaint(hWnd, &ps);
      EndPaint(hWnd, &ps);
      return 0;

    case WM_SETFOCUS:
      if (g_handler.get() && g_handler->GetBrowserHwnd()) {
        // Pass focus to the browser window
        PostMessage(g_handler->GetBrowserHwnd(), WM_SETFOCUS, wParam, NULL);
      }
      return 0;

    case WM_SIZE:
      if (g_handler.get() && g_handler->GetBrowserHwnd()) {
        // Resize the browser window and address bar to match the new frame
        // window size
        RECT rect;
        GetClientRect(hWnd, &rect);

        HDWP hdwp = BeginDeferWindowPos(1);
        hdwp = DeferWindowPos(hdwp, g_handler->GetBrowserHwnd(), NULL,
          rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
          SWP_NOZORDER);
        EndDeferWindowPos(hdwp);
      }
      break;

    case WM_ERASEBKGND:
      if (g_handler.get() && g_handler->GetBrowserHwnd()) {
        // Dont erase the background if the browser window has been loaded
        // (this avoids flashing)
        return 0;
      }
      break;

    case WM_CLOSE:
      if (g_handler.get()) {
        CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
        if (browser.get()) {
          // Let the browser window know we are about to destroy it.
          browser->ParentWindowWillClose();
        }
      }
      break;

    case WM_DESTROY:
      // The frame window has exited
      PostQuitMessage(0);
      return 0;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
  }

} /* appjs */
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/core/SkStrikeCache.h"

#include <cctype>

#include "include/core/SkGraphics.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkTraceMemoryDump.h"
#include "include/core/SkTypeface.h"
#include "include/private/SkMutex.h"
#include "include/private/SkTemplates.h"
#include "src/core/SkGlyphRunPainter.h"
#include "src/core/SkScalerCache.h"

bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental = false;

SkStrikeCache* SkStrikeCache::GlobalStrikeCache() {
#if !defined(SK_BUILD_FOR_IOS)
    if (gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental) {
        static thread_local auto* cache = new SkStrikeCache;
        return cache;
    }
#endif
    static auto* cache = new SkStrikeCache;
    return cache;
}

auto SkStrikeCache::findOrCreateStrike(const SkDescriptor& desc,
                                       const SkScalerContextEffects& effects,
                                       const SkTypeface& typeface) -> sk_sp<Strike> {
    SkAutoSpinlock ac(fLock);
    sk_sp<Strike> strike = this->internalFindStrikeOrNull(desc);
    if (strike == nullptr) {
        auto scaler = typeface.createScalerContext(effects, &desc);
        strike = this->internalCreateStrike(desc, std::move(scaler));
    }
    this->internalPurge();
    return strike;
}

SkScopedStrikeForGPU SkStrikeCache::findOrCreateScopedStrike(const SkDescriptor& desc,
                                                             const SkScalerContextEffects& effects,
                                                             const SkTypeface& typeface) {
    return SkScopedStrikeForGPU{this->findOrCreateStrike(desc, effects, typeface).release()};
}

void SkStrikeCache::PurgeAll() {
    GlobalStrikeCache()->purgeAll();
}

void SkStrikeCache::Dump() {
    SkDebugf("GlyphCache [     used    budget ]\n");
    SkDebugf("    bytes  [ %8zu  %8zu ]\n",
             SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
    SkDebugf("    count  [ %8zu  %8zu ]\n",
             SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());

    int counter = 0;

    auto visitor = [&counter](const Strike& strike) {
        const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();

        SkDebugf("index %d\n", counter);
        SkDebugf("%s", rec.dump().c_str());
        counter += 1;
    };

    GlobalStrikeCache()->forEachStrike(visitor);
}

namespace {
    const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache";
}  // namespace

void SkStrikeCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
    dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
    dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
                           SkGraphics::GetFontCacheLimit());
    dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
                           SkGraphics::GetFontCacheCountUsed());
    dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
                           SkGraphics::GetFontCacheCountLimit());

    if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
        dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
        return;
    }

    auto visitor = [&dump](const Strike& strike) {
        const SkTypeface* face = strike.fScalerCache.getScalerContext()->getTypeface();
        const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();

        SkString fontName;
        face->getFamilyName(&fontName);
        // Replace all special characters with '_'.
        for (size_t index = 0; index < fontName.size(); ++index) {
            if (!std::isalnum(fontName[index])) {
                fontName[index] = '_';
            }
        }

        SkString dumpName = SkStringPrintf(
                "%s/%s_%d/%p", gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &strike);

        dump->dumpNumericValue(dumpName.c_str(),
                               "size", "bytes", strike.fMemoryUsed);
        dump->dumpNumericValue(dumpName.c_str(),
                               "glyph_count", "objects",
                               strike.fScalerCache.countCachedGlyphs());
        dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
    };

    GlobalStrikeCache()->forEachStrike(visitor);
}

sk_sp<SkStrike> SkStrikeCache::findStrike(const SkDescriptor& desc) {
    SkAutoSpinlock ac(fLock);
    sk_sp<SkStrike> result = this->internalFindStrikeOrNull(desc);
    this->internalPurge();
    return result;
}

auto SkStrikeCache::internalFindStrikeOrNull(const SkDescriptor& desc) -> sk_sp<Strike> {

    // Check head because it is likely the strike we are looking for.
    if (fHead != nullptr && fHead->getDescriptor() == desc) { return sk_ref_sp(fHead); }

    // Do the heavy search looking for the strike.
    sk_sp<Strike>* strikeHandle = fStrikeLookup.find(desc);
    if (strikeHandle == nullptr) { return nullptr; }
    Strike* strikePtr = strikeHandle->get();
    SkASSERT(strikePtr != nullptr);
    if (fHead != strikePtr) {
        // Make most recently used
        strikePtr->fPrev->fNext = strikePtr->fNext;
        if (strikePtr->fNext != nullptr) {
            strikePtr->fNext->fPrev = strikePtr->fPrev;
        } else {
            fTail = strikePtr->fPrev;
        }
        fHead->fPrev = strikePtr;
        strikePtr->fNext = fHead;
        strikePtr->fPrev = nullptr;
        fHead = strikePtr;
    }
    return sk_ref_sp(strikePtr);
}

sk_sp<SkStrike> SkStrikeCache::createStrike(
        const SkDescriptor& desc,
        std::unique_ptr<SkScalerContext> scaler,
        SkFontMetrics* maybeMetrics,
        std::unique_ptr<SkStrikePinner> pinner) {
    SkAutoSpinlock ac(fLock);
    return this->internalCreateStrike(desc, std::move(scaler), maybeMetrics, std::move(pinner));
}

auto SkStrikeCache::internalCreateStrike(
        const SkDescriptor& desc,
        std::unique_ptr<SkScalerContext> scaler,
        SkFontMetrics* maybeMetrics,
        std::unique_ptr<SkStrikePinner> pinner) -> sk_sp<Strike> {
    auto strike =
            sk_make_sp<Strike>(this, desc, std::move(scaler), maybeMetrics, std::move(pinner));
    this->internalAttachToHead(strike);
    return strike;
}

void SkStrikeCache::purgeAll() {
    SkAutoSpinlock ac(fLock);
    this->internalPurge(fTotalMemoryUsed);
}

size_t SkStrikeCache::getTotalMemoryUsed() const {
    SkAutoSpinlock ac(fLock);
    return fTotalMemoryUsed;
}

int SkStrikeCache::getCacheCountUsed() const {
    SkAutoSpinlock ac(fLock);
    return fCacheCount;
}

int SkStrikeCache::getCacheCountLimit() const {
    SkAutoSpinlock ac(fLock);
    return fCacheCountLimit;
}

size_t SkStrikeCache::setCacheSizeLimit(size_t newLimit) {
    SkAutoSpinlock ac(fLock);

    size_t prevLimit = fCacheSizeLimit;
    fCacheSizeLimit = newLimit;
    this->internalPurge();
    return prevLimit;
}

size_t  SkStrikeCache::getCacheSizeLimit() const {
    SkAutoSpinlock ac(fLock);
    return fCacheSizeLimit;
}

int SkStrikeCache::setCacheCountLimit(int newCount) {
    if (newCount < 0) {
        newCount = 0;
    }

    SkAutoSpinlock ac(fLock);

    int prevCount = fCacheCountLimit;
    fCacheCountLimit = newCount;
    this->internalPurge();
    return prevCount;
}

int SkStrikeCache::getCachePointSizeLimit() const {
    SkAutoSpinlock ac(fLock);
    return fPointSizeLimit;
}

int SkStrikeCache::setCachePointSizeLimit(int newLimit) {
    if (newLimit < 0) {
        newLimit = 0;
    }

    SkAutoSpinlock ac(fLock);

    int prevLimit = fPointSizeLimit;
    fPointSizeLimit = newLimit;
    return prevLimit;
}

void SkStrikeCache::forEachStrike(std::function<void(const Strike&)> visitor) const {
    SkAutoSpinlock ac(fLock);

    this->validate();

    for (Strike* strike = fHead; strike != nullptr; strike = strike->fNext) {
        visitor(*strike);
    }
}

size_t SkStrikeCache::internalPurge(size_t minBytesNeeded) {
    this->validate();

    size_t bytesNeeded = 0;
    if (fTotalMemoryUsed > fCacheSizeLimit) {
        bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
    }
    bytesNeeded = std::max(bytesNeeded, minBytesNeeded);
    if (bytesNeeded) {
        // no small purges!
        bytesNeeded = std::max(bytesNeeded, fTotalMemoryUsed >> 2);
    }

    int countNeeded = 0;
    if (fCacheCount > fCacheCountLimit) {
        countNeeded = fCacheCount - fCacheCountLimit;
        // no small purges!
        countNeeded = std::max(countNeeded, fCacheCount >> 2);
    }

    // early exit
    if (!countNeeded && !bytesNeeded) {
        return 0;
    }

    size_t  bytesFreed = 0;
    int     countFreed = 0;

    // Start at the tail and proceed backwards deleting; the list is in LRU
    // order, with unimportant entries at the tail.
    Strike* strike = fTail;
    while (strike != nullptr && (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
        Strike* prev = strike->fPrev;

        // Only delete if the strike is not pinned.
        if (strike->fPinner == nullptr || strike->fPinner->canDelete()) {
            bytesFreed += strike->fMemoryUsed;
            countFreed += 1;
            this->internalRemoveStrike(strike);
        }
        strike = prev;
    }

    this->validate();

#ifdef SPEW_PURGE_STATUS
    if (countFreed) {
        SkDebugf("purging %dK from font cache [%d entries]\n",
                 (int)(bytesFreed >> 10), countFreed);
    }
#endif

    return bytesFreed;
}

void SkStrikeCache::internalAttachToHead(sk_sp<Strike> strike) {
    SkASSERT(fStrikeLookup.find(strike->getDescriptor()) == nullptr);
    Strike* strikePtr = strike.get();
    fStrikeLookup.set(std::move(strike));
    SkASSERT(nullptr == strikePtr->fPrev && nullptr == strikePtr->fNext);

    fCacheCount += 1;
    fTotalMemoryUsed += strikePtr->fMemoryUsed;

    if (fHead != nullptr) {
        fHead->fPrev = strikePtr;
        strikePtr->fNext = fHead;
    }

    if (fTail == nullptr) {
        fTail = strikePtr;
    }

    fHead = strikePtr; // Transfer ownership of strike to the cache list.
}

void SkStrikeCache::internalRemoveStrike(Strike* strike) {
    SkASSERT(fCacheCount > 0);
    fCacheCount -= 1;
    fTotalMemoryUsed -= strike->fMemoryUsed;

    if (strike->fPrev) {
        strike->fPrev->fNext = strike->fNext;
    } else {
        fHead = strike->fNext;
    }
    if (strike->fNext) {
        strike->fNext->fPrev = strike->fPrev;
    } else {
        fTail = strike->fPrev;
    }

    strike->fPrev = strike->fNext = nullptr;
    strike->fRemoved = true;
    fStrikeLookup.remove(strike->getDescriptor());
}

void SkStrikeCache::validate() const {
#ifdef SK_DEBUG
    size_t computedBytes = 0;
    int computedCount = 0;

    const Strike* strike = fHead;
    while (strike != nullptr) {
        computedBytes += strike->fMemoryUsed;
        computedCount += 1;
        SkASSERT(fStrikeLookup.findOrNull(strike->getDescriptor()) != nullptr);
        strike = strike->fNext;
    }

    if (fCacheCount != computedCount) {
        SkDebugf("fCacheCount: %d, computedCount: %d", fCacheCount, computedCount);
        SK_ABORT("fCacheCount != computedCount");
    }
    if (fTotalMemoryUsed != computedBytes) {
        SkDebugf("fTotalMemoryUsed: %d, computedBytes: %d", fTotalMemoryUsed, computedBytes);
        SK_ABORT("fTotalMemoryUsed == computedBytes");
    }
#endif
}

void SkStrikeCache::Strike::updateDelta(size_t increase) {
    if (increase != 0) {
        SkAutoSpinlock lock{fStrikeCache->fLock};
        fMemoryUsed += increase;
        if (!fRemoved) {
            fStrikeCache->fTotalMemoryUsed += increase;
        }
    }
}
<commit_msg>SkStrikeCache: only validate post-purge<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/core/SkStrikeCache.h"

#include <cctype>

#include "include/core/SkGraphics.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkTraceMemoryDump.h"
#include "include/core/SkTypeface.h"
#include "include/private/SkMutex.h"
#include "include/private/SkTemplates.h"
#include "src/core/SkGlyphRunPainter.h"
#include "src/core/SkScalerCache.h"

bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental = false;

SkStrikeCache* SkStrikeCache::GlobalStrikeCache() {
#if !defined(SK_BUILD_FOR_IOS)
    if (gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental) {
        static thread_local auto* cache = new SkStrikeCache;
        return cache;
    }
#endif
    static auto* cache = new SkStrikeCache;
    return cache;
}

auto SkStrikeCache::findOrCreateStrike(const SkDescriptor& desc,
                                       const SkScalerContextEffects& effects,
                                       const SkTypeface& typeface) -> sk_sp<Strike> {
    SkAutoSpinlock ac(fLock);
    sk_sp<Strike> strike = this->internalFindStrikeOrNull(desc);
    if (strike == nullptr) {
        auto scaler = typeface.createScalerContext(effects, &desc);
        strike = this->internalCreateStrike(desc, std::move(scaler));
    }
    this->internalPurge();
    return strike;
}

SkScopedStrikeForGPU SkStrikeCache::findOrCreateScopedStrike(const SkDescriptor& desc,
                                                             const SkScalerContextEffects& effects,
                                                             const SkTypeface& typeface) {
    return SkScopedStrikeForGPU{this->findOrCreateStrike(desc, effects, typeface).release()};
}

void SkStrikeCache::PurgeAll() {
    GlobalStrikeCache()->purgeAll();
}

void SkStrikeCache::Dump() {
    SkDebugf("GlyphCache [     used    budget ]\n");
    SkDebugf("    bytes  [ %8zu  %8zu ]\n",
             SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
    SkDebugf("    count  [ %8zu  %8zu ]\n",
             SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());

    int counter = 0;

    auto visitor = [&counter](const Strike& strike) {
        const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();

        SkDebugf("index %d\n", counter);
        SkDebugf("%s", rec.dump().c_str());
        counter += 1;
    };

    GlobalStrikeCache()->forEachStrike(visitor);
}

namespace {
    const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache";
}  // namespace

void SkStrikeCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
    dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
    dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
                           SkGraphics::GetFontCacheLimit());
    dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
                           SkGraphics::GetFontCacheCountUsed());
    dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
                           SkGraphics::GetFontCacheCountLimit());

    if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
        dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
        return;
    }

    auto visitor = [&dump](const Strike& strike) {
        const SkTypeface* face = strike.fScalerCache.getScalerContext()->getTypeface();
        const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();

        SkString fontName;
        face->getFamilyName(&fontName);
        // Replace all special characters with '_'.
        for (size_t index = 0; index < fontName.size(); ++index) {
            if (!std::isalnum(fontName[index])) {
                fontName[index] = '_';
            }
        }

        SkString dumpName = SkStringPrintf(
                "%s/%s_%d/%p", gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &strike);

        dump->dumpNumericValue(dumpName.c_str(),
                               "size", "bytes", strike.fMemoryUsed);
        dump->dumpNumericValue(dumpName.c_str(),
                               "glyph_count", "objects",
                               strike.fScalerCache.countCachedGlyphs());
        dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
    };

    GlobalStrikeCache()->forEachStrike(visitor);
}

sk_sp<SkStrike> SkStrikeCache::findStrike(const SkDescriptor& desc) {
    SkAutoSpinlock ac(fLock);
    sk_sp<SkStrike> result = this->internalFindStrikeOrNull(desc);
    this->internalPurge();
    return result;
}

auto SkStrikeCache::internalFindStrikeOrNull(const SkDescriptor& desc) -> sk_sp<Strike> {

    // Check head because it is likely the strike we are looking for.
    if (fHead != nullptr && fHead->getDescriptor() == desc) { return sk_ref_sp(fHead); }

    // Do the heavy search looking for the strike.
    sk_sp<Strike>* strikeHandle = fStrikeLookup.find(desc);
    if (strikeHandle == nullptr) { return nullptr; }
    Strike* strikePtr = strikeHandle->get();
    SkASSERT(strikePtr != nullptr);
    if (fHead != strikePtr) {
        // Make most recently used
        strikePtr->fPrev->fNext = strikePtr->fNext;
        if (strikePtr->fNext != nullptr) {
            strikePtr->fNext->fPrev = strikePtr->fPrev;
        } else {
            fTail = strikePtr->fPrev;
        }
        fHead->fPrev = strikePtr;
        strikePtr->fNext = fHead;
        strikePtr->fPrev = nullptr;
        fHead = strikePtr;
    }
    return sk_ref_sp(strikePtr);
}

sk_sp<SkStrike> SkStrikeCache::createStrike(
        const SkDescriptor& desc,
        std::unique_ptr<SkScalerContext> scaler,
        SkFontMetrics* maybeMetrics,
        std::unique_ptr<SkStrikePinner> pinner) {
    SkAutoSpinlock ac(fLock);
    return this->internalCreateStrike(desc, std::move(scaler), maybeMetrics, std::move(pinner));
}

auto SkStrikeCache::internalCreateStrike(
        const SkDescriptor& desc,
        std::unique_ptr<SkScalerContext> scaler,
        SkFontMetrics* maybeMetrics,
        std::unique_ptr<SkStrikePinner> pinner) -> sk_sp<Strike> {
    auto strike =
            sk_make_sp<Strike>(this, desc, std::move(scaler), maybeMetrics, std::move(pinner));
    this->internalAttachToHead(strike);
    return strike;
}

void SkStrikeCache::purgeAll() {
    SkAutoSpinlock ac(fLock);
    this->internalPurge(fTotalMemoryUsed);
}

size_t SkStrikeCache::getTotalMemoryUsed() const {
    SkAutoSpinlock ac(fLock);
    return fTotalMemoryUsed;
}

int SkStrikeCache::getCacheCountUsed() const {
    SkAutoSpinlock ac(fLock);
    return fCacheCount;
}

int SkStrikeCache::getCacheCountLimit() const {
    SkAutoSpinlock ac(fLock);
    return fCacheCountLimit;
}

size_t SkStrikeCache::setCacheSizeLimit(size_t newLimit) {
    SkAutoSpinlock ac(fLock);

    size_t prevLimit = fCacheSizeLimit;
    fCacheSizeLimit = newLimit;
    this->internalPurge();
    return prevLimit;
}

size_t  SkStrikeCache::getCacheSizeLimit() const {
    SkAutoSpinlock ac(fLock);
    return fCacheSizeLimit;
}

int SkStrikeCache::setCacheCountLimit(int newCount) {
    if (newCount < 0) {
        newCount = 0;
    }

    SkAutoSpinlock ac(fLock);

    int prevCount = fCacheCountLimit;
    fCacheCountLimit = newCount;
    this->internalPurge();
    return prevCount;
}

int SkStrikeCache::getCachePointSizeLimit() const {
    SkAutoSpinlock ac(fLock);
    return fPointSizeLimit;
}

int SkStrikeCache::setCachePointSizeLimit(int newLimit) {
    if (newLimit < 0) {
        newLimit = 0;
    }

    SkAutoSpinlock ac(fLock);

    int prevLimit = fPointSizeLimit;
    fPointSizeLimit = newLimit;
    return prevLimit;
}

void SkStrikeCache::forEachStrike(std::function<void(const Strike&)> visitor) const {
    SkAutoSpinlock ac(fLock);

    this->validate();

    for (Strike* strike = fHead; strike != nullptr; strike = strike->fNext) {
        visitor(*strike);
    }
}

size_t SkStrikeCache::internalPurge(size_t minBytesNeeded) {
    size_t bytesNeeded = 0;
    if (fTotalMemoryUsed > fCacheSizeLimit) {
        bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
    }
    bytesNeeded = std::max(bytesNeeded, minBytesNeeded);
    if (bytesNeeded) {
        // no small purges!
        bytesNeeded = std::max(bytesNeeded, fTotalMemoryUsed >> 2);
    }

    int countNeeded = 0;
    if (fCacheCount > fCacheCountLimit) {
        countNeeded = fCacheCount - fCacheCountLimit;
        // no small purges!
        countNeeded = std::max(countNeeded, fCacheCount >> 2);
    }

    // early exit
    if (!countNeeded && !bytesNeeded) {
        return 0;
    }

    size_t  bytesFreed = 0;
    int     countFreed = 0;

    // Start at the tail and proceed backwards deleting; the list is in LRU
    // order, with unimportant entries at the tail.
    Strike* strike = fTail;
    while (strike != nullptr && (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
        Strike* prev = strike->fPrev;

        // Only delete if the strike is not pinned.
        if (strike->fPinner == nullptr || strike->fPinner->canDelete()) {
            bytesFreed += strike->fMemoryUsed;
            countFreed += 1;
            this->internalRemoveStrike(strike);
        }
        strike = prev;
    }

    this->validate();

#ifdef SPEW_PURGE_STATUS
    if (countFreed) {
        SkDebugf("purging %dK from font cache [%d entries]\n",
                 (int)(bytesFreed >> 10), countFreed);
    }
#endif

    return bytesFreed;
}

void SkStrikeCache::internalAttachToHead(sk_sp<Strike> strike) {
    SkASSERT(fStrikeLookup.find(strike->getDescriptor()) == nullptr);
    Strike* strikePtr = strike.get();
    fStrikeLookup.set(std::move(strike));
    SkASSERT(nullptr == strikePtr->fPrev && nullptr == strikePtr->fNext);

    fCacheCount += 1;
    fTotalMemoryUsed += strikePtr->fMemoryUsed;

    if (fHead != nullptr) {
        fHead->fPrev = strikePtr;
        strikePtr->fNext = fHead;
    }

    if (fTail == nullptr) {
        fTail = strikePtr;
    }

    fHead = strikePtr; // Transfer ownership of strike to the cache list.
}

void SkStrikeCache::internalRemoveStrike(Strike* strike) {
    SkASSERT(fCacheCount > 0);
    fCacheCount -= 1;
    fTotalMemoryUsed -= strike->fMemoryUsed;

    if (strike->fPrev) {
        strike->fPrev->fNext = strike->fNext;
    } else {
        fHead = strike->fNext;
    }
    if (strike->fNext) {
        strike->fNext->fPrev = strike->fPrev;
    } else {
        fTail = strike->fPrev;
    }

    strike->fPrev = strike->fNext = nullptr;
    strike->fRemoved = true;
    fStrikeLookup.remove(strike->getDescriptor());
}

void SkStrikeCache::validate() const {
#ifdef SK_DEBUG
    size_t computedBytes = 0;
    int computedCount = 0;

    const Strike* strike = fHead;
    while (strike != nullptr) {
        computedBytes += strike->fMemoryUsed;
        computedCount += 1;
        SkASSERT(fStrikeLookup.findOrNull(strike->getDescriptor()) != nullptr);
        strike = strike->fNext;
    }

    if (fCacheCount != computedCount) {
        SkDebugf("fCacheCount: %d, computedCount: %d", fCacheCount, computedCount);
        SK_ABORT("fCacheCount != computedCount");
    }
    if (fTotalMemoryUsed != computedBytes) {
        SkDebugf("fTotalMemoryUsed: %d, computedBytes: %d", fTotalMemoryUsed, computedBytes);
        SK_ABORT("fTotalMemoryUsed == computedBytes");
    }
#endif
}

void SkStrikeCache::Strike::updateDelta(size_t increase) {
    if (increase != 0) {
        SkAutoSpinlock lock{fStrikeCache->fLock};
        fMemoryUsed += increase;
        if (!fRemoved) {
            fStrikeCache->fTotalMemoryUsed += increase;
        }
    }
}
<|endoftext|>