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

#include <sstream>

#include "copasi.h"

#include "CMathDependencyGraph.h"
#include "CMathDependencyNode.h"
#include "CMathObject.h"

#include "report/CCopasiContainer.h"
#include "report/CCopasiObjectName.h"
#include "utilities/CCopasiMessage.h"

CMathDependencyGraph::CMathDependencyGraph():
  mObjects2Nodes(),
  mObject2Index()
{}

CMathDependencyGraph::~CMathDependencyGraph()
{
  clear();
}

void CMathDependencyGraph::clear()
{
  iterator it = mObjects2Nodes.begin();
  iterator end = mObjects2Nodes.end();

  for (; it != end; ++it)
    {
      delete(it->second);
    }

  mObjects2Nodes.clear();
}

CMathDependencyGraph::iterator CMathDependencyGraph::addObject(const CObjectInterface * pObject)
{
  iterator found = mObjects2Nodes.find(pObject);

  if (found == mObjects2Nodes.end())
    {
      found = mObjects2Nodes.insert(std::make_pair(pObject, new CMathDependencyNode(pObject))).first;

      const CObjectInterface::ObjectSet & Prerequisites = pObject->getPrerequisites();
      CObjectInterface::ObjectSet::const_iterator it = Prerequisites.begin();
      CObjectInterface::ObjectSet::const_iterator end = Prerequisites.end();

      for (; it != end; ++it)
        {
          iterator foundPrerequisite = mObjects2Nodes.find(*it);

          if (foundPrerequisite == mObjects2Nodes.end())
            {
              foundPrerequisite = addObject(*it);
            }

          foundPrerequisite->second->addDependent(found->second);
          found->second->addPrerequisite(foundPrerequisite->second);
        }
    }

  return found;
}

bool CMathDependencyGraph::getUpdateSequence(const CMath::SimulationContextFlag & context,
    const CObjectInterface::ObjectSet & changedObjects,
    const CObjectInterface::ObjectSet & requestedObjects,
    CObjectInterface::UpdateSequence & updateSequence)
{

  bool success = true;

  iterator found;
  iterator notFound = mObjects2Nodes.end();

  updateSequence.clear();

  CObjectInterface::ObjectSet::const_iterator it = changedObjects.begin();
  CObjectInterface::ObjectSet::const_iterator end = changedObjects.end();

  const_iterator itCheck = mObjects2Nodes.begin();
  const_iterator endCheck = mObjects2Nodes.end();

  // Mark all nodes which are changed or need to be calculated
  for (; it != end && success; ++it)
    {
      found = mObjects2Nodes.find(*it);

      if (found != notFound)
        {
          success &= found->second->updateDependentState(context, changedObjects);
          continue;
        }

      success = false;
    }

  if (!success) goto finish;

  it = requestedObjects.begin();
  end = requestedObjects.end();

  // Mark all nodes which are requested and its prerequisites.
  for (; it != end && success; ++it)
    {
      found = mObjects2Nodes.find(*it);

      if (found != notFound)
        {
          found->second->setRequested(true);
          success &= found->second->updatePrerequisiteState(context, changedObjects);
          continue;
        }

      success = false;
    }

#ifdef COPASI_DEBUG_TRACE
  std::ofstream GetUpdateSequence("GetUpdateSequence.dot");
  exportDOTFormat(GetUpdateSequence, "GetUpdateSequence");
  GetUpdateSequence.close();
#endif //COPASI_DEBUG_TRACE

  if (!success) goto finish;

  it = requestedObjects.begin();
  end = requestedObjects.end();

  for (; it != end && success; ++it)
    {
      found = mObjects2Nodes.find(*it);

      if (found != notFound)
        {
          success &= found->second->buildUpdateSequence(context, updateSequence);
          continue;
        }

      success = false;
    }

  if (!success) goto finish;

  for (; itCheck != endCheck; ++itCheck)
    {
      // Reset the dependency nodes for the next call.
      itCheck->second->setChanged(false);
      itCheck->second->setRequested(false);
    }

finish:

  if (!success)
    {
      updateSequence.clear();

      CCopasiMessage(CCopasiMessage::ERROR, MCMathModel + 3, (*it)->getCN().c_str());
    }

#ifdef XXXX
  CObjectInterface::UpdateSequence::const_iterator itSeq = updateSequence.begin();
  CObjectInterface::UpdateSequence::const_iterator endSeq = updateSequence.end();

  std::cout << std::endl <<  "Start" << std::endl;

  for (; itSeq != endSeq; ++itSeq)
    {
      if (dynamic_cast< const CMathObject * >(*itSeq))
        {
          std::cout << *static_cast< const CMathObject * >(*itSeq);
        }
      else
        {
          std::cout << (*itSeq)->getCN() << std::endl;
        }
    }

  std::cout << "End" << std::endl;
#endif //

  return success;
}

void CMathDependencyGraph::exportDOTFormat(std::ostream & os, const std::string & name) const
{
  os << "digraph " << name << " {" << std::endl;
  os << "rankdir=LR;" << std::endl;

  mObject2Index.clear();

  const_iterator it = mObjects2Nodes.begin();
  const_iterator end = mObjects2Nodes.end();

  for (; it != end; ++it)
    {
      const CObjectInterface * pObject = it->second->getObject();

      const std::vector< CMathDependencyNode * > & Dependents = it->second->getDependents();
      std::vector< CMathDependencyNode * >::const_iterator itDep = Dependents.begin();
      std::vector< CMathDependencyNode * >::const_iterator endDep = Dependents.end();

      for (; itDep != endDep; ++itDep)
        {
          os << "\"";
          os << getDOTNodeId(pObject);
          os << ((it->second->isChanged()) ? "\\nC" : "\\no");
          os << ((it->second->isRequested()) ? "R" : "o");
          os << "\"";
          os << " -> ";
          os << "\"";
          os << getDOTNodeId((*itDep)->getObject());
          os << (((*itDep)->isChanged()) ? "\\nC" : "\\no");
          os << (((*itDep)->isRequested()) ? "R" : "o");
          os << "\"";
          os << ";" << std::endl;
        }
    }

  os << "}" << std::endl;
}

// static
std::string CMathDependencyGraph::getDOTNodeId(const CObjectInterface * pObject) const
{
  const CCopasiObject * pDataObject = dynamic_cast< const CCopasiObject * >(pObject);
  const CMathObject * pMathObject = dynamic_cast< const CMathObject * >(pObject);

  if (pDataObject == NULL && pMathObject == NULL)
    {
      return "Invalid Node";
    }

  if (pDataObject == NULL)
    {
      pDataObject = pMathObject->getDataObject();
    }

  if (pDataObject == NULL)
    {
      std::ostringstream os;

      switch (pMathObject->getValueType())
        {
          case CMath::ValueTypeUndefined:
            os << "ValueTypeUndefined";
            break;

          case CMath::Value:
            os << "Value";
            break;

          case CMath::Rate:
            os << "ValueRate";
            break;

          case CMath::ParticleFlux:
            os << "ParticleFlux";
            break;

          case CMath::Flux:
            os << "Flux";
            break;

          case CMath::Propensity:
            os << "Propensity";
            break;

          case CMath::TotalMass:
            os << "TotalMass";
            break;

          case CMath::DependentMass:
            os << "DependentMass";
            break;

          case CMath::Discontinuous:
            os << "Discontinuous";
            break;

          case CMath::EventDelay:
            os << "EventDelay";
            break;

          case CMath::EventPriority:
            os << "EventPriority";
            break;

          case CMath::EventAssignment:
            os << "EventAssignment";
            break;

          case CMath::EventTrigger:
            os << "EventTrigger";
            break;

          case CMath::EventRoot:
            os << "EventRoot";
            break;

          case CMath::EventRootState:
            os << "EventRootState";
            break;
        }

      std::map< const CObjectInterface *, size_t >::const_iterator found = mObject2Index.find(pMathObject);

      if (found != mObject2Index.end())
        {
          os << "::"  << found->second;
        }
      else
        {
          os << "::"  << mObject2Index.size();
          mObject2Index[pMathObject] = mObject2Index.size();
        }

      return os.str();
    }

  return pDataObject->getObjectParent()->getObjectName() + "::" + pDataObject->getObjectName();
}
<commit_msg>fixed compile error: COPASI/copasi/math/CMathDependencyGraph.cpp:156: error: jump to label 'finish' COPASI/copasi/math/CMathDependencyGraph.cpp:103: error:   from here COPASI/copasi/math/CMathDependencyGraph.cpp:124: error:   crosses initialization of 'std::ofstream GetUpdateSequence' *** [copasi/CMakeFiles/libCOPASI.dir/math/CMathDependencyGraph.cpp.o] Error code 1<commit_after>// Copyright (C) 2011 - 2013 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and The University
// of Manchester.
// All rights reserved.

#include <sstream>

#include "copasi.h"

#include "CMathDependencyGraph.h"
#include "CMathDependencyNode.h"
#include "CMathObject.h"

#include "report/CCopasiContainer.h"
#include "report/CCopasiObjectName.h"
#include "utilities/CCopasiMessage.h"

CMathDependencyGraph::CMathDependencyGraph():
  mObjects2Nodes(),
  mObject2Index()
{}

CMathDependencyGraph::~CMathDependencyGraph()
{
  clear();
}

