<commit_before>#include "SpriteBatch.h"
#include "../Objects/Object.h"
#include "../StarComponents.h"
#include "../Scenes/SceneManager.h"
#include "GraphicsManager.h"
#include "../Components/CameraComponent.h"
#include "../Objects/FreeCamera.h"
#include "../Scenes/BaseScene.h"
#include <algorithm>
#include "../Helpers/Math.h"
#include "ScaleSystem.h"

namespace star
{
	SpriteBatch * SpriteBatch::m_pSpriteBatch = nullptr;

	SpriteBatch::SpriteBatch(void):
		m_SpriteQueue(),
		m_TextBackQueue(),
		m_TextFrontQueue(),
		m_HUDTextQueue(),
		m_VertexBuffer(),
		m_UvCoordBuffer(),
		m_TextureSamplerID(0),
		m_ColorID(0),
		m_ShaderPtr(nullptr)
	{

	}

	SpriteBatch::~SpriteBatch(void)
	{
		delete m_ShaderPtr;
	}

	SpriteBatch * SpriteBatch::GetInstance()
	{
		if(m_pSpriteBatch == nullptr)
		{
			m_pSpriteBatch = new SpriteBatch();
			m_pSpriteBatch->Initialize();
		}
		return m_pSpriteBatch;
	}

	void SpriteBatch::Initialize()
	{
		//Set Shader and shader variables
#ifdef DESKTOP
		tstring vShader(_T("WinShaders/Texture_Batch_Shader.vert")),
				fShader(_T("WinShaders/Texture_Batch_Shader.frag"));
#else
		tstring vShader(_T("AndroidShaders/BatchTexShader.vert")),
				fShader(_T("AndroidShaders/BatchTexShader.frag"));
#endif
		m_ShaderPtr = new Shader();
		if(!m_ShaderPtr->Init(vShader, fShader))
		{
			Logger::GetInstance()->Log(star::LogLevel::Info, _T("Initialization of Spritebatch Shader has Failed!"));
		}

		m_TextureSamplerID = m_ShaderPtr->GetUniformLocation("textureSampler");
		m_ColorID = m_ShaderPtr->GetUniformLocation("colorMultiplier");
	}

	void SpriteBatch::Flush()
	{
		//Game drawn in negative
		//Game hUD drawn in positive
		Begin();
		//Sprites
		DrawSprites();
		//First background text
		for(auto& textDesc : m_TextFrontQueue)
		{
			FlushText(textDesc);
		}

		End();
	}

	void SpriteBatch::Begin()
	{
		m_ShaderPtr->Bind();

		//[TODO] Test android!
		glEnableVertexAttribArray(ATTRIB_VERTEX);
		glEnableVertexAttribArray(ATTRIB_UV);

		//Create Vertexbuffer
		CreateSpriteQuads();

		//Set uniforms
		glUniform1i(m_TextureSamplerID, 0);

		//more?
	}

	void SpriteBatch::DrawSprites()
	{
		uint32 batchStart(0);
		uint32 batchSize(0);
		GLuint texture(0);
		for(auto& currentSprite : m_SpriteQueue)
		{
			//If != -> Flush
			if(texture != currentSprite.textureID)
			{
				FlushSprites(batchStart, batchSize, texture);

				batchStart += batchSize;
				batchSize = 0;

				texture = currentSprite.textureID;
			}

			++batchSize;
		}
		FlushSprites(batchStart, batchSize, texture);
	}

	void SpriteBatch::FlushSprites(uint32 start, uint32 size, uint32 texture)
	{
		if(size > 0)
		{
			//[TODO] Check if this can be optimized
			glActiveTexture(GL_TEXTURE0);
			glBindTexture(GL_TEXTURE_2D, texture);

			//Set attributes and buffers
			glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT,0,0,
				reinterpret_cast<GLvoid*>(&m_VertexBuffer.at(0)));
			glVertexAttribPointer(ATTRIB_UV, 2, GL_FLOAT, 0, 0,
				reinterpret_cast<GLvoid*>(&m_UvCoordBuffer.at(0)));

			//[TODO] Change this, shouldn't be a uniform
			glUniform4f(
				m_ColorID,
				1.f,
				1.f,
				1.f,
				1.f
				);

			glDrawArrays(GL_TRIANGLES, start * 6, size * 6);
		}
	}

	void SpriteBatch::End()
	{
		//Unbind attributes and buffers
		glDisableVertexAttribArray(ATTRIB_VERTEX);
		glDisableVertexAttribArray(ATTRIB_UV);

		m_ShaderPtr->Unbind();

		m_SpriteQueue.clear();

		m_TextBackQueue.clear();
		m_TextFrontQueue.clear();
		m_HUDTextQueue.clear();

		m_VertexBuffer.clear();
		m_UvCoordBuffer.clear();
	}

	void SpriteBatch::FlushText(const TextDesc& textDesc)
	{
		FlushText(
			textDesc.Text,
			textDesc.Fontname,
			textDesc.VerticalSpacing,
			textDesc.HorizontalTextOffset,
			textDesc.TransformComp,
			textDesc.TextColor,
			textDesc.IsHUDText);
	}

	void SpriteBatch::FlushText(
		const tstring & text,
		const tstring& fontname,
		int32 spacing,
		const std::vector<int32> & horOffset,
		TransformComponent* transform,
		const Color& color,
		bool isHUD
		)
	{
		if(text.size() == 0)
		{
			Logger::GetInstance()->Log(LogLevel::Warning,
				_T("FontManager::DrawText: Drawing an empty string..."));
			return;
		}

		auto curfont = FontManager::GetInstance()->GetFont(fontname);
		float32 h = curfont.GetSize()/0.63f;
		const vec2& position = transform->GetWorldPosition().pos2D();
		const vec2& origposition = position;

		GLuint* textures = curfont.GetTextures();
		const std::vector<fontUvCoords>& tempuvs = curfont.GetUvCoords();
		const std::vector<fontVertices>& tempverts = curfont.GetVetrices();
		const std::vector<ivec2>& tempsizes = curfont.GetLetterDimensions();

		m_ShaderPtr->Bind();

		//Enable the attributes
		glEnableVertexAttribArray(ATTRIB_VERTEX);
		glEnableVertexAttribArray(ATTRIB_UV);

		glActiveTexture(GL_TEXTURE0);
		GLint s_textureId = glGetUniformLocation(m_ShaderPtr->GetID(), "textureSampler");
		glUniform1i(s_textureId, 0);
		GLint s_colorId = glGetUniformLocation(m_ShaderPtr->GetID(), "colorMultiplier");
		glUniform4f(s_colorId,color.r,color.g,color.b,color.a);

		float32 scaleValue = ScaleSystem::GetInstance()->GetScale();
		mat4 scaleMat = Scale(scaleValue, scaleValue, 1.0f);

		int32 line_counter(0);
		int32 offsetX(horOffset[line_counter]);
		int32 offsetY(0);

		const tchar *start_line = text.c_str();
		for(int32 i = 0 ; start_line[i] != 0 ; ++i)
		{
			if(start_line[i] == _T('\n'))
			{
				offsetY -= curfont.GetMaxLetterHeight() + spacing;
				++line_counter;
				offsetX = horOffset[line_counter];
			}
			else if(start_line[i] > 31) // is printable
			{
				glBindTexture(GL_TEXTURE_2D, textures[start_line[i]]);

				//Set attributes and buffers
				glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT,0,0,
					tempverts[start_line[i]].ver);
				glVertexAttribPointer(ATTRIB_UV, 2, GL_FLOAT, 0, 0,
					tempuvs[start_line[i]].uv);

				mat4 offsetTrans;

				if(start_line[i] != 0)
				{
					offsetTrans = Translate(
						vec3(
							offsetX,
							offsetY + tempsizes[start_line[i]].y,
							0
							)
						);
					offsetX += tempsizes[start_line[i]].x;
				}
				else
				{
					offsetTrans = Translate(0.0f, 0.0f, 0.0f);
				}
				const mat4& world = transform->GetWorldMatrix() * offsetTrans;

				glUniformMatrix4fv(glGetUniformLocation(m_ShaderPtr->GetID(),"MVP"),
					1,GL_FALSE,
					ToPointerValue(
						Transpose(world) *
						scaleMat *
						(isHUD ?
							GraphicsManager::GetInstance()->GetProjectionMatrix() :
							GraphicsManager::GetInstance()->GetViewProjectionMatrix()
							)
						)
					);
				glDrawArrays(GL_TRIANGLE_STRIP,0,4);
			}
		}

		//Unbind attributes and buffers
		glDisableVertexAttribArray(ATTRIB_VERTEX);
		glDisableVertexAttribArray(ATTRIB_UV);
		m_ShaderPtr->Unbind();
	}

	void SpriteBatch::CreateSpriteQuads()
	{
		//for every sprite that has to be drawn, push back all vertices
		//(VERTEX_AMOUNT per sprite) into the vertexbuffer and all uvcoords
		//(UV_AMOUNT per sprite) into the uvbuffer

		/*
		*  TL    TR
		*   0----1
		*   |   /|
		*   |  / |
		*   | /  |
		*   |/   |
		*   2----3
		*  BL    BR
		*/

		float32 scaleValue = ScaleSystem::GetInstance()->GetScale();
		mat4 scaleMat = Scale(scaleValue, scaleValue, 1.0f);

		for(auto& sprite : m_SpriteQueue)
		{
			//Push back all vertices

			mat4 transformMat = Transpose(sprite.transformPtr->GetWorldMatrix())
								* scaleMat
								* (sprite.bIsHud ?
									GraphicsManager::GetInstance()->GetProjectionMatrix() :
									GraphicsManager::GetInstance()->GetViewProjectionMatrix());

			//[TODO] Add depth!
			//[TODO] Check if this can be changed :(

			vec4 TL = vec4(0, sprite.vertices.y, 0, 1);
			Mul(TL, transformMat, TL);

			vec4 TR = vec4(sprite.vertices.x, sprite.vertices.y, 0, 1);
			Mul(TR, transformMat, TR);

			vec4 BL = vec4(0, 0, 0, 1);
			Mul(BL, transformMat, BL);

			vec4 BR = vec4(sprite.vertices.x, 0, 0, 1);
			Mul(BR, transformMat, BR);

			//0
			m_VertexBuffer.push_back(TL.x);
			m_VertexBuffer.push_back(TL.y);
			m_VertexBuffer.push_back(0);

			//1
			m_VertexBuffer.push_back(TR.x);
			m_VertexBuffer.push_back(TR.y);
			m_VertexBuffer.push_back(0);

			//2
			m_VertexBuffer.push_back(BL.x);
			m_VertexBuffer.push_back(BL.y);
			m_VertexBuffer.push_back(0);

			//1
			m_VertexBuffer.push_back(TR.x);
			m_VertexBuffer.push_back(TR.y);
			m_VertexBuffer.push_back(0);

			//3
			m_VertexBuffer.push_back(BR.x);
			m_VertexBuffer.push_back(BR.y);
			m_VertexBuffer.push_back(0);

			//2
			m_VertexBuffer.push_back(BL.x);
			m_VertexBuffer.push_back(BL.y);
			m_VertexBuffer.push_back(0);

			//Push back all uv's

			//0
			m_UvCoordBuffer.push_back(sprite.uvCoords.x);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y + sprite.uvCoords.w);

			//1
			m_UvCoordBuffer.push_back(sprite.uvCoords.x + sprite.uvCoords.z);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y + sprite.uvCoords.w);

			//2
			m_UvCoordBuffer.push_back(sprite.uvCoords.x);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y);

			//1
			m_UvCoordBuffer.push_back(sprite.uvCoords.x + sprite.uvCoords.z);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y + sprite.uvCoords.w);

			//3
			m_UvCoordBuffer.push_back(sprite.uvCoords.x + sprite.uvCoords.z);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y);

			//2
			m_UvCoordBuffer.push_back(sprite.uvCoords.x);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y);
		}
	}

	void SpriteBatch::AddSpriteToQueue(const SpriteInfo& spriteInfo)
	{
		m_SpriteQueue.push_back(spriteInfo);
	}

	void SpriteBatch::AddTextToQueue(const TextDesc& text, bool bInFrontOfSprites)
	{
		if(!bInFrontOfSprites)
		{
			m_TextBackQueue.push_back(text);
		}
		else
		{
			m_TextFrontQueue.push_back(text);
		}
	}
}
<commit_msg>Code cleanup<commit_after>#include "SpriteBatch.h"
#include "../Objects/Object.h"
#include "../StarComponents.h"
#include "../Scenes/SceneManager.h"
#include "GraphicsManager.h"
#include "../Components/CameraComponent.h"
#include "../Objects/FreeCamera.h"
#include "../Scenes/BaseScene.h"
#include <algorithm>
#include "../Helpers/Math.h"
#include "ScaleSystem.h"