void CMathDependencyGraph::clear()
{
  iterator it = mObjects2Nodes.begin();
  iterator end = mObjects2Nodes.end();

  for (; it != end; ++it)
    {
      delete(it->second);
    }

  mObjects2Nodes.clear();
}

CMathDependencyGraph::iterator CMathDependencyGraph::addObject(const CObjectInterface * pObject)
{
  iterator found = mObjects2Nodes.find(pObject);

  if (found == mObjects2Nodes.end())
    {
      found = mObjects2Nodes.insert(std::make_pair(pObject, new CMathDependencyNode(pObject))).first;

      const CObjectInterface::ObjectSet & Prerequisites = pObject->getPrerequisites();
      CObjectInterface::ObjectSet::const_iterator it = Prerequisites.begin();
      CObjectInterface::ObjectSet::const_iterator end = Prerequisites.end();

      for (; it != end; ++it)
        {
          iterator foundPrerequisite = mObjects2Nodes.find(*it);

          if (foundPrerequisite == mObjects2Nodes.end())
            {
              foundPrerequisite = addObject(*it);
            }

          foundPrerequisite->second->addDependent(found->second);
          found->second->addPrerequisite(foundPrerequisite->second);
        }
    }

  return found;
}

bool CMathDependencyGraph::getUpdateSequence(const CMath::SimulationContextFlag & context,
    const CObjectInterface::ObjectSet & changedObjects,
    const CObjectInterface::ObjectSet & requestedObjects,
    CObjectInterface::UpdateSequence & updateSequence)
{

  bool success = true;

  iterator found;
  iterator notFound = mObjects2Nodes.end();

  updateSequence.clear();

  CObjectInterface::ObjectSet::const_iterator it = changedObjects.begin();
  CObjectInterface::ObjectSet::const_iterator end = changedObjects.end();

  const_iterator itCheck = mObjects2Nodes.begin();
  const_iterator endCheck = mObjects2Nodes.end();

  // Mark all nodes which are changed or need to be calculated
  for (; it != end && success; ++it)
    {
      found = mObjects2Nodes.find(*it);

      if (found != notFound)
        {
          success &= found->second->updateDependentState(context, changedObjects);
          continue;
        }

      success = false;
    }

  if (!success) goto finish;

  it = requestedObjects.begin();
  end = requestedObjects.end();

  // Mark all nodes which are requested and its prerequisites.
  for (; it != end && success; ++it)
    {
      found = mObjects2Nodes.find(*it);

      if (found != notFound)
        {
          found->second->setRequested(true);
          success &= found->second->updatePrerequisiteState(context, changedObjects);
          continue;
        }

      success = false;
    }

#ifdef COPASI_DEBUG_TRACE
{
  std::ofstream GetUpdateSequence("GetUpdateSequence.dot");
  exportDOTFormat(GetUpdateSequence, "GetUpdateSequence");
  GetUpdateSequence.close();
}
#endif //COPASI_DEBUG_TRACE

  if (!success) goto finish;

  it = requestedObjects.begin();
  end = requestedObjects.end();

  for (; it != end && success; ++it)
    {
      found = mObjects2Nodes.find(*it);

      if (found != notFound)
        {
          success &= found->second->buildUpdateSequence(context, updateSequence);
          continue;
        }

      success = false;
    }

  if (!success) goto finish;

  for (; itCheck != endCheck; ++itCheck)
    {
      // Reset the dependency nodes for the next call.
      itCheck->second->setChanged(false);
      itCheck->second->setRequested(false);
    }

finish:

  if (!success)
    {
      updateSequence.clear();

      CCopasiMessage(CCopasiMessage::ERROR, MCMathModel + 3, (*it)->getCN().c_str());
    }

#ifdef XXXX
  CObjectInterface::UpdateSequence::const_iterator itSeq = updateSequence.begin();
  CObjectInterface::UpdateSequence::const_iterator endSeq = updateSequence.end();

  std::cout << std::endl <<  "Start" << std::endl;

  for (; itSeq != endSeq; ++itSeq)
    {
      if (dynamic_cast< const CMathObject * >(*itSeq))
        {
          std::cout << *static_cast< const CMathObject * >(*itSeq);
        }
      else
        {
          std::cout << (*itSeq)->getCN() << std::endl;
        }
    }

  std::cout << "End" << std::endl;
#endif //

  return success;
}

void CMathDependencyGraph::exportDOTFormat(std::ostream & os, const std::string & name) const
{
  os << "digraph " << name << " {" << std::endl;
  os << "rankdir=LR;" << std::endl;

  mObject2Index.clear();

  const_iterator it = mObjects2Nodes.begin();
  const_iterator end = mObjects2Nodes.end();

  for (; it != end; ++it)
    {
      const CObjectInterface * pObject = it->second->getObject();

      const std::vector< CMathDependencyNode * > & Dependents = it->second->getDependents();
      std::vector< CMathDependencyNode * >::const_iterator itDep = Dependents.begin();
      std::vector< CMathDependencyNode * >::const_iterator endDep = Dependents.end();

      for (; itDep != endDep; ++itDep)
        {
          os << "\"";
          os << getDOTNodeId(pObject);
          os << ((it->second->isChanged()) ? "\\nC" : "\\no");
          os << ((it->second->isRequested()) ? "R" : "o");
          os << "\"";
          os << " -> ";
          os << "\"";
          os << getDOTNodeId((*itDep)->getObject());
          os << (((*itDep)->isChanged()) ? "\\nC" : "\\no");
          os << (((*itDep)->isRequested()) ? "R" : "o");
          os << "\"";
          os << ";" << std::endl;
        }
    }

  os << "}" << std::endl;
}

// static
std::string CMathDependencyGraph::getDOTNodeId(const CObjectInterface * pObject) const
{
  const CCopasiObject * pDataObject = dynamic_cast< const CCopasiObject * >(pObject);
  const CMathObject * pMathObject = dynamic_cast< const CMathObject * >(pObject);

  if (pDataObject == NULL && pMathObject == NULL)
    {
      return "Invalid Node";
    }

  if (pDataObject == NULL)
    {
      pDataObject = pMathObject->getDataObject();
    }

  if (pDataObject == NULL)
    {
      std::ostringstream os;

      switch (pMathObject->getValueType())
        {
          case CMath::ValueTypeUndefined:
            os << "ValueTypeUndefined";
            break;

          case CMath::Value:
            os << "Value";
            break;

          case CMath::Rate:
            os << "ValueRate";
            break;

          case CMath::ParticleFlux:
            os << "ParticleFlux";
            break;

          case CMath::Flux:
            os << "Flux";
            break;

          case CMath::Propensity:
            os << "Propensity";
            break;

          case CMath::TotalMass:
            os << "TotalMass";
            break;

          case CMath::DependentMass:
            os << "DependentMass";
            break;

          case CMath::Discontinuous:
            os << "Discontinuous";
            break;

          case CMath::EventDelay:
            os << "EventDelay";
            break;

          case CMath::EventPriority:
            os << "EventPriority";
            break;

          case CMath::EventAssignment:
            os << "EventAssignment";
            break;

          case CMath::EventTrigger:
            os << "EventTrigger";
            break;

          case CMath::EventRoot:
            os << "EventRoot";
            break;

          case CMath::EventRootState:
            os << "EventRootState";
            break;
        }

      std::map< const CObjectInterface *, size_t >::const_iterator found = mObject2Index.find(pMathObject);

      if (found != mObject2Index.end())
        {
          os << "::"  << found->second;
        }
      else
        {
          os << "::"  << mObject2Index.size();
          mObject2Index[pMathObject] = mObject2Index.size();
        }

      return os.str();
    }

  return pDataObject->getObjectParent()->getObjectName() + "::" + pDataObject->getObjectName();
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Sebastien Rombauts (sebastien.rombauts@gmail.com)
//
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
// or copy at http://opensource.org/licenses/MIT)

#include "GitSourceControlPrivatePCH.h"
#include "GitSourceControlOperations.h"
#include "GitSourceControlState.h"
#include "GitSourceControlCommand.h"
#include "GitSourceControlModule.h"
#include "GitSourceControlUtils.h"

#define LOCTEXT_NAMESPACE "GitSourceControl"

FName FGitConnectWorker::GetName() const
{
	return "Connect";
}

bool FGitConnectWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == "Connect");

	InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("status"), TArray<FString>(), TArray<FString>(), InCommand.InfoMessages, InCommand.ErrorMessages);
	if(!InCommand.bCommandSuccessful || InCommand.ErrorMessages.Num() > 0 || InCommand.InfoMessages.Num() == 0)
	{
		// @todo popup to propose to initialize the git repository
		TSharedRef<FConnect, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FConnect>(InCommand.Operation);
		Operation->SetErrorText(LOCTEXT("NotAWorkingCopyError", "Project is not part of a Git working copy."));
		InCommand.ErrorMessages.Add(LOCTEXT("NotAWorkingCopyErrorHelp", "You should check out a working copy into your project directory.").ToString());
		InCommand.bCommandSuccessful = false;
	}
	return InCommand.bCommandSuccessful;
}

bool FGitConnectWorker::UpdateStates() const
{
	return false;
}

FName FGitRevertWorker::GetName() const
{
	return "Revert";
}

bool FGitRevertWorker::Execute(FGitSourceControlCommand& InCommand)
{
	// reset any changes already added in index
	{
		TArray<FString> Parameters;
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("reset"), Parameters, InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);
	}

	// revert any changes in working copy
	{
		TArray<FString> Parameters;
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("checkout"), Parameters, InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);
	}

	// now update the status of our files
	{
		TArray<FString> Results;
		TArray<FString> Parameters;
		Parameters.Add(TEXT("--porcelain"));
		Parameters.Add(TEXT("--ignored"));

		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("status"), Parameters, InCommand.Files, Results, InCommand.ErrorMessages);
		GitSourceControlUtils::ParseStatusResults(InCommand.Files, Results, States);
	}

	return InCommand.bCommandSuccessful;
}

bool FGitRevertWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitUpdateStatusWorker::GetName() const
{
	return "UpdateStatus";
}

bool FGitUpdateStatusWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == "UpdateStatus");

	// @todo cleanup the following if/else if if conditions (see mercurial plugin)
	if(InCommand.Files.Num() > 0)
	{
		TArray<FString> Results;
		TArray<FString> Parameters;
		Parameters.Add(TEXT("--porcelain"));
		Parameters.Add(TEXT("--ignored"));

		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("status"), Parameters, InCommand.Files, Results, InCommand.ErrorMessages);
		GitSourceControlUtils::ParseStatusResults(InCommand.Files, Results, States);
	}
	else
	{
		InCommand.bCommandSuccessful = true; // nothing to do
	}

	TSharedRef<FUpdateStatus, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FUpdateStatus>(InCommand.Operation);

	if(Operation->ShouldUpdateHistory())
	{
		for(const auto& ItFile : InCommand.Files)
		{
			TArray<FString> Results;
			TArray<FString> Parameters;

			Parameters.Add(TEXT("--max-count 100"));
			Parameters.Add(TEXT("--follow")); // follow file renames
			Parameters.Add(TEXT("--date=raw"));
			Parameters.Add(TEXT("--name-status")); // relative filename at this revision, preceded by a status character

			TArray<FString> Files;
			Files.Add(*ItFile);

			InCommand.bCommandSuccessful &= GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("log"), Parameters, Files, Results, InCommand.ErrorMessages);
			TGitSourceControlHistory History;
			GitSourceControlUtils::ParseLogResults(Results, History);
			Histories.Add(*ItFile, History);
		}
	}

	if(Operation->ShouldGetOpenedOnly())
	{
		// "Open files" are those that have been modified (or added/deleted)

		TArray<FString> Results;
		TArray<FString> Parameters;
		Parameters.Add(TEXT("--porcelain"));
		Parameters.Add(TEXT("--ignored"));

		TArray<FString> Files;
		Files.Add(FPaths::GameDir());

		InCommand.bCommandSuccessful &= GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("status"), Parameters, Files, Results, InCommand.ErrorMessages);
		GitSourceControlUtils::ParseStatusResults(InCommand.Files, Results, States);
	}

	if(Operation->ShouldUpdateModifiedState())
	{
		// @todo: we dont use the ShouldUpdateModifiedState() hint here as a normal Git status will tell us this information?
		TArray<FString> Results;
	}

	return InCommand.bCommandSuccessful;
}

bool FGitUpdateStatusWorker::UpdateStates() const
{
	bool bUpdated = GitSourceControlUtils::UpdateCachedStates(States);

	FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked<FGitSourceControlModule>( "GitSourceControl" );
	FGitSourceControlProvider& Provider = GitSourceControl.GetProvider();

	// add history, if any
	for(const auto& ItHistory : Histories)
	{
		TSharedRef<FGitSourceControlState, ESPMode::ThreadSafe> State = Provider.GetStateInternal(ItHistory.Key);
		State->History = ItHistory.Value;
		State->TimeStamp = FDateTime::Now();
		bUpdated = true;
	}

	return bUpdated;
}

#undef LOCTEXT_NAMESPACE
<commit_msg>Fix for the initial Git Status command path error<commit_after>// Copyright (c) 2014 Sebastien Rombauts (sebastien.rombauts@gmail.com)
//
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
// or copy at http://opensource.org/licenses/MIT)

#include "GitSourceControlPrivatePCH.h"
#include "GitSourceControlOperations.h"
#include "GitSourceControlState.h"
#include "GitSourceControlCommand.h"
#include "GitSourceControlModule.h"
#include "GitSourceControlUtils.h"

#define LOCTEXT_NAMESPACE "GitSourceControl"

FName FGitConnectWorker::GetName() const
{
	return "Connect";
}

bool FGitConnectWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == "Connect");

	InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("status"), TArray<FString>(), TArray<FString>(), InCommand.InfoMessages, InCommand.ErrorMessages);
	if(!InCommand.bCommandSuccessful || InCommand.ErrorMessages.Num() > 0 || InCommand.InfoMessages.Num() == 0)
	{
		// @todo popup to propose to initialize the git repository
		TSharedRef<FConnect, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FConnect>(InCommand.Operation);
		Operation->SetErrorText(LOCTEXT("NotAWorkingCopyError", "Project is not part of a Git working copy."));
		InCommand.ErrorMessages.Add(LOCTEXT("NotAWorkingCopyErrorHelp", "You should check out a working copy into your project directory.").ToString());
		InCommand.bCommandSuccessful = false;
	}
	return InCommand.bCommandSuccessful;
}

bool FGitConnectWorker::UpdateStates() const
{
	return false;
}

FName FGitRevertWorker::GetName() const
{
	return "Revert";
}

bool FGitRevertWorker::Execute(FGitSourceControlCommand& InCommand)
{
	// reset any changes already added in index
	{
		TArray<FString> Parameters;
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("reset"), Parameters, InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);
	}

	// revert any changes in working copy
	{
		TArray<FString> Parameters;
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("checkout"), Parameters, InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);
	}

	// now update the status of our files
	{
		TArray<FString> Results;
		TArray<FString> Parameters;
		Parameters.Add(TEXT("--porcelain"));
		Parameters.Add(TEXT("--ignored"));

		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("status"), Parameters, InCommand.Files, Results, InCommand.ErrorMessages);
		GitSourceControlUtils::ParseStatusResults(InCommand.Files, Results, States);
	}

	return InCommand.bCommandSuccessful;
}

bool FGitRevertWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitUpdateStatusWorker::GetName() const
{
	return "UpdateStatus";
}

bool FGitUpdateStatusWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == "UpdateStatus");

	// @todo cleanup the following if/else if if conditions (see mercurial plugin)
	if(InCommand.Files.Num() > 0)
	{
		TArray<FString> Results;
		TArray<FString> Parameters;
		Parameters.Add(TEXT("--porcelain"));
		Parameters.Add(TEXT("--ignored"));

		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("status"), Parameters, InCommand.Files, Results, InCommand.ErrorMessages);
		GitSourceControlUtils::ParseStatusResults(InCommand.Files, Results, States);
	}
	else
	{
		InCommand.bCommandSuccessful = true; // nothing to do
	}

	TSharedRef<FUpdateStatus, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FUpdateStatus>(InCommand.Operation);

	if(Operation->ShouldUpdateHistory())
	{
		for(const auto& ItFile : InCommand.Files)
		{
			TArray<FString> Results;
			TArray<FString> Parameters;

			Parameters.Add(TEXT("--max-count 100"));
			Parameters.Add(TEXT("--follow")); // follow file renames
			Parameters.Add(TEXT("--date=raw"));
			Parameters.Add(TEXT("--name-status")); // relative filename at this revision, preceded by a status character

			TArray<FString> Files;
			Files.Add(*ItFile);

			InCommand.bCommandSuccessful &= GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("log"), Parameters, Files, Results, InCommand.ErrorMessages);
			TGitSourceControlHistory History;
			GitSourceControlUtils::ParseLogResults(Results, History);
			Histories.Add(*ItFile, History);
		}
	}

	if(Operation->ShouldGetOpenedOnly())
	{
		// "Open files" are those that have been modified (or added/deleted)

		TArray<FString> Results;
		TArray<FString> Parameters;
		Parameters.Add(TEXT("--porcelain"));
		Parameters.Add(TEXT("--ignored"));

		TArray<FString> Files;
		Files.Add(FPaths::ConvertRelativePathToFull(FPaths::GameDir()));

		InCommand.bCommandSuccessful &= GitSourceControlUtils::RunCommand(InCommand.PathToGitBinary, InCommand.PathToGameDir, TEXT("status"), Parameters, Files, Results, InCommand.ErrorMessages);
		GitSourceControlUtils::ParseStatusResults(InCommand.Files, Results, States);
	}

	if(Operation->ShouldUpdateModifiedState())
	{
		// @todo: we dont use the ShouldUpdateModifiedState() hint here as a normal Git status will tell us this information?
		TArray<FString> Results;
	}

	return InCommand.bCommandSuccessful;
}

bool FGitUpdateStatusWorker::UpdateStates() const
{
	bool bUpdated = GitSourceControlUtils::UpdateCachedStates(States);

	FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked<FGitSourceControlModule>( "GitSourceControl" );
	FGitSourceControlProvider& Provider = GitSourceControl.GetProvider();

	// add history, if any
	for(const auto& ItHistory : Histories)
	{
		TSharedRef<FGitSourceControlState, ESPMode::ThreadSafe> State = Provider.GetStateInternal(ItHistory.Key);
		State->History = ItHistory.Value;
		State->TimeStamp = FDateTime::Now();
		bUpdated = true;
	}

	return bUpdated;
}