namespace star
{
	SpriteBatch * SpriteBatch::m_pSpriteBatch = nullptr;

	SpriteBatch::SpriteBatch(void):
		m_SpriteQueue(),
		m_TextBackQueue(),
		m_TextFrontQueue(),
		m_HUDTextQueue(),
		m_VertexBuffer(),
		m_UvCoordBuffer(),
		m_TextureSamplerID(0),
		m_ColorID(0),
		m_ShaderPtr(nullptr)
	{

	}

	SpriteBatch::~SpriteBatch(void)
	{
		delete m_ShaderPtr;
	}

	SpriteBatch * SpriteBatch::GetInstance()
	{
		if(m_pSpriteBatch == nullptr)
		{
			m_pSpriteBatch = new SpriteBatch();
			m_pSpriteBatch->Initialize();
		}
		return m_pSpriteBatch;
	}

	void SpriteBatch::Initialize()
	{
		//Set Shader and shader variables
#ifdef DESKTOP
		tstring vShader(_T("WinShaders/Texture_Batch_Shader.vert")),
				fShader(_T("WinShaders/Texture_Batch_Shader.frag"));
#else
		tstring vShader(_T("AndroidShaders/BatchTexShader.vert")),
				fShader(_T("AndroidShaders/BatchTexShader.frag"));
#endif
		m_ShaderPtr = new Shader();
		if(!m_ShaderPtr->Init(vShader, fShader))
		{
			Logger::GetInstance()->Log(star::LogLevel::Info, _T("Initialization of Spritebatch Shader has Failed!"));
		}

		m_TextureSamplerID = m_ShaderPtr->GetUniformLocation("textureSampler");
		m_ColorID = m_ShaderPtr->GetUniformLocation("colorMultiplier");
	}

	void SpriteBatch::Flush()
	{
		//Game drawn in negative
		//Game hUD drawn in positive
		Begin();
		//Sprites
		DrawSprites();
		//First background text
		for(auto& textDesc : m_TextFrontQueue)
		{
			FlushText(textDesc);
		}

		End();
	}

	void SpriteBatch::Begin()
	{
		m_ShaderPtr->Bind();

		//[TODO] Test android!
		glEnableVertexAttribArray(ATTRIB_VERTEX);
		glEnableVertexAttribArray(ATTRIB_UV);

		//Create Vertexbuffer
		CreateSpriteQuads();

		//Set uniforms
		glUniform1i(m_TextureSamplerID, 0);

		//more?
	}

	void SpriteBatch::DrawSprites()
	{
		uint32 batchStart(0);
		uint32 batchSize(0);
		GLuint texture(0);
		for(auto& currentSprite : m_SpriteQueue)
		{
			//If != -> Flush
			if(texture != currentSprite.textureID)
			{
				FlushSprites(batchStart, batchSize, texture);

				batchStart += batchSize;
				batchSize = 0;

				texture = currentSprite.textureID;
			}

			++batchSize;
		}
		FlushSprites(batchStart, batchSize, texture);
	}

	void SpriteBatch::FlushSprites(uint32 start, uint32 size, uint32 texture)
	{
		if(size > 0)
		{
			//[TODO] Check if this can be optimized
			glActiveTexture(GL_TEXTURE0);
			glBindTexture(GL_TEXTURE_2D, texture);

			//Set attributes and buffers
			glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT,0,0,
				reinterpret_cast<GLvoid*>(&m_VertexBuffer.at(0)));
			glVertexAttribPointer(ATTRIB_UV, 2, GL_FLOAT, 0, 0,
				reinterpret_cast<GLvoid*>(&m_UvCoordBuffer.at(0)));

			//[TODO] Change this, shouldn't be a uniform
			glUniform4f(
				m_ColorID,
				1.f,
				1.f,
				1.f,
				1.f
				);

			glDrawArrays(GL_TRIANGLES, start * 6, size * 6);
		}
	}

	void SpriteBatch::End()
	{
		//Unbind attributes and buffers
		glDisableVertexAttribArray(ATTRIB_VERTEX);
		glDisableVertexAttribArray(ATTRIB_UV);

		m_ShaderPtr->Unbind();

		m_SpriteQueue.clear();

		m_TextBackQueue.clear();
		m_TextFrontQueue.clear();
		m_HUDTextQueue.clear();

		m_VertexBuffer.clear();
		m_UvCoordBuffer.clear();
	}

	void SpriteBatch::FlushText(const TextDesc& textDesc)
	{
		FlushText(
			textDesc.Text,
			textDesc.Fontname,
			textDesc.VerticalSpacing,
			textDesc.HorizontalTextOffset,
			textDesc.TransformComp,
			textDesc.TextColor,
			textDesc.IsHUDText);
	}

	void SpriteBatch::FlushText(
		const tstring & text,
		const tstring& fontname,
		int32 spacing,
		const std::vector<int32> & horOffset,
		TransformComponent* transform,
		const Color& color,
		bool isHUD
		)
	{
		if(text.size() == 0)
		{
			Logger::GetInstance()->Log(LogLevel::Warning,
				_T("FontManager::DrawText: Drawing an empty string..."));
			return;
		}

		auto curfont = FontManager::GetInstance()->GetFont(fontname);
		float32 h = curfont.GetSize()/0.63f;
		const vec2& position = transform->GetWorldPosition().pos2D();
		const vec2& origposition = position;

		GLuint* textures = curfont.GetTextures();
		const std::vector<fontUvCoords>& tempuvs = curfont.GetUvCoords();
		const std::vector<fontVertices>& tempverts = curfont.GetVetrices();
		const std::vector<ivec2>& tempsizes = curfont.GetLetterDimensions();

		m_ShaderPtr->Bind();

		//Enable the attributes
		glEnableVertexAttribArray(ATTRIB_VERTEX);
		glEnableVertexAttribArray(ATTRIB_UV);

		glActiveTexture(GL_TEXTURE0);
		GLint s_textureId = glGetUniformLocation(m_ShaderPtr->GetID(), "textureSampler");
		glUniform1i(s_textureId, 0);
		GLint s_colorId = glGetUniformLocation(m_ShaderPtr->GetID(), "colorMultiplier");
		glUniform4f(s_colorId,color.r,color.g,color.b,color.a);

		float32 scaleValue = ScaleSystem::GetInstance()->GetScale();
		mat4 scaleMat = Scale(scaleValue, scaleValue, 1.0f);

		int32 line_counter(0);
		int32 offsetX(horOffset[line_counter]);
		int32 offsetY(0);

		const tchar *start_line = text.c_str();
		for(int32 i = 0 ; start_line[i] != 0 ; ++i)
		{
			if(start_line[i] == _T('\n'))
			{
				offsetY -= curfont.GetMaxLetterHeight() + spacing;
				++line_counter;
				offsetX = horOffset[line_counter];
			}
			else if(start_line[i] > 31) // is printable
			{
				glBindTexture(GL_TEXTURE_2D, textures[start_line[i]]);

				//Set attributes and buffers
				glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT,0,0,
					tempverts[start_line[i]].ver);
				glVertexAttribPointer(ATTRIB_UV, 2, GL_FLOAT, 0, 0,
					tempuvs[start_line[i]].uv);

				mat4 offsetTrans;

				if(start_line[i] != 0)
				{
					offsetTrans = Translate(
						vec3(
							offsetX,
							offsetY + tempsizes[start_line[i]].y,
							0
							)
						);
					offsetX += tempsizes[start_line[i]].x;
				}
				else
				{
					offsetTrans = Translate(0.0f, 0.0f, 0.0f);
				}
				const mat4& world = transform->GetWorldMatrix() * offsetTrans;

				glUniformMatrix4fv(glGetUniformLocation(m_ShaderPtr->GetID(),"MVP"),
					1,GL_FALSE,
					ToPointerValue(
						Transpose(world) *
						scaleMat *
						(isHUD ?
							GraphicsManager::GetInstance()->GetProjectionMatrix() :
							GraphicsManager::GetInstance()->GetViewProjectionMatrix()
							)
						)
					);
				glDrawArrays(GL_TRIANGLE_STRIP,0,4);
			}
		}

		//Unbind attributes and buffers
		glDisableVertexAttribArray(ATTRIB_VERTEX);
		glDisableVertexAttribArray(ATTRIB_UV);
		m_ShaderPtr->Unbind();
	}

	void SpriteBatch::CreateSpriteQuads()
	{
		//for every sprite that has to be drawn, push back all vertices
		//(VERTEX_AMOUNT per sprite) into the vertexbuffer and all uvcoords
		//(UV_AMOUNT per sprite) into the uvbuffer

		/*
		*  TL    TR
		*   0----1
		*   |   /|
		*   |  / |
		*   | /  |
		*   |/   |
		*   2----3
		*  BL    BR
		*/

		float32 scaleValue = ScaleSystem::GetInstance()->GetScale();
		mat4 scaleMat = Scale(scaleValue, scaleValue, 1.0f);

		for(auto& sprite : m_SpriteQueue)
		{
			//Push back all vertices

			mat4 transformMat = Transpose(sprite.transformPtr->GetWorldMatrix())
								* scaleMat
								* (sprite.bIsHud ?
									GraphicsManager::GetInstance()->GetProjectionMatrix() :
									GraphicsManager::GetInstance()->GetViewProjectionMatrix());

			//[TODO] Add depth!
			//[TODO] Check if this can be changed :(

			vec4 TL = vec4(0, sprite.vertices.y, 0, 1);
			Mul(TL, transformMat, TL);

			vec4 TR = vec4(sprite.vertices.x, sprite.vertices.y, 0, 1);
			Mul(TR, transformMat, TR);

			vec4 BL = vec4(0, 0, 0, 1);
			Mul(BL, transformMat, BL);

			vec4 BR = vec4(sprite.vertices.x, 0, 0, 1);
			Mul(BR, transformMat, BR);

			//0
			m_VertexBuffer.push_back(TL.x);
			m_VertexBuffer.push_back(TL.y);
			m_VertexBuffer.push_back(0);

			//1
			m_VertexBuffer.push_back(TR.x);
			m_VertexBuffer.push_back(TR.y);
			m_VertexBuffer.push_back(0);

			//2
			m_VertexBuffer.push_back(BL.x);
			m_VertexBuffer.push_back(BL.y);
			m_VertexBuffer.push_back(0);

			//1
			m_VertexBuffer.push_back(TR.x);
			m_VertexBuffer.push_back(TR.y);
			m_VertexBuffer.push_back(0);

			//3
			m_VertexBuffer.push_back(BR.x);
			m_VertexBuffer.push_back(BR.y);
			m_VertexBuffer.push_back(0);

			//2
			m_VertexBuffer.push_back(BL.x);
			m_VertexBuffer.push_back(BL.y);
			m_VertexBuffer.push_back(0);

			//Push back all uv's

			//0
			m_UvCoordBuffer.push_back(sprite.uvCoords.x);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y + sprite.uvCoords.w);

			//1
			m_UvCoordBuffer.push_back(sprite.uvCoords.x + sprite.uvCoords.z);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y + sprite.uvCoords.w);

			//2
			m_UvCoordBuffer.push_back(sprite.uvCoords.x);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y);

			//1
			m_UvCoordBuffer.push_back(sprite.uvCoords.x + sprite.uvCoords.z);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y + sprite.uvCoords.w);

			//3
			m_UvCoordBuffer.push_back(sprite.uvCoords.x + sprite.uvCoords.z);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y);

			//2
			m_UvCoordBuffer.push_back(sprite.uvCoords.x);
			m_UvCoordBuffer.push_back(sprite.uvCoords.y);
		}
	}

	void SpriteBatch::AddSpriteToQueue(const SpriteInfo& spriteInfo)
	{
		m_SpriteQueue.push_back(spriteInfo);
	}

	void SpriteBatch::AddTextToQueue(const TextDesc& text, bool bInFrontOfSprites)
	{
		if(!bInFrontOfSprites)
		{
			m_TextBackQueue.push_back(text);
		}
		else
		{
			m_TextFrontQueue.push_back(text);
		}
	}
}
<|endoftext|>