#undef LOCTEXT_NAMESPACE
<|endoftext|>


<commit_before>// Copyright (c) 2014-2015 Sebastien Rombauts (sebastien.rombauts@gmail.com)
//
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
// or copy at http://opensource.org/licenses/MIT)

#include "GitSourceControlPrivatePCH.h"
#include "GitSourceControlOperations.h"
#include "Misc/Paths.h"
#include "Modules/ModuleManager.h"
#include "SourceControlOperations.h"
#include "ISourceControlModule.h"
#include "GitSourceControlModule.h"
#include "GitSourceControlCommand.h"
#include "GitSourceControlUtils.h"

#define LOCTEXT_NAMESPACE "GitSourceControl"

FName FGitConnectWorker::GetName() const
{
	return "Connect";
}

bool FGitConnectWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	if (0 < InCommand.PathToGitBinary.Len() && GitSourceControlUtils::CheckGitAvailability(InCommand.PathToGitBinary))
	{
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("status"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), TArray<FString>(), InCommand.InfoMessages, InCommand.ErrorMessages);
		if(!InCommand.bCommandSuccessful || InCommand.ErrorMessages.Num() > 0 || InCommand.InfoMessages.Num() == 0)
		{
			StaticCastSharedRef<FConnect>(InCommand.Operation)->SetErrorText(LOCTEXT("NotAGitRepository", "Failed to enable Git source control. You need to initialize the project as a Git repository first."));
			InCommand.bCommandSuccessful = false;
		}
	}
	else
	{
		StaticCastSharedRef<FConnect>(InCommand.Operation)->SetErrorText(LOCTEXT("GitNotFound", "Failed to enable Git source control. You need to install Git and specify a valid path to git executable."));
		InCommand.bCommandSuccessful = false;
	}

	return InCommand.bCommandSuccessful;
}

bool FGitConnectWorker::UpdateStates() const
{
	return false;
}

static FText ParseCommitResults(const TArray<FString>& InResults)
{
	if(InResults.Num() >= 1)
	{
		const FString& FirstLine = InResults[0];
		return FText::Format(LOCTEXT("CommitMessage", "Commited {0}."), FText::FromString(FirstLine));
	}
	return LOCTEXT("CommitMessageUnknown", "Submitted revision.");
}

FName FGitCheckInWorker::GetName() const
{
	return "CheckIn";
}

bool FGitCheckInWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	TSharedRef<FCheckIn, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FCheckIn>(InCommand.Operation);

	// make a temp file to place our commit message in
	FGitScopedTempFile CommitMsgFile(Operation->GetDescription());
	if(CommitMsgFile.GetFilename().Len() > 0)
	{
		TArray<FString> Parameters;
		FString ParamCommitMsgFilename = TEXT("--file=\"");
		ParamCommitMsgFilename += FPaths::ConvertRelativePathToFull(CommitMsgFile.GetFilename());
		ParamCommitMsgFilename += TEXT("\"");
		Parameters.Add(ParamCommitMsgFilename);

		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommit(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, Parameters, InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);
		if(InCommand.bCommandSuccessful)
		{
			// Remove any deleted files from status cache
			FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked<FGitSourceControlModule>("GitSourceControl");
			FGitSourceControlProvider& Provider = GitSourceControl.GetProvider();

			TArray<TSharedRef<ISourceControlState, ESPMode::ThreadSafe>> LocalStates;
			Provider.GetState(InCommand.Files, LocalStates, EStateCacheUsage::Use);
			for (const auto& State : LocalStates)
			{
				if (State->IsDeleted())
				{
					Provider.RemoveFileFromCache(State->GetFilename());
				}
			}

			Operation->SetSuccessMessage(ParseCommitResults(InCommand.InfoMessages));
			UE_LOG(LogSourceControl, Log, TEXT("FGitCheckInWorker: commit successful"));
		}
	}

	// now update the status of our files
	GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

	return InCommand.bCommandSuccessful;
}

bool FGitCheckInWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitMarkForAddWorker::GetName() const
{
	return "MarkForAdd";
}

bool FGitMarkForAddWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("add"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);

	// now update the status of our files
	GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

	return InCommand.bCommandSuccessful;
}

bool FGitMarkForAddWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitDeleteWorker::GetName() const
{
	return "Delete";
}

bool FGitDeleteWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("rm"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);

	// now update the status of our files
	GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

	return InCommand.bCommandSuccessful;
}

bool FGitDeleteWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitRevertWorker::GetName() const
{
	return "Revert";
}

bool FGitRevertWorker::Execute(FGitSourceControlCommand& InCommand)
{
	// reset any changes already added in index
	{
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("reset"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);
	}

	// revert any changes in working copy
	{
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("checkout"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);
	}

	// now update the status of our files
	GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

	return InCommand.bCommandSuccessful;
}

bool FGitRevertWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitSyncWorker::GetName() const
{
   return "Sync";
}

bool FGitSyncWorker::Execute(FGitSourceControlCommand& InCommand)
{
   // pull the branch to get remote changes by rebasing any local commits (not merging them to avoid complex graphs)
   // (this cannot work if any local files are modified but not commited)
   {
      TArray<FString> Parameters;
      Parameters.Add(TEXT("--rebase"));
      InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("pull"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, Parameters, TArray<FString>(), InCommand.InfoMessages, InCommand.ErrorMessages);
   }

   // now update the status of our files
   GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

   return InCommand.bCommandSuccessful;
}

bool FGitSyncWorker::UpdateStates() const
{
   return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitUpdateStatusWorker::GetName() const
{
	return "UpdateStatus";
}

bool FGitUpdateStatusWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	TSharedRef<FUpdateStatus, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FUpdateStatus>(InCommand.Operation);

	if(InCommand.Files.Num() > 0)
	{
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);
		GitSourceControlUtils::RemoveRedundantErrors(InCommand, TEXT("' is outside repository"));

		if(Operation->ShouldUpdateHistory())
		{
			for(int32 Index = 0; Index < States.Num(); Index++)
			{
				FString& File = InCommand.Files[Index];
				TGitSourceControlHistory History;

				if(States[Index].IsConflicted())
				{
					// In case of a merge conflict, we first need to get the tip of the "remote branch" (MERGE_HEAD)
					GitSourceControlUtils::RunGetHistory(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, File, true, InCommand.ErrorMessages, History);
				}
				// Get the history of the file in the current branch
				InCommand.bCommandSuccessful &= GitSourceControlUtils::RunGetHistory(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, File, false, InCommand.ErrorMessages, History);
				Histories.Add(*File, History);
			}
		}
	}
	else
	{
		// Perforce "opened files" are those that have been modified (or added/deleted): that is what we get with a simple Git status from the root
		if(Operation->ShouldGetOpenedOnly())
		{
			TArray<FString> Files;
			Files.Add(FPaths::ConvertRelativePathToFull(FPaths::GameDir()));
			InCommand.bCommandSuccessful = GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, Files, InCommand.ErrorMessages, States);
		}
	}

	// don't use the ShouldUpdateModifiedState() hint here as it is specific to Perforce: the above normal Git status has already told us this information (like Git and Mercurial)

	return InCommand.bCommandSuccessful;
}

bool FGitUpdateStatusWorker::UpdateStates() const
{
	bool bUpdated = GitSourceControlUtils::UpdateCachedStates(States);

	FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked<FGitSourceControlModule>( "GitSourceControl" );
	FGitSourceControlProvider& Provider = GitSourceControl.GetProvider();

	// add history, if any
	for(const auto& History : Histories)
	{
		TSharedRef<FGitSourceControlState, ESPMode::ThreadSafe> State = Provider.GetStateInternal(History.Key);
		State->History = History.Value;
		State->TimeStamp = FDateTime::Now();
		bUpdated = true;
	}

	return bUpdated;
}

FName FGitCopyWorker::GetName() const
{
	return "Copy";
}

bool FGitCopyWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	// Copy or Move operation on a single file : Git does not need an explicit copy nor move,
	// but after a Move the Editor create a redirector file with the old asset name that points to the new asset.
	// The redirector needs to be commited with the new asset to perform a real rename.
	// => the following is to "MarkForAdd" the redirector, but it still need to be committed by selecting the whole directory and "check-in"
	InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("add"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);

	return InCommand.bCommandSuccessful;
}

bool FGitCopyWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(OutStates);
}

FName FGitResolveWorker::GetName() const
{
	return "Resolve";
}

bool FGitResolveWorker::Execute( class FGitSourceControlCommand& InCommand )
{
	check(InCommand.Operation->GetName() == GetName());

	// mark the conflicting files as resolved:
	{
		TArray<FString> Results;
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("add"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, Results, InCommand.ErrorMessages);
	}

	// now update the status of our files
	GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

	return InCommand.bCommandSuccessful;
}

bool FGitResolveWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

#undef LOCTEXT_NAMESPACE
<commit_msg>Improve log message on successful CheckIn<commit_after>// Copyright (c) 2014-2015 Sebastien Rombauts (sebastien.rombauts@gmail.com)
//
// Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
// or copy at http://opensource.org/licenses/MIT)