<commit_before>/*
 * This file is part of TelepathyQt4
 *
 * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
 * Copyright (C) 2008 Nokia Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <TelepathyQt4/Client/PendingChannel>

#include "TelepathyQt4/Client/_gen/pending-channel.moc.hpp"

#include "TelepathyQt4/debug-internal.h"

#include <TelepathyQt4/Client/Channel>
#include <TelepathyQt4/Client/Connection>

/**
 * \addtogroup clientsideproxies Client-side proxies
 *
 * Proxy objects representing remote service objects accessed via D-Bus.
 *
 * In addition to providing direct access to methods, signals and properties
 * exported by the remote objects, some of these proxies offer features like
 * automatic inspection of remote object capabilities, property tracking,
 * backwards compatibility helpers for older services and other utilities.
 */

namespace Telepathy
{
namespace Client
{

struct PendingChannel::Private
{
    bool yours;
    QString channelType;
    uint handleType;
    uint handle;
    QDBusObjectPath objectPath;
};

/**
 * \class PendingChannel
 * \ingroup clientconn
 * \headerfile <TelepathyQt4/Client/pending-channel.h> <TelepathyQt4/Client/PendingChannel>
 *
 * Class containing the parameters of and the reply to an asynchronous channel
 * request. Instances of this class cannot be constructed directly; the only way
 * to get one is trough Connection.
 */

/**
 * Construct a new PendingChannel object that will fail.
 *
 * \param connection Connection to use.
 * \param errorName The error name.
 * \param errorMessage The error message.
 */
PendingChannel::PendingChannel(Connection *connection, const QString &errorName,
        const QString &errorMessage)
    : PendingOperation(connection),
      mPriv(new Private)
{
    mPriv->yours = false;
    mPriv->handleType = 0;
    mPriv->handle = 0;

    setFinishedWithError(errorName, errorMessage);
}

/**
 * Construct a new PendingChannel object.
 *
 * \param connection Connection to use.
 * \param request A dictionary containing the desirable properties.
 * \param create Whether createChannel or ensureChannel should be called.
 */
PendingChannel::PendingChannel(Connection *connection,
        const QVariantMap &request, bool create)
    : PendingOperation(connection),
      mPriv(new Private)
{
    mPriv->yours = create;
    mPriv->channelType = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
    mPriv->handleType = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
    mPriv->handle = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")).toUInt();

    if (create) {
        QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
                connection->requestsInterface()->CreateChannel(request), this);
        connect(watcher,
                SIGNAL(finished(QDBusPendingCallWatcher *)),
                SLOT(onCallCreateChannelFinished(QDBusPendingCallWatcher *)));
    }
    else {
        QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
                connection->requestsInterface()->EnsureChannel(request), this);
        connect(watcher,
                SIGNAL(finished(QDBusPendingCallWatcher *)),
                SLOT(onCallEnsureChannelFinished(QDBusPendingCallWatcher *)));
    }
}

/**
 * Class destructor.
 */
PendingChannel::~PendingChannel()
{
    delete mPriv;
}

/**
 * Return the Connection object through which the channel request was made.
 *
 * \return Pointer to the Connection.
 */
Connection *PendingChannel::connection() const
{
    return qobject_cast<Connection *>(parent());
}

/**
 * Return whether this channel belongs to this process.
 *
 * If false, the caller MUST assume that some other process is
 * handling this channel; if true, the caller SHOULD handle it
 * themselves or delegate it to another client.
 *
 * Note that the value is undefined until the operation finishes.
 *
 * \return Boolean indicating whether this channel belongs to this process.
 */
bool PendingChannel::yours() const
{
    return mPriv->yours;
}

/**
 * Return the channel type specified in the channel request.
 *
 * \return The D-Bus interface name of the interface specific to the
 *         requested channel type.
 */
const QString &PendingChannel::channelType() const
{
    return mPriv->channelType;
}

/**
 * Return the handle type specified in the channel request.
 *
 * \return The handle type, as specified in #HandleType.
 */
uint PendingChannel::handleType() const
{
    return mPriv->handleType;
}

/**
 * Return the handle specified in the channel request.
 *
 * \return The handle.
 */
uint PendingChannel::handle() const
{
    return mPriv->handle;
}

/**
 * Returns a newly constructed Channel high-level proxy object associated
 * with the remote channel resulting from the channel request. If isValid()
 * returns false, the request has not (at least yet) completed
 * successfully, and 0 will be returned.
 *
 * \param parent Passed to the Channel constructor.
 * \return Pointer to the new Channel object, 0 if an error occurred.
 */
Channel *PendingChannel::channel(QObject *parent) const
{
    if (!isFinished()) {
        warning() << "PendingChannel::channel called before finished, returning 0";
        return 0;
    } else if (!isValid()) {
        warning() << "PendingChannel::channel called when not valid, returning 0";
        return 0;
    }

    Channel *channel =
        new Channel(connection(),
                    mPriv->objectPath.path(),
                    parent);
    return channel;
}

void PendingChannel::onCallCreateChannelFinished(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<QDBusObjectPath, QVariantMap> reply = *watcher;

    if (!reply.isError()) {
        mPriv->objectPath = reply.argumentAt<0>();
        debug() << "Got reply to Connection.CreateChannel - object path:" <<
            mPriv->objectPath.path();

        QVariantMap map = reply.argumentAt<1>();
        mPriv->channelType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
        mPriv->handleType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
        mPriv->handle = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")).toUInt();

        setFinished();
    } else {
        debug().nospace() << "CreateChannel failed:" <<
            reply.error().name() << ": " << reply.error().message();
        setFinishedWithError(reply.error());
    }

    watcher->deleteLater();
}

void PendingChannel::onCallEnsureChannelFinished(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<bool, QDBusObjectPath, QVariantMap> reply = *watcher;

    if (!reply.isError()) {
        mPriv->yours = reply.argumentAt<0>();

        mPriv->objectPath = reply.argumentAt<1>();
        debug() << "Got reply to Connection.EnsureChannel - object path:" <<
            mPriv->objectPath.path();

        QVariantMap map = reply.argumentAt<2>();
        mPriv->channelType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
        mPriv->handleType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
        mPriv->handle = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")).toUInt();

        setFinished();
    } else {
        debug().nospace() << "EnsureChannel failed:" <<
            reply.error().name() << ": " << reply.error().message();
        setFinishedWithError(reply.error());
    }

    watcher->deleteLater();
}

} // Telepathy::Client
} // Telepathy
<commit_msg>Connection: Added more PendingChannel debug messages.<commit_after>/*
 * This file is part of TelepathyQt4
 *
 * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
 * Copyright (C) 2008 Nokia Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <TelepathyQt4/Client/PendingChannel>

#include "TelepathyQt4/Client/_gen/pending-channel.moc.hpp"

#include "TelepathyQt4/debug-internal.h"

#include <TelepathyQt4/Client/Channel>
#include <TelepathyQt4/Client/Connection>

/**
 * \addtogroup clientsideproxies Client-side proxies
 *
 * Proxy objects representing remote service objects accessed via D-Bus.
 *
 * In addition to providing direct access to methods, signals and properties
 * exported by the remote objects, some of these proxies offer features like
 * automatic inspection of remote object capabilities, property tracking,
 * backwards compatibility helpers for older services and other utilities.
 */

namespace Telepathy
{
namespace Client
{

struct PendingChannel::Private
{
    bool yours;
    QString channelType;
    uint handleType;
    uint handle;
    QDBusObjectPath objectPath;
};

/**
 * \class PendingChannel
 * \ingroup clientconn
 * \headerfile <TelepathyQt4/Client/pending-channel.h> <TelepathyQt4/Client/PendingChannel>
 *
 * Class containing the parameters of and the reply to an asynchronous channel
 * request. Instances of this class cannot be constructed directly; the only way
 * to get one is trough Connection.
 */

/**
 * Construct a new PendingChannel object that will fail.
 *
 * \param connection Connection to use.
 * \param errorName The error name.
 * \param errorMessage The error message.
 */
PendingChannel::PendingChannel(Connection *connection, const QString &errorName,
        const QString &errorMessage)
    : PendingOperation(connection),
      mPriv(new Private)
{
    mPriv->yours = false;
    mPriv->handleType = 0;
    mPriv->handle = 0;

    setFinishedWithError(errorName, errorMessage);
}

/**
 * Construct a new PendingChannel object.
 *
 * \param connection Connection to use.
 * \param request A dictionary containing the desirable properties.
 * \param create Whether createChannel or ensureChannel should be called.
 */
PendingChannel::PendingChannel(Connection *connection,
        const QVariantMap &request, bool create)
    : PendingOperation(connection),
      mPriv(new Private)
{
    mPriv->yours = create;
    mPriv->channelType = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
    mPriv->handleType = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
    mPriv->handle = request.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")).toUInt();

    if (create) {
        QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
                connection->requestsInterface()->CreateChannel(request), this);
        connect(watcher,
                SIGNAL(finished(QDBusPendingCallWatcher *)),
                SLOT(onCallCreateChannelFinished(QDBusPendingCallWatcher *)));
    }
    else {
        QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
                connection->requestsInterface()->EnsureChannel(request), this);
        connect(watcher,
                SIGNAL(finished(QDBusPendingCallWatcher *)),
                SLOT(onCallEnsureChannelFinished(QDBusPendingCallWatcher *)));
    }
}

/**
 * Class destructor.
 */
PendingChannel::~PendingChannel()
{
    delete mPriv;
}

/**
 * Return the Connection object through which the channel request was made.
 *
 * \return Pointer to the Connection.
 */
Connection *PendingChannel::connection() const
{
    return qobject_cast<Connection *>(parent());
}

/**
 * Return whether this channel belongs to this process.
 *
 * If false, the caller MUST assume that some other process is
 * handling this channel; if true, the caller SHOULD handle it
 * themselves or delegate it to another client.
 *
 * Note that the value is undefined until the operation finishes.
 *
 * \return Boolean indicating whether this channel belongs to this process.
 */
bool PendingChannel::yours() const
{
    if (!isFinished()) {
        warning() << "PendingChannel::yours called before finished, returning undefined value";
    }
    else if (!isValid()) {
        warning() << "PendingChannel::yours called when not valid, returning undefined value";
    }

    return mPriv->yours;
}

/**
 * Return the channel type specified in the channel request.
 *
 * \return The D-Bus interface name of the interface specific to the
 *         requested channel type.
 */
const QString &PendingChannel::channelType() const
{
    return mPriv->channelType;
}

/**
 * Return the handle type specified in the channel request.
 *
 * \return The handle type, as specified in #HandleType.
 */
uint PendingChannel::handleType() const
{
    return mPriv->handleType;
}

/**
 * Return the handle specified in the channel request.
 *
 * \return The handle.
 */
uint PendingChannel::handle() const
{
    return mPriv->handle;
}

/**
 * Returns a newly constructed Channel high-level proxy object associated
 * with the remote channel resulting from the channel request. If isValid()
 * returns false, the request has not (at least yet) completed
 * successfully, and 0 will be returned.
 *
 * \param parent Passed to the Channel constructor.
 * \return Pointer to the new Channel object, 0 if an error occurred.
 */
Channel *PendingChannel::channel(QObject *parent) const
{
    if (!isFinished()) {
        warning() << "PendingChannel::channel called before finished, returning 0";
        return 0;
    }
    else if (!isValid()) {
        warning() << "PendingChannel::channel called when not valid, returning 0";
        return 0;
    }

    Channel *channel =
        new Channel(connection(),
                    mPriv->objectPath.path(),
                    parent);
    return channel;
}

void PendingChannel::onCallCreateChannelFinished(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<QDBusObjectPath, QVariantMap> reply = *watcher;

    if (!reply.isError()) {
        mPriv->objectPath = reply.argumentAt<0>();
        debug() << "Got reply to Connection.CreateChannel - object path:" <<
            mPriv->objectPath.path();

        QVariantMap map = reply.argumentAt<1>();
        mPriv->channelType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
        mPriv->handleType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
        mPriv->handle = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")).toUInt();

        setFinished();
    } else {
        debug().nospace() << "CreateChannel failed:" <<
            reply.error().name() << ": " << reply.error().message();
        setFinishedWithError(reply.error());
    }

    watcher->deleteLater();
}

void PendingChannel::onCallEnsureChannelFinished(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<bool, QDBusObjectPath, QVariantMap> reply = *watcher;

    if (!reply.isError()) {
        mPriv->yours = reply.argumentAt<0>();

        mPriv->objectPath = reply.argumentAt<1>();
        debug() << "Got reply to Connection.EnsureChannel - object path:" <<
            mPriv->objectPath.path();

        QVariantMap map = reply.argumentAt<2>();
        mPriv->channelType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")).toString();
        mPriv->handleType = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")).toUInt();
        mPriv->handle = map.value(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")).toUInt();

        setFinished();
    } else {
        debug().nospace() << "EnsureChannel failed:" <<
            reply.error().name() << ": " << reply.error().message();
        setFinishedWithError(reply.error());
    }

    watcher->deleteLater();
}

} // Telepathy::Client
} // Telepathy
<|endoftext|>


<commit_before>/*
MIT License
Copyright (c) 2017 Arlen Keshabyan (arlen.albert@gmail.com)
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.
*/

#include <iostream>
#include "strings.hpp"
#include "string_id.hpp"
#include "platform_utilities.hpp"

int main()
{
    nstd::platform::utilities::scoped_console_utf8 set_console_utf8;

    using namespace std::string_literals;
    using namespace nstd::str;
    using namespace nstd::str::sid::literals;

    auto u8_to_str = [](const std::u8string &str) { return std::string(std::begin(str), std::end(str)); };

    std::u16string u16str(trim(u"    Hello World!      "));

    std::cout << u8"Всем привет :)" << std::endl;
    std::cout << trim("    Hello World!      ") << std::endl;
    std::wcout << trim(L"    Hello World!      ") << std::endl;
    std::cout << u8_to_str(from_utf16_to_utf8(u16str)) << std::endl;
    std::cout << u8_to_str(from_utf32_to_utf8(std::u32string(trim(U"    Hello World!      ")))) << std::endl;

    std::cout << u8_to_str(from_utf32_to_utf8(replace_all(U"good strings (UTF-32)"s, U"o"s, U"OO"s))) << std::endl;
    std::cout << replace_regex("good strings (string)"s, "o"s, "[$&]"s) << std::endl;
    std::wcout << replace_regex(L"good strings (wstring)"s, L"o"s, L"[$&]"s) << std::endl;

    auto str_container = { 1, 2, 3 };
    std::wcout << L"{" << join(str_container, L"}{"s) << L"}" << std::endl;

    auto str_to_split { L"let's  split       every  word   "s };

    std::wcout << "to split: '" << str_to_split << "'" << std::endl;

    auto splitted { split_regex(str_to_split, LR"(\s+)"s) };

    for (auto &&str : splitted) std::wcout << str << std::endl;

    std::wcout << "joined: '" << replace_all(join(splitted, L" "s), L"split"s, L"join"s) << "'" << std::endl;

    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws(""s)] << std::endl;
    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws(std::wstring{})] << std::endl;
    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws("   \n\t      "s)] << std::endl;
    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws(L"   \n\t      "s)] << std::endl;
    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws("   \n\t  ABC  "s)] << std::endl;
    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws(L"   \n\t ABC  "s)] << std::endl;

    auto nr { 3 };
    auto s { "'a const char array'" };
    auto true_val { true };
    std::cout << compose_string("\n", "The value is ", nr, " and another value is ", 8, "; string value is:\t", s, ";\nmax long double precision: ", std::numeric_limits<long double>::max_digits10,
                                            "; double: ", numeric_to_string(123.56789012L, std::numeric_limits<long double>::max_digits10),
                                            "\ninfinity: ", numeric_to_string(-std::numeric_limits<double>::infinity()), "\n", "\n", boolalpha[true_val], ", ",
                                            boolalpha[false]) << std::endl;

    std::cout << std::setprecision(std::numeric_limits<long double>::max_digits10) << wstring_to_numeric<long double>(L"123.56789012") << std::endl;

    // create database to store the strings in
    // it must stay valid as long as each string_id using it
    sid::default_database database;

    //=== string_id usage ===//
    // create an id
    sid::string_id sid("Test0815", database);
    std::cout << "Hash code " << sid.hash_code() << " belongs to string \"" << sid.string() << "\"\n";
    // Output (Database supports retrieving): Hash code 16741300784925887095 belongs to string "Test0815"
    // Output (Database doesn't): Hash code 16741300784925887095 belongs to string "string_id database disabled"

    sid::string_id a("Hello", database), b("World", database);

    // compare two ids
    std::cout << std::boolalpha << (a == b) << '\n';
    // Output: false

    // compare id with constant
    std::cout << (a == "Hello"_id) << '\n';
    // Output: true

    // literal is compile-time
    switch (b.hash_code())
    {
    case "Hello"_id:
        std::cout << "Hello\n";
        break;
    case "world"_id: // case-sensitive
        std::cout << "world\n";
        break;
    case "World"_id:
        std::cout << "World\n";
        break;
    }

    //=== generation ===//
    // the prefix for all generated ids
    sid::string_id prefix("entity-", database);
    try
    {
        // a generator type appending 8 random characters to the prefix
        // it uses the std::mt19937 generator for the actual generation
        typedef sid::random_generator<std::mt19937, 8> generator_t;
        // create a generator, seed the random number generator with the current time
        generator_t generator(prefix, std::mt19937(std::int_fast32_t(std::time(nullptr))));

        std::vector<sid::string_id> ids;
        for (auto i = 0; i != 10; ++i)
            // generate new identifier
            // it is guaranteed unique and will be stored in the database of the prefix
            ids.push_back(generator());

        // print the name of all the ids
        for (auto &id : ids)
            std::cout << id.string() << '\n';
        // possible generated name: entity-jXRnZAVG
    }
    catch (sid::generation_error &ex)
    {
        // the generator was unable to generate a new unique identifier (very unlikely)
        std::cerr << "[ERROR] " << ex.what() << '\n';
    }

    try
    {
        // a generator appending an increasing number to the prefix
        typedef sid::counter_generator generator_t;

        // create a generator starting with 0, each number will be 4 digits long
        generator_t generator(prefix, 0, 4);

        std::vector<sid::string_id> ids;
        for (auto i = 0; i != 10; ++i)
            // generate new identifier
            // it is guaranteed unique and will be stored in the database of the prefix
            ids.push_back(generator());

        // print the name of all the ids
        for (auto &id : ids)
            std::cout << id.string() << '\n';
        // possible generated name: entity-0006
    }
    catch (sid::generation_error &ex)
    {
        // the generator was unable to generate a new unique identifier (very unlikely)
        std::cerr << "[ERROR] " << ex.what() << '\n';
    }

    std::cout << "exitting..." << std::endl;

    return 0;
}
<commit_msg>u8 output fix<commit_after>/*
MIT License
Copyright (c) 2017 Arlen Keshabyan (arlen.albert@gmail.com)
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.
*/

#include <iostream>
#include "strings.hpp"
#include "string_id.hpp"
#include "platform_utilities.hpp"

int main()
{
    nstd::platform::utilities::scoped_console_utf8 set_console_utf8;

    using namespace std::string_literals;
    using namespace nstd::str;
    using namespace nstd::str::sid::literals;

    auto u8_to_str = [](const std::u8string &str) { return std::string(std::begin(str), std::end(str)); };

    std::u16string u16str(trim(u"    Hello World!      "));

    std::cout << u8_to_str(u8"Всем привет :)") << std::endl;
    std::cout << trim("    Hello World!      ") << std::endl;
    std::wcout << trim(L"    Hello World!      ") << std::endl;
    std::cout << u8_to_str(from_utf16_to_utf8(u16str)) << std::endl;
    std::cout << u8_to_str(from_utf32_to_utf8(std::u32string(trim(U"    Hello World!      ")))) << std::endl;

    std::cout << u8_to_str(from_utf32_to_utf8(replace_all(U"good strings (UTF-32)"s, U"o"s, U"OO"s))) << std::endl;
    std::cout << replace_regex("good strings (string)"s, "o"s, "[$&]"s) << std::endl;
    std::wcout << replace_regex(L"good strings (wstring)"s, L"o"s, L"[$&]"s) << std::endl;

    auto str_container = { 1, 2, 3 };
    std::wcout << L"{" << join(str_container, L"}{"s) << L"}" << std::endl;

    auto str_to_split { L"let's  split       every  word   "s };

    std::wcout << "to split: '" << str_to_split << "'" << std::endl;

    auto splitted { split_regex(str_to_split, LR"(\s+)"s) };

    for (auto &&str : splitted) std::wcout << str << std::endl;

    std::wcout << "joined: '" << replace_all(join(splitted, L" "s), L"split"s, L"join"s) << "'" << std::endl;

    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws(""s)] << std::endl;
    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws(std::wstring{})] << std::endl;
    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws("   \n\t      "s)] << std::endl;
    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws(L"   \n\t      "s)] << std::endl;
    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws("   \n\t  ABC  "s)] << std::endl;
    std::cout << "Is empty or white space: " << boolalpha[is_empty_or_ws(L"   \n\t ABC  "s)] << std::endl;

    auto nr { 3 };
    auto s { "'a const char array'" };
    auto true_val { true };
    std::cout << compose_string("\n", "The value is ", nr, " and another value is ", 8, "; string value is:\t", s, ";\nmax long double precision: ", std::numeric_limits<long double>::max_digits10,
                                            "; double: ", numeric_to_string(123.56789012L, std::numeric_limits<long double>::max_digits10),
                                            "\ninfinity: ", numeric_to_string(-std::numeric_limits<double>::infinity()), "\n", "\n", boolalpha[true_val], ", ",
                                            boolalpha[false]) << std::endl;

    std::cout << std::setprecision(std::numeric_limits<long double>::max_digits10) << wstring_to_numeric<long double>(L"123.56789012") << std::endl;

    // create database to store the strings in
    // it must stay valid as long as each string_id using it
    sid::default_database database;

    //=== string_id usage ===//
    // create an id
    sid::string_id sid("Test0815", database);
    std::cout << "Hash code " << sid.hash_code() << " belongs to string \"" << sid.string() << "\"\n";
    // Output (Database supports retrieving): Hash code 16741300784925887095 belongs to string "Test0815"
    // Output (Database doesn't): Hash code 16741300784925887095 belongs to string "string_id database disabled"

    sid::string_id a("Hello", database), b("World", database);

    // compare two ids
    std::cout << std::boolalpha << (a == b) << '\n';
    // Output: false

    // compare id with constant
    std::cout << (a == "Hello"_id) << '\n';
    // Output: true

    // literal is compile-time
    switch (b.hash_code())
    {
    case "Hello"_id:
        std::cout << "Hello\n";
        break;
    case "world"_id: // case-sensitive
        std::cout << "world\n";
        break;
    case "World"_id:
        std::cout << "World\n";
        break;
    }

    //=== generation ===//
    // the prefix for all generated ids
    sid::string_id prefix("entity-", database);
    try
    {
        // a generator type appending 8 random characters to the prefix
        // it uses the std::mt19937 generator for the actual generation
        typedef sid::random_generator<std::mt19937, 8> generator_t;
        // create a generator, seed the random number generator with the current time
        generator_t generator(prefix, std::mt19937(std::int_fast32_t(std::time(nullptr))));

        std::vector<sid::string_id> ids;
        for (auto i = 0; i != 10; ++i)
            // generate new identifier
            // it is guaranteed unique and will be stored in the database of the prefix
            ids.push_back(generator());

        // print the name of all the ids
        for (auto &id : ids)
            std::cout << id.string() << '\n';
        // possible generated name: entity-jXRnZAVG
    }
    catch (sid::generation_error &ex)
    {
        // the generator was unable to generate a new unique identifier (very unlikely)
        std::cerr << "[ERROR] " << ex.what() << '\n';
    }

    try
    {
        // a generator appending an increasing number to the prefix
        typedef sid::counter_generator generator_t;

        // create a generator starting with 0, each number will be 4 digits long
        generator_t generator(prefix, 0, 4);

        std::vector<sid::string_id> ids;
        for (auto i = 0; i != 10; ++i)
            // generate new identifier
            // it is guaranteed unique and will be stored in the database of the prefix
            ids.push_back(generator());

        // print the name of all the ids
        for (auto &id : ids)
            std::cout << id.string() << '\n';
        // possible generated name: entity-0006
    }
    catch (sid::generation_error &ex)
    {
        // the generator was unable to generate a new unique identifier (very unlikely)
        std::cerr << "[ERROR] " << ex.what() << '\n';
    }

    std::cout << "exitting..." << std::endl;

    return 0;
}
<|endoftext|>