#include "GitSourceControlPrivatePCH.h"
#include "GitSourceControlOperations.h"
#include "Misc/Paths.h"
#include "Modules/ModuleManager.h"
#include "SourceControlOperations.h"
#include "ISourceControlModule.h"
#include "GitSourceControlModule.h"
#include "GitSourceControlCommand.h"
#include "GitSourceControlUtils.h"

#define LOCTEXT_NAMESPACE "GitSourceControl"

FName FGitConnectWorker::GetName() const
{
	return "Connect";
}

bool FGitConnectWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	if (0 < InCommand.PathToGitBinary.Len() && GitSourceControlUtils::CheckGitAvailability(InCommand.PathToGitBinary))
	{
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("status"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), TArray<FString>(), InCommand.InfoMessages, InCommand.ErrorMessages);
		if(!InCommand.bCommandSuccessful || InCommand.ErrorMessages.Num() > 0 || InCommand.InfoMessages.Num() == 0)
		{
			StaticCastSharedRef<FConnect>(InCommand.Operation)->SetErrorText(LOCTEXT("NotAGitRepository", "Failed to enable Git source control. You need to initialize the project as a Git repository first."));
			InCommand.bCommandSuccessful = false;
		}
	}
	else
	{
		StaticCastSharedRef<FConnect>(InCommand.Operation)->SetErrorText(LOCTEXT("GitNotFound", "Failed to enable Git source control. You need to install Git and specify a valid path to git executable."));
		InCommand.bCommandSuccessful = false;
	}

	return InCommand.bCommandSuccessful;
}

bool FGitConnectWorker::UpdateStates() const
{
	return false;
}

static FText ParseCommitResults(const TArray<FString>& InResults)
{
	if(InResults.Num() >= 1)
	{
		const FString& FirstLine = InResults[0];
		return FText::Format(LOCTEXT("CommitMessage", "Commited {0}."), FText::FromString(FirstLine));
	}
	return LOCTEXT("CommitMessageUnknown", "Submitted revision.");
}

FName FGitCheckInWorker::GetName() const
{
	return "CheckIn";
}

bool FGitCheckInWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	TSharedRef<FCheckIn, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FCheckIn>(InCommand.Operation);

	// make a temp file to place our commit message in
	FGitScopedTempFile CommitMsgFile(Operation->GetDescription());
	if(CommitMsgFile.GetFilename().Len() > 0)
	{
		TArray<FString> Parameters;
		FString ParamCommitMsgFilename = TEXT("--file=\"");
		ParamCommitMsgFilename += FPaths::ConvertRelativePathToFull(CommitMsgFile.GetFilename());
		ParamCommitMsgFilename += TEXT("\"");
		Parameters.Add(ParamCommitMsgFilename);

		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommit(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, Parameters, InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);
		if(InCommand.bCommandSuccessful)
		{
			// Remove any deleted files from status cache
			FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked<FGitSourceControlModule>("GitSourceControl");
			FGitSourceControlProvider& Provider = GitSourceControl.GetProvider();

			TArray<TSharedRef<ISourceControlState, ESPMode::ThreadSafe>> LocalStates;
			Provider.GetState(InCommand.Files, LocalStates, EStateCacheUsage::Use);
			for (const auto& State : LocalStates)
			{
				if (State->IsDeleted())
				{
					Provider.RemoveFileFromCache(State->GetFilename());
				}
			}

			Operation->SetSuccessMessage(ParseCommitResults(InCommand.InfoMessages));
			UE_LOG(LogSourceControl, Log, TEXT("commit successful: %s"), *InCommand.InfoMessages[0]);
		}
	}

	// now update the status of our files
	GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

	return InCommand.bCommandSuccessful;
}

bool FGitCheckInWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitMarkForAddWorker::GetName() const
{
	return "MarkForAdd";
}

bool FGitMarkForAddWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("add"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);

	// now update the status of our files
	GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

	return InCommand.bCommandSuccessful;
}

bool FGitMarkForAddWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitDeleteWorker::GetName() const
{
	return "Delete";
}

bool FGitDeleteWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("rm"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);

	// now update the status of our files
	GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

	return InCommand.bCommandSuccessful;
}

bool FGitDeleteWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitRevertWorker::GetName() const
{
	return "Revert";
}

bool FGitRevertWorker::Execute(FGitSourceControlCommand& InCommand)
{
	// reset any changes already added in index
	{
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("reset"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);
	}

	// revert any changes in working copy
	{
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("checkout"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);
	}

	// now update the status of our files
	GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

	return InCommand.bCommandSuccessful;
}

bool FGitRevertWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitSyncWorker::GetName() const
{
   return "Sync";
}

bool FGitSyncWorker::Execute(FGitSourceControlCommand& InCommand)
{
   // pull the branch to get remote changes by rebasing any local commits (not merging them to avoid complex graphs)
   // (this cannot work if any local files are modified but not commited)
   {
      TArray<FString> Parameters;
      Parameters.Add(TEXT("--rebase"));
      InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("pull"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, Parameters, TArray<FString>(), InCommand.InfoMessages, InCommand.ErrorMessages);
   }

   // now update the status of our files
   GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

   return InCommand.bCommandSuccessful;
}

bool FGitSyncWorker::UpdateStates() const
{
   return GitSourceControlUtils::UpdateCachedStates(States);
}

FName FGitUpdateStatusWorker::GetName() const
{
	return "UpdateStatus";
}

bool FGitUpdateStatusWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	TSharedRef<FUpdateStatus, ESPMode::ThreadSafe> Operation = StaticCastSharedRef<FUpdateStatus>(InCommand.Operation);

	if(InCommand.Files.Num() > 0)
	{
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);
		GitSourceControlUtils::RemoveRedundantErrors(InCommand, TEXT("' is outside repository"));

		if(Operation->ShouldUpdateHistory())
		{
			for(int32 Index = 0; Index < States.Num(); Index++)
			{
				FString& File = InCommand.Files[Index];
				TGitSourceControlHistory History;

				if(States[Index].IsConflicted())
				{
					// In case of a merge conflict, we first need to get the tip of the "remote branch" (MERGE_HEAD)
					GitSourceControlUtils::RunGetHistory(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, File, true, InCommand.ErrorMessages, History);
				}
				// Get the history of the file in the current branch
				InCommand.bCommandSuccessful &= GitSourceControlUtils::RunGetHistory(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, File, false, InCommand.ErrorMessages, History);
				Histories.Add(*File, History);
			}
		}
	}
	else
	{
		// Perforce "opened files" are those that have been modified (or added/deleted): that is what we get with a simple Git status from the root
		if(Operation->ShouldGetOpenedOnly())
		{
			TArray<FString> Files;
			Files.Add(FPaths::ConvertRelativePathToFull(FPaths::GameDir()));
			InCommand.bCommandSuccessful = GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, Files, InCommand.ErrorMessages, States);
		}
	}

	// don't use the ShouldUpdateModifiedState() hint here as it is specific to Perforce: the above normal Git status has already told us this information (like Git and Mercurial)

	return InCommand.bCommandSuccessful;
}

bool FGitUpdateStatusWorker::UpdateStates() const
{
	bool bUpdated = GitSourceControlUtils::UpdateCachedStates(States);

	FGitSourceControlModule& GitSourceControl = FModuleManager::LoadModuleChecked<FGitSourceControlModule>( "GitSourceControl" );
	FGitSourceControlProvider& Provider = GitSourceControl.GetProvider();

	// add history, if any
	for(const auto& History : Histories)
	{
		TSharedRef<FGitSourceControlState, ESPMode::ThreadSafe> State = Provider.GetStateInternal(History.Key);
		State->History = History.Value;
		State->TimeStamp = FDateTime::Now();
		bUpdated = true;
	}

	return bUpdated;
}

FName FGitCopyWorker::GetName() const
{
	return "Copy";
}

bool FGitCopyWorker::Execute(FGitSourceControlCommand& InCommand)
{
	check(InCommand.Operation->GetName() == GetName());

	// Copy or Move operation on a single file : Git does not need an explicit copy nor move,
	// but after a Move the Editor create a redirector file with the old asset name that points to the new asset.
	// The redirector needs to be commited with the new asset to perform a real rename.
	// => the following is to "MarkForAdd" the redirector, but it still need to be committed by selecting the whole directory and "check-in"
	InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("add"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, InCommand.InfoMessages, InCommand.ErrorMessages);

	return InCommand.bCommandSuccessful;
}

bool FGitCopyWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(OutStates);
}

FName FGitResolveWorker::GetName() const
{
	return "Resolve";
}

bool FGitResolveWorker::Execute( class FGitSourceControlCommand& InCommand )
{
	check(InCommand.Operation->GetName() == GetName());

	// mark the conflicting files as resolved:
	{
		TArray<FString> Results;
		InCommand.bCommandSuccessful = GitSourceControlUtils::RunCommand(TEXT("add"), InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, TArray<FString>(), InCommand.Files, Results, InCommand.ErrorMessages);
	}

	// now update the status of our files
	GitSourceControlUtils::RunUpdateStatus(InCommand.PathToGitBinary, InCommand.PathToRepositoryRoot, InCommand.Files, InCommand.ErrorMessages, States);

	return InCommand.bCommandSuccessful;
}