<commit_before>#include "backenddata.h"

#include "debug.h"

#include "tasks-tarsnap.h"

BackendData::BackendData()
{
}

QMap<QString, JobPtr> BackendData::jobs()
{
    return _jobMap;
}

QMap<QString, ArchivePtr> BackendData::archives()
{
    return _archiveMap;
}

quint64 BackendData::numArchives()
{
    return static_cast<quint64>(_archiveMap.count());
}

bool BackendData::loadArchives()
{
    _archiveMap.clear();
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from archives")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(!global_store->runQuery(query))
    {
        DEBUG << "loadArchives query failed.";
        return false;
    }
    while(query.next())
    {
        ArchivePtr archive(new Archive);
        archive->setName(
            query.value(query.record().indexOf("name")).toString());
        archive->load();
        _archiveMap[archive->name()] = archive;
    }
    return true;
}

QList<ArchivePtr> BackendData::findMatchingArchives(const QString &jobPrefix)
{
    QList<ArchivePtr> matching;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->name().startsWith(jobPrefix + QChar('_'))
           && archive->jobRef().isEmpty())
            matching << archive;
    }
    return matching;
}

ArchivePtr BackendData::newArchive(BackupTaskDataPtr backupTaskData,
                                   bool              truncated)
{
    ArchivePtr archive(new Archive);
    archive->setName(backupTaskData->name());
    if(truncated)
    {
        archive->setName(archive->name().append(".part"));
        archive->setTruncated(true);
    }
    archive->setCommand(backupTaskData->command());
    // Lose milliseconds precision by converting to Unix timestamp and back.
    // So that a subsequent comparison in getArchiveListFinished won't fail.
    archive->setTimestamp(
        QDateTime::fromTime_t(backupTaskData->timestamp().toTime_t()));
    archive->setJobRef(backupTaskData->jobRef());
    // parseArchiveStats(stdErr, true, archive);
    archive->save();
    backupTaskData->setArchive(archive);
    _archiveMap.insert(archive->name(), archive);
    for(const JobPtr &job : _jobMap)
    {
        if(job->objectKey() == archive->jobRef())
            emit job->loadArchives();
    }
    return archive;
}

QList<ArchivePtr>
BackendData::setArchivesFromList(QList<struct archive_list_data> metadatas)
{
    QList<ArchivePtr> newArchives;

    QMap<QString, ArchivePtr> nextArchiveMap;
    for(const struct archive_list_data &metadata : metadatas)
    {
        ArchivePtr archive =
            _archiveMap.value(metadata.archiveName, ArchivePtr(new Archive));
        if(!archive->objectKey().isEmpty()
           && (archive->timestamp() != metadata.timestamp))
        {
            // There is a different archive with the same name on the remote
            archive->purge();
            archive.clear();
            archive = archive.create();
        }
        if(archive->objectKey().isEmpty())
        {
            // New archive
            archive->setName(metadata.archiveName);
            archive->setTimestamp(metadata.timestamp);
            archive->setCommand(metadata.command);
            // Automagically set Job ownership
            for(const JobPtr &job : _jobMap)
            {
                if(archive->name().startsWith(job->archivePrefix()))
                    archive->setJobRef(job->objectKey());
            }
            archive->save();
            newArchives.append(archive);
        }
        nextArchiveMap.insert(archive->name(), archive);
        _archiveMap.remove(archive->name());
    }
    // Purge archives left in old _archiveMap (not mirrored by the remote)
    for(const ArchivePtr &archive : _archiveMap)
    {
        archive->purge();
    }
    _archiveMap.clear();
    _archiveMap = nextArchiveMap;
    for(const JobPtr &job : _jobMap)
    {
        emit job->loadArchives();
    }
    return newArchives;
}

void BackendData::removeArchives(QList<ArchivePtr> archives)
{
    for(const ArchivePtr &archive : archives)
    {
        _archiveMap.remove(archive->name());
        archive->purge();
    }
}

bool BackendData::loadJobs()
{
    _jobMap.clear();
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from jobs")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(!global_store->runQuery(query))
    {
        DEBUG << "loadJobs query failed.";
        return false;
    }
    while(query.next())
    {
        JobPtr job(new Job);
        job->setName(query.value(query.record().indexOf("name")).toString());
        connect(job.data(), &Job::loadArchives, this,
                &BackendData::loadJobArchives);
        job->load();
        _jobMap[job->name()] = job;
    }
    return true;
}

void BackendData::deleteJob(JobPtr job)
{
    // Clear JobRef for assigned Archives.
    for(const ArchivePtr &archive : job->archives())
    {
        archive->setJobRef("");
        archive->save();
    }

    job->purge();
    _jobMap.remove(job->name());
}

void BackendData::loadJobArchives()
{
    Job *             job = qobject_cast<Job *>(sender());
    QList<ArchivePtr> archives;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->jobRef() == job->objectKey())
            archives << archive;
    }
    job->setArchives(archives);
}

void BackendData::addJob(JobPtr job)
{
    _jobMap[job->name()] = job;
    connect(job.data(), &Job::loadArchives, this,
            &BackendData::loadJobArchives);
}
<commit_msg>BackendData: simplify query processing<commit_after>#include "backenddata.h"

#include "debug.h"

#include "tasks-tarsnap.h"

BackendData::BackendData()
{
}

QMap<QString, JobPtr> BackendData::jobs()
{
    return _jobMap;
}

QMap<QString, ArchivePtr> BackendData::archives()
{
    return _archiveMap;
}

quint64 BackendData::numArchives()
{
    return static_cast<quint64>(_archiveMap.count());
}

bool BackendData::loadArchives()
{
    _archiveMap.clear();
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from archives")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(!global_store->runQuery(query))
    {
        DEBUG << "loadArchives query failed.";
        return false;
    }
    const int index = query.record().indexOf("name");
    while(query.next())
    {
        ArchivePtr archive(new Archive);
        archive->setName(query.value(index).toString());
        archive->load();
        _archiveMap[archive->name()] = archive;
    }
    return true;
}

QList<ArchivePtr> BackendData::findMatchingArchives(const QString &jobPrefix)
{
    QList<ArchivePtr> matching;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->name().startsWith(jobPrefix + QChar('_'))
           && archive->jobRef().isEmpty())
            matching << archive;
    }
    return matching;
}

ArchivePtr BackendData::newArchive(BackupTaskDataPtr backupTaskData,
                                   bool              truncated)
{
    ArchivePtr archive(new Archive);
    archive->setName(backupTaskData->name());
    if(truncated)
    {
        archive->setName(archive->name().append(".part"));
        archive->setTruncated(true);
    }
    archive->setCommand(backupTaskData->command());
    // Lose milliseconds precision by converting to Unix timestamp and back.
    // So that a subsequent comparison in getArchiveListFinished won't fail.
    archive->setTimestamp(
        QDateTime::fromTime_t(backupTaskData->timestamp().toTime_t()));
    archive->setJobRef(backupTaskData->jobRef());
    // parseArchiveStats(stdErr, true, archive);
    archive->save();
    backupTaskData->setArchive(archive);
    _archiveMap.insert(archive->name(), archive);
    for(const JobPtr &job : _jobMap)
    {
        if(job->objectKey() == archive->jobRef())
            emit job->loadArchives();
    }
    return archive;
}

QList<ArchivePtr>
BackendData::setArchivesFromList(QList<struct archive_list_data> metadatas)
{
    QList<ArchivePtr> newArchives;

    QMap<QString, ArchivePtr> nextArchiveMap;
    for(const struct archive_list_data &metadata : metadatas)
    {
        ArchivePtr archive =
            _archiveMap.value(metadata.archiveName, ArchivePtr(new Archive));
        if(!archive->objectKey().isEmpty()
           && (archive->timestamp() != metadata.timestamp))
        {
            // There is a different archive with the same name on the remote
            archive->purge();
            archive.clear();
            archive = archive.create();
        }
        if(archive->objectKey().isEmpty())
        {
            // New archive
            archive->setName(metadata.archiveName);
            archive->setTimestamp(metadata.timestamp);
            archive->setCommand(metadata.command);
            // Automagically set Job ownership
            for(const JobPtr &job : _jobMap)
            {
                if(archive->name().startsWith(job->archivePrefix()))
                    archive->setJobRef(job->objectKey());
            }
            archive->save();
            newArchives.append(archive);
        }
        nextArchiveMap.insert(archive->name(), archive);
        _archiveMap.remove(archive->name());
    }
    // Purge archives left in old _archiveMap (not mirrored by the remote)
    for(const ArchivePtr &archive : _archiveMap)
    {
        archive->purge();
    }
    _archiveMap.clear();
    _archiveMap = nextArchiveMap;
    for(const JobPtr &job : _jobMap)
    {
        emit job->loadArchives();
    }
    return newArchives;
}

void BackendData::removeArchives(QList<ArchivePtr> archives)
{
    for(const ArchivePtr &archive : archives)
    {
        _archiveMap.remove(archive->name());
        archive->purge();
    }
}

bool BackendData::loadJobs()
{
    _jobMap.clear();
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from jobs")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(!global_store->runQuery(query))
    {
        DEBUG << "loadJobs query failed.";
        return false;
    }
    const int index = query.record().indexOf("name");
    while(query.next())
    {
        JobPtr job(new Job);
        job->setName(query.value(index).toString());
        connect(job.data(), &Job::loadArchives, this,
                &BackendData::loadJobArchives);
        job->load();
        _jobMap[job->name()] = job;
    }
    return true;
}

void BackendData::deleteJob(JobPtr job)
{
    // Clear JobRef for assigned Archives.
    for(const ArchivePtr &archive : job->archives())
    {
        archive->setJobRef("");
        archive->save();
    }

    job->purge();
    _jobMap.remove(job->name());
}

void BackendData::loadJobArchives()
{
    Job *             job = qobject_cast<Job *>(sender());
    QList<ArchivePtr> archives;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->jobRef() == job->objectKey())
            archives << archive;
    }
    job->setArchives(archives);
}

void BackendData::addJob(JobPtr job)
{
    _jobMap[job->name()] = job;
    connect(job.data(), &Job::loadArchives, this,
            &BackendData::loadJobArchives);
}
<|endoftext|>


<commit_before>#include "backenddata.h"

#include "debug.h"

#include "tasks-tarsnap.h"

BackendData::BackendData()
{
}

QMap<QString, JobPtr> BackendData::jobs()
{
    return _jobMap;
}

QMap<QString, ArchivePtr> BackendData::archives()
{
    return _archiveMap;
}

quint64 BackendData::numArchives()
{
    return static_cast<quint64>(_archiveMap.count());
}

bool BackendData::loadArchives()
{
    _archiveMap.clear();
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from archives")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(global_store->runQuery(query) && query.next())
    {
        do
        {
            ArchivePtr archive(new Archive);
            archive->setName(
                query.value(query.record().indexOf("name")).toString());
            archive->load();
            _archiveMap[archive->name()] = archive;
        } while(query.next());
    }
    return true;
}

QList<ArchivePtr> BackendData::findMatchingArchives(const QString &jobPrefix)
{
    QList<ArchivePtr> matching;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->name().startsWith(jobPrefix + QChar('_'))
           && archive->jobRef().isEmpty())
            matching << archive;
    }
    return matching;
}

ArchivePtr BackendData::newArchive(BackupTaskDataPtr backupTaskData,
                                   bool              truncated)
{
    ArchivePtr archive(new Archive);
    archive->setName(backupTaskData->name());
    if(truncated)
    {
        archive->setName(archive->name().append(".part"));
        archive->setTruncated(true);
    }
    archive->setCommand(backupTaskData->command());
    // Lose milliseconds precision by converting to Unix timestamp and back.
    // So that a subsequent comparison in getArchiveListFinished won't fail.
    archive->setTimestamp(
        QDateTime::fromTime_t(backupTaskData->timestamp().toTime_t()));
    archive->setJobRef(backupTaskData->jobRef());
    // parseArchiveStats(stdErr, true, archive);
    archive->save();
    backupTaskData->setArchive(archive);
    _archiveMap.insert(archive->name(), archive);
    for(const JobPtr &job : _jobMap)
    {
        if(job->objectKey() == archive->jobRef())
            emit job->loadArchives();
    }
    return archive;
}

QList<ArchivePtr>
BackendData::setArchivesFromList(QList<struct archive_list_data> metadatas)
{
    QList<ArchivePtr> newArchives;

    QMap<QString, ArchivePtr> nextArchiveMap;
    for(const struct archive_list_data &metadata : metadatas)
    {
        ArchivePtr archive =
            _archiveMap.value(metadata.archiveName, ArchivePtr(new Archive));
        if(!archive->objectKey().isEmpty()
           && (archive->timestamp() != metadata.timestamp))
        {
            // There is a different archive with the same name on the remote
            archive->purge();
            archive.clear();
            archive = archive.create();
        }
        if(archive->objectKey().isEmpty())
        {
            // New archive
            archive->setName(metadata.archiveName);
            archive->setTimestamp(metadata.timestamp);
            archive->setCommand(metadata.command);
            // Automagically set Job ownership
            for(const JobPtr &job : _jobMap)
            {
                if(archive->name().startsWith(job->archivePrefix()))
                    archive->setJobRef(job->objectKey());
            }
            archive->save();
            newArchives.append(archive);
        }
        nextArchiveMap.insert(archive->name(), archive);
        _archiveMap.remove(archive->name());
    }
    // Purge archives left in old _archiveMap (not mirrored by the remote)
    for(const ArchivePtr &archive : _archiveMap)
    {
        archive->purge();
    }
    _archiveMap.clear();
    _archiveMap = nextArchiveMap;
    for(const JobPtr &job : _jobMap)
    {
        emit job->loadArchives();
    }
    return newArchives;
}

void BackendData::removeArchives(QList<ArchivePtr> archives)
{
    for(const ArchivePtr &archive : archives)
    {
        _archiveMap.remove(archive->name());
        archive->purge();
    }
}

bool BackendData::loadJobs()
{
    _jobMap.clear();
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from jobs")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(global_store->runQuery(query) && query.next())
    {
        do
        {
            JobPtr job(new Job);
            job->setName(
                query.value(query.record().indexOf("name")).toString());
            connect(job.data(), &Job::loadArchives, this,
                    &BackendData::loadJobArchives);
            job->load();
            _jobMap[job->name()] = job;
        } while(query.next());
    }
    return true;
}

void BackendData::deleteJob(JobPtr job)
{
    // Clear JobRef for assigned Archives.
    for(const ArchivePtr &archive : job->archives())
    {
        archive->setJobRef("");
        archive->save();
    }

    job->purge();
    _jobMap.remove(job->name());
}

void BackendData::loadJobArchives()
{
    Job *             job = qobject_cast<Job *>(sender());
    QList<ArchivePtr> archives;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->jobRef() == job->objectKey())
            archives << archive;
    }
    job->setArchives(archives);
}

void BackendData::addJob(JobPtr job)
{
    _jobMap[job->name()] = job;
    connect(job.data(), &Job::loadArchives, this,
            &BackendData::loadJobArchives);
}
<commit_msg>BackendData: separate query from processing<commit_after>#include "backenddata.h"

#include "debug.h"

#include "tasks-tarsnap.h"

BackendData::BackendData()
{
}

QMap<QString, JobPtr> BackendData::jobs()
{
    return _jobMap;
}

QMap<QString, ArchivePtr> BackendData::archives()
{
    return _archiveMap;
}

quint64 BackendData::numArchives()
{
    return static_cast<quint64>(_archiveMap.count());
}

bool BackendData::loadArchives()
{
    _archiveMap.clear();
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from archives")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(!global_store->runQuery(query))
    {
        DEBUG << "loadArchives query failed.";
        return false;
    }
    while(query.next())
    {
        ArchivePtr archive(new Archive);
        archive->setName(
            query.value(query.record().indexOf("name")).toString());
        archive->load();
        _archiveMap[archive->name()] = archive;
    }
    return true;
}

QList<ArchivePtr> BackendData::findMatchingArchives(const QString &jobPrefix)
{
    QList<ArchivePtr> matching;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->name().startsWith(jobPrefix + QChar('_'))
           && archive->jobRef().isEmpty())
            matching << archive;
    }
    return matching;
}

ArchivePtr BackendData::newArchive(BackupTaskDataPtr backupTaskData,
                                   bool              truncated)
{
    ArchivePtr archive(new Archive);
    archive->setName(backupTaskData->name());
    if(truncated)
    {
        archive->setName(archive->name().append(".part"));
        archive->setTruncated(true);
    }
    archive->setCommand(backupTaskData->command());
    // Lose milliseconds precision by converting to Unix timestamp and back.
    // So that a subsequent comparison in getArchiveListFinished won't fail.
    archive->setTimestamp(
        QDateTime::fromTime_t(backupTaskData->timestamp().toTime_t()));
    archive->setJobRef(backupTaskData->jobRef());
    // parseArchiveStats(stdErr, true, archive);
    archive->save();
    backupTaskData->setArchive(archive);
    _archiveMap.insert(archive->name(), archive);
    for(const JobPtr &job : _jobMap)
    {
        if(job->objectKey() == archive->jobRef())
            emit job->loadArchives();
    }
    return archive;
}

QList<ArchivePtr>
BackendData::setArchivesFromList(QList<struct archive_list_data> metadatas)
{
    QList<ArchivePtr> newArchives;

    QMap<QString, ArchivePtr> nextArchiveMap;
    for(const struct archive_list_data &metadata : metadatas)
    {
        ArchivePtr archive =
            _archiveMap.value(metadata.archiveName, ArchivePtr(new Archive));
        if(!archive->objectKey().isEmpty()
           && (archive->timestamp() != metadata.timestamp))
        {
            // There is a different archive with the same name on the remote
            archive->purge();
            archive.clear();
            archive = archive.create();
        }
        if(archive->objectKey().isEmpty())
        {
            // New archive
            archive->setName(metadata.archiveName);
            archive->setTimestamp(metadata.timestamp);
            archive->setCommand(metadata.command);
            // Automagically set Job ownership
            for(const JobPtr &job : _jobMap)
            {
                if(archive->name().startsWith(job->archivePrefix()))
                    archive->setJobRef(job->objectKey());
            }
            archive->save();
            newArchives.append(archive);
        }
        nextArchiveMap.insert(archive->name(), archive);
        _archiveMap.remove(archive->name());
    }
    // Purge archives left in old _archiveMap (not mirrored by the remote)
    for(const ArchivePtr &archive : _archiveMap)
    {
        archive->purge();
    }
    _archiveMap.clear();
    _archiveMap = nextArchiveMap;
    for(const JobPtr &job : _jobMap)
    {
        emit job->loadArchives();
    }
    return newArchives;
}

void BackendData::removeArchives(QList<ArchivePtr> archives)
{
    for(const ArchivePtr &archive : archives)
    {
        _archiveMap.remove(archive->name());
        archive->purge();
    }
}

bool BackendData::loadJobs()
{
    _jobMap.clear();
    if(!global_store->initialized())
    {
        DEBUG << "PersistentStore was not initialized properly.";
        return false;
    }
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from jobs")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    if(!global_store->runQuery(query))
    {
        DEBUG << "loadJobs query failed.";
        return false;
    }
    while(query.next())
    {
        JobPtr job(new Job);
        job->setName(query.value(query.record().indexOf("name")).toString());
        connect(job.data(), &Job::loadArchives, this,
                &BackendData::loadJobArchives);
        job->load();
        _jobMap[job->name()] = job;
    }
    return true;
}

void BackendData::deleteJob(JobPtr job)
{
    // Clear JobRef for assigned Archives.
    for(const ArchivePtr &archive : job->archives())
    {
        archive->setJobRef("");
        archive->save();
    }

    job->purge();
    _jobMap.remove(job->name());
}

void BackendData::loadJobArchives()
{
    Job *             job = qobject_cast<Job *>(sender());
    QList<ArchivePtr> archives;
    for(const ArchivePtr &archive : _archiveMap)
    {
        if(archive->jobRef() == job->objectKey())
            archives << archive;
    }
    job->setArchives(archives);
}

void BackendData::addJob(JobPtr job)
{
    _jobMap[job->name()] = job;
    connect(job.data(), &Job::loadArchives, this,
            &BackendData::loadJobArchives);
}
<|endoftext|>


<commit_before>#include <stan/math/prim/scal.hpp>
#include <gtest/gtest.h>

TEST(MathFunctions, distance) {
  double x1 = 1;
  double x2 = 4;

  EXPECT_FLOAT_EQ(3, stan::math::distance(x1, x2));
  EXPECT_FLOAT_EQ(3, stan::math::distance(x2, x1));
  EXPECT_FLOAT_EQ(0, stan::math::distance(x1, x1));
  EXPECT_FLOAT_EQ(0, stan::math::distance(x2, x2));
}

TEST(MathFunctions, distance_nan) {
  double x = 1;
  double nan = std::numeric_limits<double>::quiet_NaN();

  EXPECT_THROW(stan::math::distance(x, nan), std::domain_error);
  EXPECT_THROW(stan::math::distance(nan, x), std::domain_error);
  EXPECT_THROW(stan::math::distance(nan, nan), std::domain_error);
}