bool FGitResolveWorker::UpdateStates() const
{
	return GitSourceControlUtils::UpdateCachedStates(States);
}

#undef LOCTEXT_NAMESPACE
<|endoftext|>


<commit_before>#include "ride/view.h"

#include "base/cc.h"

#include "api/app.h"
#include "api/renderer.h"

#include "ride/theme.h"

View::View()
    : app(nullptr)
    , theme(nullptr)
    , client_rect
    {
        {50_px, 50_px},
        {300_px, 300_px}
    }
    , scroll{0_px, 0_px}
    , body_rect(0_px)
{
}

std::optional<ScrollbarData> layout_scrollbar(rect<pix>* view_rect, Theme* theme,
    pix document_height, pix current_scroll,
    bool is_vertical, Side scrollbar_on_side, Side top_side, Side bottom_side)
{
    const auto view_rect_width = view_rect->width;
    const auto view_rect_height = view_rect->height;

    const auto view_prop = is_vertical ? view_rect_height : view_rect_width;

    if(view_prop >= document_height)
    {
        return {};
    }

    const auto fraction = view_prop / document_height;

    const auto through_rect = view_rect->cut(scrollbar_on_side, theme->scrollbar_width);

    auto track_rect = through_rect;
    const auto top_button_rect = track_rect.cut(top_side, theme->scrollbar_width);
    const auto bottom_button_rect = track_rect.cut(bottom_side, theme->scrollbar_width);

    /*

    +---+ ------------------------------+
    | A | top_button_rect               |
    +---+ --------------+               |
    |///|               |               |
    |///| offset        |               |
    |///|               |               |
    +---+               |               |
    |   |               |               |
    |   |               |               |
    |   | thumb_rect    | track_rect    | through_rect
    |   |               |               |
    |   |               |               |
    +---+               |               |
    |///|               |               |
    |///|               |               |
    +---+  -------------+               |
    | V | bottom_button_rect            |
    +---+ ------------------------------+

    */

    const auto scroll_fraction = current_scroll / document_height;

    const auto size_prop = is_vertical ? track_rect.height : track_rect.width;
    const auto thumb_size = size_prop * fraction;
    const auto available_size = size_prop - thumb_size;
    const auto thumb_offset = available_size * scroll_fraction;
    xassert(thumb_offset >= 0_px, thumb_offset.value);

    const auto vtop = track_rect.get_top() - thumb_offset;

    const auto thumb_rect = is_vertical
        ? rect<pix>::from_lrtb(track_rect.get_left(), track_rect.get_right(), vtop, vtop - thumb_size)
        : rect<pix>::from_lrtb(track_rect.get_left() + thumb_offset, track_rect.get_left() + thumb_offset + thumb_size, track_rect.get_top(), track_rect.get_bottom())
        ;

    return ScrollbarData
    {
        through_rect,
        thumb_rect,
        top_button_rect,
        bottom_button_rect
    };
}

void draw_scrollbar(const ScrollbarData& data, App* app, Theme* theme, Renderer* cache)
{
    const auto clip_scope = ClipScope{cache, app->to_dip(data.through_rect)};
    draw_rect(cache, app->to_dip(data.through_rect), theme->scroll_through_color);
    draw_rect(cache, app->to_dip(data.thumb_rect), theme->scroll_thumb_color);
    draw_rect(cache, app->to_dip(data.top_button_rect), theme->scroll_button_color);
    draw_rect(cache, app->to_dip(data.bottom_button_rect), theme->scroll_button_color);
}

void View::on_layout(const rect<pix>& new_client_rect)
{
    client_rect = new_client_rect;
    body_rect = new_client_rect;

    const auto scroll_size = get_scroll_size();

    if(scroll_size.height)
    {
        vertical_scrollbar_data = layout_scrollbar
        (
            &body_rect, theme, *scroll_size.height, scroll.y,
            true, get_vertical_scroll_side(*theme), Side::top, Side::bottom
        );
    }

    if(scroll_size.width)
    {
        horizontal_scrollbar_data = layout_scrollbar
        (
            &body_rect, theme,
            *scroll_size.width, scroll.x,
            false, get_horizontal_scroll_side(*theme), Side::left, Side::right
        );
    }

    on_layout_body();
}

void View::on_layout_body()
{
}

std::optional<pix> keep_above_zero(std::optional<pix> p)
{
    if(p.has_value() == false) { return p; }

    if(p->value > 0) { return p; }
    else return 0_px;
}

ScrollSize View::get_scroll_size()
{
    const auto r = calculate_scroll_size();
    return
    {
        keep_above_zero(r.width),
        keep_above_zero(r.height)
    };
}

void View::draw(Renderer* cache)
{
    if(vertical_scrollbar_data)
    {
        draw_scrollbar(*vertical_scrollbar_data, app, theme, cache);
    }

    if(horizontal_scrollbar_data)
    {
        draw_scrollbar(*horizontal_scrollbar_data, app, theme, cache);
    }

    const auto clip_scope = ClipScope{cache, app->to_dip(body_rect)};
    draw_body(cache);
}

void do_scroll(pix* scroll, const std::optional<pix>& size, int d, pix change)
{
    if(size)
    {
        if(d != 0)
        {
            *scroll = keep_within
            (
                0_px,
                *scroll + static_cast<double>(d) * change,
                *size
            );
        }
        else
        {
            *scroll = keep_within
            (
                0_px,
                *scroll,
                *size
            );
        }
    }
    else
    {
        // if not scroll is requested, make sure the scroll is zero
        *scroll = 0_px;
    }

}

void View::on_mouse_wheel(int dx, int dy)
{
    const auto scroll_size = get_scroll_size();

    // y: scroll+ up => y - scroll
    // x: scroll+ right =>x + scroll

    do_scroll(&scroll.x, scroll_size.width, dx, theme->horizontal_scroll);
    do_scroll(&scroll.y, scroll_size.height, -dy, theme->vertical_scroll);
}

void View::keep_scroll_within()
{
    on_mouse_wheel(0, 0);
}

void View::on_mouse_pressed(MouseButton, const Meta&, const vec2<pix>&, int)
{
}

void View::on_mouse_moved(const Meta&, const vec2<pix>&)
{
}

void View::on_mouse_released(MouseButton, const Meta&, const vec2<pix>&)
{
}

void View::on_text(const std::string&)
{
}

///////////////////////////////////////////////////////////////////////////////////////////////////

rect<pix> LineView::hit_rect_for_line(const pix& ypos) const
{
    const auto height = app->to_pix(font->get_height());
    const auto spacing = theme->line_spacing;

    return rect<pix>::from_ltrb
    (
        body_rect.get_left(),
        ypos + height + spacing,
        body_rect.get_right(),
        ypos
    );
}

pix LineView::calculate_line_height() const
{
    return app->to_pix(font->get_height()) + theme->line_spacing;
}

pix LineView::line_number_to_y(std::size_t line) const
{
    const auto line_offset = static_cast<double>(line) * calculate_line_height();
    return client_rect.height - calculate_line_height() - line_offset;
}

ScrollSize LineView::calculate_scroll_size()
{
    return
    {
        // todo(Gustav): is this correct?
        get_document_width(),
        static_cast<double>(get_number_of_lines()) * calculate_line_height() - calculate_line_height()
    };
}

std::optional<std::size_t> LineView::get_index_under_view_position(const vec2<pix> relative_mouse)
{
    const auto p = vec2<pix>{relative_mouse.x - scroll.x, relative_mouse.y - scroll.y};

    // todo(Gustav): guesstimate entry from y coordinate and then do the checks to avoid checking all the items...
    for(std::size_t index  = 0; index < get_number_of_lines(); index += 1)
    {
        if(hit_rect_for_line(line_number_to_y(index)).contains(p))
        {
            return index;
        }
    }

    return std::nullopt;
}

void LineView::draw_lines(Renderer* cache)
{
    // todo(Gustav): guesstimate entry from y coordinate and then do the checks to avoid checking all the items...
    for(std::size_t index = 0; index < get_number_of_lines(); index+=1)
    {
        draw_line(cache, index,
            body_rect.x + scroll.x,
            body_rect.y + line_number_to_y(index) + scroll.y
        );
    }
}
<commit_msg>fix: proper horizontal scrolling<commit_after>#include "ride/view.h"

#include "base/cc.h"

#include "api/app.h"
#include "api/renderer.h"

#include "ride/theme.h"

View::View()
    : app(nullptr)
    , theme(nullptr)
    , client_rect
    {
        {50_px, 50_px},
        {300_px, 300_px}
    }
    , scroll{0_px, 0_px}
    , body_rect(0_px)
{
}