TEST(MathFunctions, distance_inf) {
  double x = 1;
  double inf = std::numeric_limits<double>::infinity();

  EXPECT_THROW(stan::math::distance(x, inf), std::domain_error);
  EXPECT_THROW(stan::math::distance(inf, x), std::domain_error);
  EXPECT_THROW(stan::math::distance(inf, inf), std::domain_error);
}
<commit_msg>added missing include<commit_after>#include <stan/math/prim/scal.hpp>
#include <gtest/gtest.h>
#include <limits>

TEST(MathFunctions, distance) {
  double x1 = 1;
  double x2 = 4;

  EXPECT_FLOAT_EQ(3, stan::math::distance(x1, x2));
  EXPECT_FLOAT_EQ(3, stan::math::distance(x2, x1));
  EXPECT_FLOAT_EQ(0, stan::math::distance(x1, x1));
  EXPECT_FLOAT_EQ(0, stan::math::distance(x2, x2));
}

TEST(MathFunctions, distance_nan) {
  double x = 1;
  double nan = std::numeric_limits<double>::quiet_NaN();

  EXPECT_THROW(stan::math::distance(x, nan), std::domain_error);
  EXPECT_THROW(stan::math::distance(nan, x), std::domain_error);
  EXPECT_THROW(stan::math::distance(nan, nan), std::domain_error);
}

TEST(MathFunctions, distance_inf) {
  double x = 1;
  double inf = std::numeric_limits<double>::infinity();

  EXPECT_THROW(stan::math::distance(x, inf), std::domain_error);
  EXPECT_THROW(stan::math::distance(inf, x), std::domain_error);
  EXPECT_THROW(stan::math::distance(inf, inf), std::domain_error);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2012-2013 BrewPi/Elco Jacobs.
 *
 * This file is part of BrewPi.
 *
 * BrewPi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * BrewPi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with BrewPi.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Brewpi.h"
#include "RotaryEncoder.h"

#include "Pins.h"
#include "util/atomic.h"
#include <limits.h>
#include "Ticks.h"
#include "Display.h"
#include "FastDigitalPin.h"
#include "Brewpi.h"
#include "TempControl.h"

RotaryEncoder rotaryEncoder;

int16_t RotaryEncoder::maximum;
int16_t RotaryEncoder::minimum;
volatile int16_t RotaryEncoder::steps;
volatile bool RotaryEncoder::pushFlag;

#if BREWPI_STATIC_CONFIG!=BREWPI_SHIELD_DIY
	#if rotarySwitchPin != 7
		#error Review interrupt vectors when not using pin 7 for menu push
	#endif
	#if rotaryAPin != 8
		#error Review interrupt vectors when not using pin 8 for menu right
	#endif
	#if rotaryBPin != 9
		#error Review interrupt vectors when not using pin 9 for menu left
	#endif
#else
	#if rotarySwitchPin != 0
	#error Review interrupt vectors when not using pin 0 for menu push
	#endif
	#if rotaryAPin != 2
	#error Review interrupt vectors when not using pin 2 for menu right
	#endif
	#if rotaryBPin != 1
	#error Review interrupt vectors when not using pin 1 for menu left
	#endif

#endif

// Implementation based on work of Ben Buxton:

/* Rotary encoder handler for arduino. v1.1
 *
 * Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3.
 * Contact: bb@cactii.net
 *
 * A typical mechanical rotary encoder emits a two bit gray code
 * on 3 output pins. Every step in the output (often accompanied
 * by a physical 'click') generates a specific sequence of output
 * codes on the pins.
 *
 * There are 3 pins used for the rotary encoding - one common and
 * two 'bit' pins.
 *
 * The following is the typical sequence of code on the output when
 * moving from one step to the next:
 *
 *   Position   Bit1   Bit2
 *   ----------------------
 *     Step1     0      0
 *      1/4      1      0
 *      1/2      1      1
 *      3/4      0      1
 *     Step2     0      0
 *
 * From this table, we can see that when moving from one 'click' to
 * the next, there are 4 changes in the output code.
 *
 * - From an initial 0 - 0, Bit1 goes high, Bit0 stays low.
 * - Then both bits are high, halfway through the step.
 * - Then Bit1 goes low, but Bit2 stays high.
 * - Finally at the end of the step, both bits return to 0.
 *
 * Detecting the direction is easy - the table simply goes in the other
 * direction (read up instead of down).
 *
 * To decode this, we use a simple state machine. Every time the output
 * code changes, it follows state, until finally a full steps worth of
 * code is received (in the correct order). At the final 0-0, it returns
 * a value indicating a step in one direction or the other.
 *
 * It's also possible to use 'half-step' mode. This just emits an event
 * at both the 0-0 and 1-1 positions. This might be useful for some
 * encoders where you want to detect all positions.
 *
 * If an invalid state happens (for example we go from '0-1' straight
 * to '1-0'), the state machine resets to the start until 0-0 and the
 * next valid codes occur.
 *
 * The biggest advantage of using a state machine over other algorithms
 * is that this has inherent debounce built in. Other algorithms emit spurious
 * output with switch bounce, but this one will simply flip between
 * sub-states until the bounce settles, then continue along the state
 * machine.
 * A side effect of debounce is that fast rotations can cause steps to
 * be skipped. By not requiring debounce, fast rotations can be accurately
 * measured.
 * Another advantage is the ability to properly handle bad state, such
 * as due to EMI, etc.
 * It is also a lot simpler than others - a static state table and less
 * than 10 lines of logic.
 */

/*
 * The below state table has, for each state (row), the new state
 * to set based on the next encoder output. From left to right in,
 * the table, the encoder outputs are 00, 01, 10, 11, and the value
 * in that position is the new state to set.
 */

#define R_START 0x0
// #define HALF_STEP

// Use the half-step state table (emits a code at 00 and 11)
#define HS_R_CCW_BEGIN 0x1
#define HS_R_CW_BEGIN 0x2
#define HS_R_START_M 0x3
#define HS_R_CW_BEGIN_M 0x4
#define HS_R_CCW_BEGIN_M 0x5
const uint8_t PROGMEM hs_ttable[7][4] = {
	// R_START (00)
	{HS_R_START_M,            HS_R_CW_BEGIN,     HS_R_CCW_BEGIN,  R_START},
	// HS_R_CCW_BEGIN
	{HS_R_START_M | DIR_CCW, R_START,        HS_R_CCW_BEGIN,  R_START},
	// HS_R_CW_BEGIN
	{HS_R_START_M | DIR_CW,  HS_R_CW_BEGIN,     R_START,      R_START},
	// HS_R_START_M (11)
	{HS_R_START_M,            HS_R_CCW_BEGIN_M,  HS_R_CW_BEGIN_M, R_START},
	// HS_R_CW_BEGIN_M
	{HS_R_START_M,            HS_R_START_M,      HS_R_CW_BEGIN_M, R_START | DIR_CW},
	// HS_R_CCW_BEGIN_M
	{HS_R_START_M,            HS_R_CCW_BEGIN_M,  HS_R_START_M,    R_START | DIR_CCW},
	{R_START, R_START, R_START, R_START}
};

// Use the full-step state table (emits a code at 00 only)
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6

const uint8_t PROGMEM ttable[7][4] = {
	// R_START
	{R_START,    R_CW_BEGIN,  R_CCW_BEGIN, R_START},
	// R_CW_FINAL
	{R_CW_NEXT,  R_START,     R_CW_FINAL,  R_START | DIR_CW},
	// R_CW_BEGIN
	{R_CW_NEXT,  R_CW_BEGIN,  R_START,     R_START},
	// R_CW_NEXT
	{R_CW_NEXT,  R_CW_BEGIN,  R_CW_FINAL,  R_START},
	// R_CCW_BEGIN
	{R_CCW_NEXT, R_START,     R_CCW_BEGIN, R_START},
	// R_CCW_FINAL
	{R_CCW_NEXT, R_CCW_FINAL, R_START,     R_START | DIR_CCW},
	// R_CCW_NEXT
	{R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};

#if BREWPI_ROTARY_ENCODER

#if BREWPI_STATIC_CONFIG==BREWPI_SHIELD_DIY
#if !defined(USBCON)
	#error DIY shield ONLY compatible with Leonardo board.
#endif

ISR(INT2_vect) {
	rotaryEncoder.setPushed();
}
#elif defined(USBCON)  // Arduino Leonardo
ISR(INT6_vect){
	rotaryEncoder.setPushed();
}
#else  // Arduino UNO or older
ISR(PCINT2_vect){
	if(!bitRead(PIND,7)){
		// high to low transition
		rotaryEncoder.setPushed();
	}
}
#endif

#if BREWPI_STATIC_CONFIG!=BREWPI_SHIELD_DIY
	ISR(PCINT0_vect){
		rotaryEncoder.process();
	}
#else
ISR(INT3_vect) {
	rotaryEncoder.process();
}
ISR(INT1_vect) {
	rotaryEncoder.process();
}
#endif

#endif  // BREWPI_ROTARY_ENCODER

void RotaryEncoder::process(void){
	static uint8_t state=R_START;
	// Grab state of input pins.
	#if defined(USBCON)
	// Arduino Leonardo
	uint8_t currPinA = !bitRead(PINB,4);
	uint8_t currPinB = !bitRead(PINB,5);
	#else
	uint8_t currPinA = bitRead(PINB,0);
	uint8_t currPinB = bitRead(PINB,1);
	#endif

	unsigned char pinstate = (currPinB << 1) | currPinA;

	// Determine new state from the pins and state table.
	if(tempControl.cc.rotaryHalfSteps){
		state = pgm_read_byte(&(hs_ttable[state & 0xf][pinstate]));
	}
	else{
		state = pgm_read_byte(&(ttable[state & 0xf][pinstate]));
	}

	// Get emit bits, ie the generated event.

	uint8_t dir = state & 0x30;

	if(dir){
		int16_t s = steps;	// steps is volatile - save a copy here to avoid multiple fetches
		s = (dir==DIR_CW) ? s+1 : s-1;
		if (s > maximum)
			s = minimum;
		else if (s < minimum)
			s = maximum;
		steps = s;
		display.resetBacklightTimer();
	}
}

void RotaryEncoder::setPushed(void){
	pushFlag = true;
	display.resetBacklightTimer();
}

void RotaryEncoder::init(void){
	#if(USE_INTERNAL_PULL_UP_RESISTORS)
	fastPinMode(rotaryAPin, INPUT_PULLUP);
	fastPinMode(rotaryBPin, INPUT_PULLUP);
	fastPinMode(rotarySwitchPin, INPUT_PULLUP);
	#else
	fastPinMode(rotaryAPin, INPUT);
	fastPinMode(rotaryBPin, INPUT);
	fastPinMode(rotarySwitchPin, INPUT);
	#endif

#if BREWPI_ROTARY_ENCODER
	#if BREWPI_STATIC_CONFIG==BREWPI_SHIELD_DIY
		EICRA |= (1<<ISC21) | (1<<ISC10) | (1<<ISC30);; // any logical change for encoder pins, falling edge for switch
		EIMSK |= (1<<INT2) | (1<<INT1) | (1<<INT3); // enable interrupts for each pin
	#elif defined(USBCON) // Arduino Leonardo
		// falling edge interrupt for switch on INT6
		EICRB |= (1<<ISC61) | (0<<ISC60);
		// enable interrupt for INT6
		EIMSK |= (1<<INT6);
		// enable pin change interrupts
		PCICR |= (1<<PCIE0);
		// enable pin change interrupt on Arduino pin 8 and 9
		PCMSK0 |= (1<<PCINT5) | (1<<PCINT4);
	#else // Arduino UNO
		// enable PCINT0 (PCINT0 and PCINT1 pin) and PCINT2 vector (PCINT23 pin)
		PCICR |= (1<<PCIE2) | (1<<PCIE0);
		// enable mask bits for PCINT0 and PCINT1
		PCMSK0 |= (1<<PCINT0) | (1<<PCINT1);
		// enable mask bit for PCINT23
		PCMSK2 |= (1<<PCINT23);
	#endif

#endif
}

void RotaryEncoder::setRange(int16_t start, int16_t minVal, int16_t maxVal){
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
		// this part cannot be interrupted
		// Multiply by two to convert to half steps
		steps = start;
		minimum = minVal;
		maximum = maxVal; // +1 to make sure that one step is still two half steps at overflow
	}
}

bool RotaryEncoder::changed(void){
	// returns one if the value changed since the last call of changed.
	static int16_t prevValue = 0;
	int16_t r = read();
	if(r != prevValue){
		prevValue = r;
		return 1;
	}
	if(pushFlag == true){
		return 1;
	}
	return 0;
}

int16_t RotaryEncoder::read(void){
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
		return steps;
	}
	return 0;
}
<commit_msg>reworked #define to disable rotary encoder<commit_after>/*
 * Copyright 2012-2013 BrewPi/Elco Jacobs.
 *
 * This file is part of BrewPi.
 *
 * BrewPi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * BrewPi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with BrewPi.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Brewpi.h"
#include "RotaryEncoder.h"

#include "Pins.h"
#include "util/atomic.h"
#include <limits.h>
#include "Ticks.h"
#include "Display.h"
#include "FastDigitalPin.h"
#include "Brewpi.h"
#include "TempControl.h"

RotaryEncoder rotaryEncoder;

int16_t RotaryEncoder::maximum;
int16_t RotaryEncoder::minimum;
volatile int16_t RotaryEncoder::steps;
volatile bool RotaryEncoder::pushFlag;

#if BREWPI_STATIC_CONFIG!=BREWPI_SHIELD_DIY
	#if rotarySwitchPin != 7
		#error Review interrupt vectors when not using pin 7 for menu push
	#endif
	#if rotaryAPin != 8
		#error Review interrupt vectors when not using pin 8 for menu right
	#endif
	#if rotaryBPin != 9
		#error Review interrupt vectors when not using pin 9 for menu left
	#endif
#else
	#if rotarySwitchPin != 0
	#error Review interrupt vectors when not using pin 0 for menu push
	#endif
	#if rotaryAPin != 2
	#error Review interrupt vectors when not using pin 2 for menu right
	#endif
	#if rotaryBPin != 1
	#error Review interrupt vectors when not using pin 1 for menu left
	#endif

#endif

// Implementation based on work of Ben Buxton:

/* Rotary encoder handler for arduino. v1.1
 *
 * Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3.
 * Contact: bb@cactii.net
 *
 * A typical mechanical rotary encoder emits a two bit gray code
 * on 3 output pins. Every step in the output (often accompanied
 * by a physical 'click') generates a specific sequence of output
 * codes on the pins.
 *
 * There are 3 pins used for the rotary encoding - one common and
 * two 'bit' pins.
 *
 * The following is the typical sequence of code on the output when
 * moving from one step to the next:
 *
 *   Position   Bit1   Bit2
 *   ----------------------
 *     Step1     0      0
 *      1/4      1      0
 *      1/2      1      1
 *      3/4      0      1
 *     Step2     0      0
 *
 * From this table, we can see that when moving from one 'click' to
 * the next, there are 4 changes in the output code.
 *
 * - From an initial 0 - 0, Bit1 goes high, Bit0 stays low.
 * - Then both bits are high, halfway through the step.
 * - Then Bit1 goes low, but Bit2 stays high.
 * - Finally at the end of the step, both bits return to 0.
 *
 * Detecting the direction is easy - the table simply goes in the other
 * direction (read up instead of down).
 *
 * To decode this, we use a simple state machine. Every time the output
 * code changes, it follows state, until finally a full steps worth of
 * code is received (in the correct order). At the final 0-0, it returns
 * a value indicating a step in one direction or the other.
 *
 * It's also possible to use 'half-step' mode. This just emits an event
 * at both the 0-0 and 1-1 positions. This might be useful for some
 * encoders where you want to detect all positions.
 *
 * If an invalid state happens (for example we go from '0-1' straight
 * to '1-0'), the state machine resets to the start until 0-0 and the
 * next valid codes occur.
 *
 * The biggest advantage of using a state machine over other algorithms
 * is that this has inherent debounce built in. Other algorithms emit spurious
 * output with switch bounce, but this one will simply flip between
 * sub-states until the bounce settles, then continue along the state
 * machine.
 * A side effect of debounce is that fast rotations can cause steps to
 * be skipped. By not requiring debounce, fast rotations can be accurately
 * measured.
 * Another advantage is the ability to properly handle bad state, such
 * as due to EMI, etc.
 * It is also a lot simpler than others - a static state table and less
 * than 10 lines of logic.
 */

/*
 * The below state table has, for each state (row), the new state
 * to set based on the next encoder output. From left to right in,
 * the table, the encoder outputs are 00, 01, 10, 11, and the value
 * in that position is the new state to set.
 */

#define R_START 0x0
// #define HALF_STEP

// Use the half-step state table (emits a code at 00 and 11)
#define HS_R_CCW_BEGIN 0x1
#define HS_R_CW_BEGIN 0x2
#define HS_R_START_M 0x3
#define HS_R_CW_BEGIN_M 0x4
#define HS_R_CCW_BEGIN_M 0x5
const uint8_t PROGMEM hs_ttable[7][4] = {
	// R_START (00)
	{HS_R_START_M,            HS_R_CW_BEGIN,     HS_R_CCW_BEGIN,  R_START},
	// HS_R_CCW_BEGIN
	{HS_R_START_M | DIR_CCW, R_START,        HS_R_CCW_BEGIN,  R_START},
	// HS_R_CW_BEGIN
	{HS_R_START_M | DIR_CW,  HS_R_CW_BEGIN,     R_START,      R_START},
	// HS_R_START_M (11)
	{HS_R_START_M,            HS_R_CCW_BEGIN_M,  HS_R_CW_BEGIN_M, R_START},
	// HS_R_CW_BEGIN_M
	{HS_R_START_M,            HS_R_START_M,      HS_R_CW_BEGIN_M, R_START | DIR_CW},
	// HS_R_CCW_BEGIN_M
	{HS_R_START_M,            HS_R_CCW_BEGIN_M,  HS_R_START_M,    R_START | DIR_CCW},
	{R_START, R_START, R_START, R_START}
};

// Use the full-step state table (emits a code at 00 only)
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6

const uint8_t PROGMEM ttable[7][4] = {
	// R_START
	{R_START,    R_CW_BEGIN,  R_CCW_BEGIN, R_START},
	// R_CW_FINAL
	{R_CW_NEXT,  R_START,     R_CW_FINAL,  R_START | DIR_CW},
	// R_CW_BEGIN
	{R_CW_NEXT,  R_CW_BEGIN,  R_START,     R_START},
	// R_CW_NEXT
	{R_CW_NEXT,  R_CW_BEGIN,  R_CW_FINAL,  R_START},
	// R_CCW_BEGIN
	{R_CCW_NEXT, R_START,     R_CCW_BEGIN, R_START},
	// R_CCW_FINAL
	{R_CCW_NEXT, R_CCW_FINAL, R_START,     R_START | DIR_CCW},
	// R_CCW_NEXT
	{R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};

#if BREWPI_ROTARY_ENCODER

#if BREWPI_STATIC_CONFIG==BREWPI_SHIELD_DIY
#if !defined(USBCON)
	#error DIY shield ONLY compatible with Leonardo board.
#endif

ISR(INT2_vect) {
	rotaryEncoder.setPushed();
}
#elif defined(USBCON)  // Arduino Leonardo
ISR(INT6_vect){
	rotaryEncoder.setPushed();
}
#else  // Arduino UNO or older
ISR(PCINT2_vect){
	if(!bitRead(PIND,7)){
		// high to low transition
		rotaryEncoder.setPushed();
	}
}
#endif

#if BREWPI_STATIC_CONFIG!=BREWPI_SHIELD_DIY
	ISR(PCINT0_vect){
		rotaryEncoder.process();
	}
#else
ISR(INT3_vect) {
	rotaryEncoder.process();
}
ISR(INT1_vect) {
	rotaryEncoder.process();
}
#endif

void RotaryEncoder::process(void){
	static uint8_t state=R_START;
	// Grab state of input pins.
	#if defined(USBCON)
	// Arduino Leonardo
	uint8_t currPinA = !bitRead(PINB,4);
	uint8_t currPinB = !bitRead(PINB,5);
	#else
	uint8_t currPinA = bitRead(PINB,0);
	uint8_t currPinB = bitRead(PINB,1);
	#endif

	unsigned char pinstate = (currPinB << 1) | currPinA;

	// Determine new state from the pins and state table.
	if(tempControl.cc.rotaryHalfSteps){
		state = pgm_read_byte(&(hs_ttable[state & 0xf][pinstate]));
	}
	else{
		state = pgm_read_byte(&(ttable[state & 0xf][pinstate]));
	}

	// Get emit bits, ie the generated event.

	uint8_t dir = state & 0x30;

	if(dir){
		int16_t s = steps;	// steps is volatile - save a copy here to avoid multiple fetches
		s = (dir==DIR_CW) ? s+1 : s-1;
		if (s > maximum)
			s = minimum;
		else if (s < minimum)
			s = maximum;
		steps = s;
		display.resetBacklightTimer();
	}
}
#endif  // BREWPI_ROTARY_ENCODER

void RotaryEncoder::setPushed(void){
	pushFlag = true;
	display.resetBacklightTimer();
}

void RotaryEncoder::init(void){
#if BREWPI_ROTARY_ENCODER
	#if(USE_INTERNAL_PULL_UP_RESISTORS)
	fastPinMode(rotaryAPin, INPUT_PULLUP);
	fastPinMode(rotaryBPin, INPUT_PULLUP);
	fastPinMode(rotarySwitchPin, INPUT_PULLUP);
	#else
	fastPinMode(rotaryAPin, INPUT);
	fastPinMode(rotaryBPin, INPUT);
	fastPinMode(rotarySwitchPin, INPUT);
	#endif

	#if BREWPI_STATIC_CONFIG==BREWPI_SHIELD_DIY
		EICRA |= (1<<ISC21) | (1<<ISC10) | (1<<ISC30);; // any logical change for encoder pins, falling edge for switch
		EIMSK |= (1<<INT2) | (1<<INT1) | (1<<INT3); // enable interrupts for each pin
	#elif defined(USBCON) // Arduino Leonardo
		// falling edge interrupt for switch on INT6
		EICRB |= (1<<ISC61) | (0<<ISC60);
		// enable interrupt for INT6
		EIMSK |= (1<<INT6);
		// enable pin change interrupts
		PCICR |= (1<<PCIE0);
		// enable pin change interrupt on Arduino pin 8 and 9
		PCMSK0 |= (1<<PCINT5) | (1<<PCINT4);
	#else // Arduino UNO
		// enable PCINT0 (PCINT0 and PCINT1 pin) and PCINT2 vector (PCINT23 pin)
		PCICR |= (1<<PCIE2) | (1<<PCIE0);
		// enable mask bits for PCINT0 and PCINT1
		PCMSK0 |= (1<<PCINT0) | (1<<PCINT1);
		// enable mask bit for PCINT23
		PCMSK2 |= (1<<PCINT23);
	#endif
#endif
}

void RotaryEncoder::setRange(int16_t start, int16_t minVal, int16_t maxVal){
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
		// this part cannot be interrupted
		// Multiply by two to convert to half steps
		steps = start;
		minimum = minVal;
		maximum = maxVal; // +1 to make sure that one step is still two half steps at overflow
	}
}

bool RotaryEncoder::changed(void){
	// returns one if the value changed since the last call of changed.
	static int16_t prevValue = 0;
	int16_t r = read();
	if(r != prevValue){
		prevValue = r;
		return 1;
	}
	if(pushFlag == true){
		return 1;
	}
	return 0;
}

int16_t RotaryEncoder::read(void){
#if BREWPI_ROTARY_ENCODER
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
		return steps;
	}
#endif
	return 0;
}
<|endoftext|>