std::optional<ScrollbarData> layout_scrollbar(rect<pix>* view_rect, Theme* theme,
    pix document_height, pix current_scroll,
    bool is_vertical, Side scrollbar_on_side, Side top_side, Side bottom_side)
{
    const auto view_rect_width = view_rect->width;
    const auto view_rect_height = view_rect->height;

    const auto view_prop = is_vertical ? view_rect_height : view_rect_width;

    if(view_prop >= document_height)
    {
        return {};
    }

    const auto fraction = view_prop / document_height;

    const auto through_rect = view_rect->cut(scrollbar_on_side, theme->scrollbar_width);

    auto track_rect = through_rect;
    const auto top_button_rect = track_rect.cut(top_side, theme->scrollbar_width);
    const auto bottom_button_rect = track_rect.cut(bottom_side, theme->scrollbar_width);

    /*

    +---+ ------------------------------+
    | A | top_button_rect               |
    +---+ --------------+               |
    |///|               |               |
    |///| offset        |               |
    |///|               |               |
    +---+               |               |
    |   |               |               |
    |   |               |               |
    |   | thumb_rect    | track_rect    | through_rect
    |   |               |               |
    |   |               |               |
    +---+               |               |
    |///|               |               |
    |///|               |               |
    +---+  -------------+               |
    | V | bottom_button_rect            |
    +---+ ------------------------------+

    */

    const auto scroll_fraction = current_scroll / document_height;

    const auto size_prop = is_vertical ? track_rect.height : track_rect.width;
    const auto thumb_size = size_prop * fraction;
    const auto available_size = size_prop - thumb_size;
    const auto thumb_offset = available_size * scroll_fraction;
    xassert(thumb_offset >= 0_px, thumb_offset.value);

    const auto vtop = track_rect.get_top() - thumb_offset;

    const auto thumb_rect = is_vertical
        ? rect<pix>::from_lrtb(track_rect.get_left(), track_rect.get_right(), vtop, vtop - thumb_size)
        : rect<pix>::from_lrtb(track_rect.get_left() + thumb_offset, track_rect.get_left() + thumb_offset + thumb_size, track_rect.get_top(), track_rect.get_bottom())
        ;

    return ScrollbarData
    {
        through_rect,
        thumb_rect,
        top_button_rect,
        bottom_button_rect
    };
}

void draw_scrollbar(const ScrollbarData& data, App* app, Theme* theme, Renderer* cache)
{
    const auto clip_scope = ClipScope{cache, app->to_dip(data.through_rect)};
    draw_rect(cache, app->to_dip(data.through_rect), theme->scroll_through_color);
    draw_rect(cache, app->to_dip(data.thumb_rect), theme->scroll_thumb_color);
    draw_rect(cache, app->to_dip(data.top_button_rect), theme->scroll_button_color);
    draw_rect(cache, app->to_dip(data.bottom_button_rect), theme->scroll_button_color);
}

void View::on_layout(const rect<pix>& new_client_rect)
{
    client_rect = new_client_rect;
    body_rect = new_client_rect;

    const auto scroll_size = get_scroll_size();

    if(scroll_size.height)
    {
        vertical_scrollbar_data = layout_scrollbar
        (
            &body_rect, theme, *scroll_size.height, scroll.y,
            true, get_vertical_scroll_side(*theme), Side::top, Side::bottom
        );
    }

    if(scroll_size.width)
    {
        horizontal_scrollbar_data = layout_scrollbar
        (
            &body_rect, theme,
            *scroll_size.width, scroll.x,
            false, get_horizontal_scroll_side(*theme), Side::left, Side::right
        );
    }

    on_layout_body();
}

void View::on_layout_body()
{
}

std::optional<pix> keep_above_zero(std::optional<pix> p)
{
    if(p.has_value() == false) { return p; }

    if(p->value > 0) { return p; }
    else return 0_px;
}

ScrollSize View::get_scroll_size()
{
    const auto r = calculate_scroll_size();
    return
    {
        keep_above_zero(r.width),
        keep_above_zero(r.height)
    };
}

void View::draw(Renderer* cache)
{
    if(vertical_scrollbar_data)
    {
        draw_scrollbar(*vertical_scrollbar_data, app, theme, cache);
    }

    if(horizontal_scrollbar_data)
    {
        draw_scrollbar(*horizontal_scrollbar_data, app, theme, cache);
    }

    const auto clip_scope = ClipScope{cache, app->to_dip(body_rect)};
    draw_body(cache);
}

void do_scroll(pix* scroll, const std::optional<pix>& size, int d, pix change)
{
    if(size)
    {
        if(d != 0)
        {
            *scroll = keep_within
            (
                0_px,
                *scroll + static_cast<double>(d) * change,
                *size
            );
        }
        else
        {
            *scroll = keep_within
            (
                0_px,
                *scroll,
                *size
            );
        }
    }
    else
    {
        // if not scroll is requested, make sure the scroll is zero
        *scroll = 0_px;
    }

}

void View::on_mouse_wheel(int dx, int dy)
{
    const auto scroll_size = get_scroll_size();

    // y: scroll+ up => y - scroll
    // x: scroll+ right =>x + scroll

    do_scroll(&scroll.x, scroll_size.width, dx, theme->horizontal_scroll);
    do_scroll(&scroll.y, scroll_size.height, -dy, theme->vertical_scroll);
}

void View::keep_scroll_within()
{
    on_mouse_wheel(0, 0);
}

void View::on_mouse_pressed(MouseButton, const Meta&, const vec2<pix>&, int)
{
}

void View::on_mouse_moved(const Meta&, const vec2<pix>&)
{
}

void View::on_mouse_released(MouseButton, const Meta&, const vec2<pix>&)
{
}

void View::on_text(const std::string&)
{
}

///////////////////////////////////////////////////////////////////////////////////////////////////

rect<pix> LineView::hit_rect_for_line(const pix& ypos) const
{
    const auto height = app->to_pix(font->get_height());
    const auto spacing = theme->line_spacing;

    return rect<pix>::from_ltrb
    (
        body_rect.get_left(),
        ypos + height + spacing,
        body_rect.get_right(),
        ypos
    );
}

pix LineView::calculate_line_height() const
{
    return app->to_pix(font->get_height()) + theme->line_spacing;
}

pix LineView::line_number_to_y(std::size_t line) const
{
    const auto line_offset = static_cast<double>(line) * calculate_line_height();
    return client_rect.height - calculate_line_height() - line_offset;
}

ScrollSize LineView::calculate_scroll_size()
{
    return
    {
        // todo(Gustav): is this correct?
        get_document_width(),
        static_cast<double>(get_number_of_lines()) * calculate_line_height() - calculate_line_height()
    };
}

std::optional<std::size_t> LineView::get_index_under_view_position(const vec2<pix> relative_mouse)
{
    const auto p = vec2<pix>{relative_mouse.x - scroll.x, relative_mouse.y - scroll.y};

    // todo(Gustav): guesstimate entry from y coordinate and then do the checks to avoid checking all the items...
    for(std::size_t index  = 0; index < get_number_of_lines(); index += 1)
    {
        if(hit_rect_for_line(line_number_to_y(index)).contains(p))
        {
            return index;
        }
    }

    return std::nullopt;
}

void LineView::draw_lines(Renderer* cache)
{
    // todo(Gustav): guesstimate entry from y coordinate and then do the checks to avoid checking all the items...
    for(std::size_t index = 0; index < get_number_of_lines(); index+=1)
    {
        draw_line(cache, index,
            body_rect.x - scroll.x,
            body_rect.y + line_number_to_y(index) + scroll.y
        );
    }
}
<|endoftext|>


<commit_before>/**\file
 * \ingroup example-programmes
 * \brief Matrix-esque terminal animation
 *
 * This is a terminal programme that uses libefgy's vt100 code to render a text
 * version of the matrix 'scrolling streams of text' animation. It's really
 * fairly simple but also kinda nice to see how the vt100 output is performing.
 *
 * \image html matrix.png "screenshot of the programme running in Terminal"
 *
 * \copyright
 * Copyright (c) 2012-2013, ef.gy Project Members
 * \copyright
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * \copyright
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * \copyright
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * \see Project Documentation: http://ef.gy/documentation/libefgy
 * \see Project Source Code: http://git.becquerel.org/jyujin/libefgy.git
 */

#include <iostream>

#include <ef.gy/vt100.h>
#include <ef.gy/random.h>
#include <chrono>

using namespace efgy;
using namespace std::chrono;

terminal::vt100<> output;
random::mersenneTwister<> rng(1337);
system_clock::time_point now;

class matrixStream
{
    public:
        class cell
        {
            public:
                cell(const unsigned long &pCharacter)
                    : character(pCharacter), created(now) {}

                unsigned long character;
                system_clock::time_point created;
        };

        matrixStream(const std::size_t &pLine, const std::size_t &pColumn)
            : line(pLine), column(pColumn), last(now), doDelete(false) {}

        std::size_t line;
        std::size_t column;
        std::vector<cell> data;
        system_clock::time_point last;
        bool doDelete;

        bool update (void)
        {
            if ((now - last) > std::chrono::milliseconds(10))
            {
                last = now;
                switch (rng() % 3)
                {
                    case 0:
                        if (data.size() > 0)
                        {
                            data.erase(data.begin() + (rng() % data.size()));
                        }
                        else
                        {
                            data.push_back(cell(rng() % (1 << 7)));
                        }
                        break;
                    default:
                        data.push_back(cell(rng() % (1 << 7)));
                }
            }
            std::array<std::size_t,2> s = output.size();
            if (data.size() > (s[1]/2))
            {
                if (line < s[1])
                {
                    if (rng() % 5 == 0)
                    {
                        output.target[line][column].content = ' ';
                        line++;
                    }
                }
                else
                {
                    doDelete = true;
                }
                data.erase(data.begin());
            }
            int i = line;
            for (cell &d : data)
            {
                if (i >= s[1])
                {
                    break;
                }
                output.target[i][column].content = d.character;
                output.target[i][column].foregroundColour = (now - d.created) > std::chrono::milliseconds(120) ? 2 : 7;
                output.target[i][column].backgroundColour = 0;
                i++;
            }
            return true;
        }
};

int main (int argc, char **argv)
{
    output.resize(output.getOSDimensions());
    std::array<std::size_t,2> s = output.size();

    int i;

    std::vector<matrixStream> streams;

    while (1)
    {
        i++;
        now = system_clock::now();

        if ((streams.size() <= 100) && (i % 50 == 0))
        {
            std::size_t l = rng() % (s[1] / 3);
            std::size_t c = rng() %  s[0];
            streams.push_back(matrixStream(l, c));
        }

        for (matrixStream &s : streams)
        {
            s.update();
        }

        for (unsigned int i = 0; i < streams.size(); i++)
        {
            if (streams[i].doDelete)
            {
                streams.erase(streams.begin() + i);
            }
        }

        std::cout << output.flush();
    }

    for (std::string s = output.flush(); s != ""; s = output.flush())
    {
        std::cout << s;
        i++;
    }

    std::cout << "\e[39;49;0m\n";

    return 0;
}
<commit_msg>minor documentation fix<commit_after>/**\file
 * \ingroup example-programmes
 * \brief Matrix-esque terminal animation
 *
 * This is a terminal programme that uses libefgy's vt100 code to render a text
 * version of the matrix 'scrolling streams of text' animation. It's really
 * fairly simple but also kinda nice to see how the vt100 output is performing.
 *
 * \image html matrix.png "Screenshot of the programme running in Terminal.app"
 *
 * \copyright
 * Copyright (c) 2012-2013, ef.gy Project Members
 * \copyright
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * \copyright
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * \copyright
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * \see Project Documentation: http://ef.gy/documentation/libefgy
 * \see Project Source Code: http://git.becquerel.org/jyujin/libefgy.git
 */

#include <iostream>

#include <ef.gy/vt100.h>
#include <ef.gy/random.h>
#include <chrono>

using namespace efgy;
using namespace std::chrono;

terminal::vt100<> output;
random::mersenneTwister<> rng(1337);
system_clock::time_point now;

class matrixStream
{
    public:
        class cell
        {
            public:
                cell(const unsigned long &pCharacter)
                    : character(pCharacter), created(now) {}

                unsigned long character;
                system_clock::time_point created;
        };

        matrixStream(const std::size_t &pLine, const std::size_t &pColumn)
            : line(pLine), column(pColumn), last(now), doDelete(false) {}

        std::size_t line;
        std::size_t column;
        std::vector<cell> data;
        system_clock::time_point last;
        bool doDelete;

        bool update (void)
        {
            if ((now - last) > std::chrono::milliseconds(10))
            {
                last = now;
                switch (rng() % 3)
                {
                    case 0:
                        if (data.size() > 0)
                        {
                            data.erase(data.begin() + (rng() % data.size()));
                        }
                        else
                        {
                            data.push_back(cell(rng() % (1 << 7)));
                        }
                        break;
                    default:
                        data.push_back(cell(rng() % (1 << 7)));
                }
            }
            std::array<std::size_t,2> s = output.size();
            if (data.size() > (s[1]/2))
            {
                if (line < s[1])
                {
                    if (rng() % 5 == 0)
                    {
                        output.target[line][column].content = ' ';
                        line++;
                    }
                }
                else
                {
                    doDelete = true;
                }
                data.erase(data.begin());
            }
            int i = line;
            for (cell &d : data)
            {
                if (i >= s[1])
                {
                    break;
                }
                output.target[i][column].content = d.character;
                output.target[i][column].foregroundColour = (now - d.created) > std::chrono::milliseconds(120) ? 2 : 7;
                output.target[i][column].backgroundColour = 0;
                i++;
            }
            return true;
        }
};

int main (int argc, char **argv)
{
    output.resize(output.getOSDimensions());
    std::array<std::size_t,2> s = output.size();

    int i;

    std::vector<matrixStream> streams;

    while (1)
    {
        i++;
        now = system_clock::now();

        if ((streams.size() <= 100) && (i % 50 == 0))
        {
            std::size_t l = rng() % (s[1] / 3);
            std::size_t c = rng() %  s[0];
            streams.push_back(matrixStream(l, c));
        }

        for (matrixStream &s : streams)
        {
            s.update();
        }

        for (unsigned int i = 0; i < streams.size(); i++)
        {
            if (streams[i].doDelete)
            {
                streams.erase(streams.begin() + i);
            }
        }

        std::cout << output.flush();
    }

    for (std::string s = output.flush(); s != ""; s = output.flush())
    {
        std::cout << s;
        i++;
    }

    std::cout << "\e[39;49;0m\n";

    return 0;
}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/generic/memory/lib/spd/rdimm/ddr4/rdimm_raw_cards.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file raw_cards.H
/// @brief Raw card data structure
///
// *HWP HWP Owner: Andre Marin <aamarin@us.ibm.com>
// *HWP HWP Backup: Jacob Harvey <jlharvey@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 3
// *HWP Consumed by: HB:FSP

#ifndef _MSS_RDIMM_RAW_CARDS_H_
#define _MSS_RDIMM_RAW_CARDS_H_

#include <fapi2.H>
#include <cstdint>
#include <vector>
#include <generic/memory/lib/spd/common/rcw_settings.H>

namespace mss
{
namespace rdimm
{

///
/// @brief raw card encoding as per SPD
/// @note In the order of the vector below which needs to be sorted by enum value
///
enum raw_card_rev : uint8_t
{
    A1 = 0x20,
    B1 = 0x21,

    // RDIMM power-on
    C1 = 0x22,

    // TK - Change VBU value to a no-op value or a value that will never be reached -JLH
    VBU = 0x23,

    B2 = 0x41,
    C2 = 0x42,

    // Default settings used for DIMMs that do not use a JEDEC raw card reference
    CUSTOM = 0xFF,
};

// Raw cards can share the same raw card # between RDIMM and LRDIMMs so
// we track them independently. Since all of these don't come from SPD for DDR4,
// we have to set some RCWs (we want limit these and derive as many as possible)
extern const std::vector< std::pair< uint8_t, rcw_settings> > RAW_CARDS;

}// rdimm

// Exposed so we can test them.
extern rcw_settings rdimm_rc_c1;
extern rcw_settings rdimm_rc_c2;
extern rcw_settings rdimm_rc_a1;
extern rcw_settings rdimm_rc_b1;
extern rcw_settings rdimm_rc_b2;
extern rcw_settings rdimm_rc_custom;
extern rcw_settings rdimm_rc_vbu;

}// mss

#endif //_MSS_RDIMM_RAW_CARDS_H_
<commit_msg>Adds DDR4 hybrid NV-RDIMM support<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/generic/memory/lib/spd/rdimm/ddr4/rdimm_raw_cards.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file raw_cards.H
/// @brief Raw card data structure
///
// *HWP HWP Owner: Andre Marin <aamarin@us.ibm.com>
// *HWP HWP Backup: Jacob Harvey <jlharvey@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 3
// *HWP Consumed by: HB:FSP

#ifndef _MSS_RDIMM_RAW_CARDS_H_
#define _MSS_RDIMM_RAW_CARDS_H_

#include <fapi2.H>
#include <cstdint>
#include <vector>
#include <generic/memory/lib/spd/common/rcw_settings.H>

namespace mss
{
namespace rdimm
{

///
/// @brief raw card encoding as per SPD
/// @note In the order of the vector below which needs to be sorted by enum value
///
enum raw_card_rev : uint8_t
{

    // TODO:RTC178807 - Update how NVDIMMs are handled once more are up and running in the lab
    // NVDIMM's have the same RC value as custom DIMMs, but are defined by SPD byte 03 differently
    NVDIMM = 0x1F,

    A1 = 0x20,
    B1 = 0x21,

    // RDIMM power-on
    C1 = 0x22,

    // TK - Change VBU value to a no-op value or a value that will never be reached -JLH
    VBU = 0x23,

    B2 = 0x41,
    C2 = 0x42,

    // Default settings used for DIMMs that do not use a JEDEC raw card reference
    CUSTOM = 0xFF,
};

// Raw cards can share the same raw card # between RDIMM and LRDIMMs so
// we track them independently. Since all of these don't come from SPD for DDR4,
// we have to set some RCWs (we want limit these and derive as many as possible)
extern const std::vector< std::pair< uint8_t, rcw_settings> > RAW_CARDS;

}// rdimm

// Exposed so we can test them.
// Alphabetized list below
extern rcw_settings rdimm_rc_a1;
extern rcw_settings rdimm_rc_b1;
extern rcw_settings rdimm_rc_b2;
extern rcw_settings rdimm_rc_c1;
extern rcw_settings rdimm_rc_c2;
extern rcw_settings rdimm_rc_custom;
extern rcw_settings rdimm_rc_nvdimm;
extern rcw_settings rdimm_rc_vbu;

}// mss

#endif //_MSS_RDIMM_RAW_CARDS_H_
<|endoftext|>