<commit_before>/****************************************************************************

 This file is part of the GLC-lib library.
 Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net)
 Version 0.9.9, packaged on May, 2008.

 http://glc-lib.sourceforge.net

 GLC-lib 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 2 of the License, or
 (at your option) any later version.

 GLC-lib 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 GLC-lib; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*****************************************************************************/

//! \file glc_3dstoworld.cpp implementation of the GLC_3dsToWorld class.

#include "glc_3dstoworld.h"
#include <QFileInfo>
#include <QGLContext>

#include "glc_mesh2.h"
#include "glc_world.h"
#include "glc_fileformatexception.h"
#include "glc_circle.h"
#include "glc_material.h"
// Lib3ds Header
#include "lib3ds/file.h"
#include "lib3ds/mesh.h"
#include "lib3ds/node.h"
#include "lib3ds/matrix.h"
#include "lib3ds/material.h"

GLC_3dsToWorld::GLC_3dsToWorld(const QGLContext *pContext)
: m_pWorld(NULL)
, m_FileName()
, m_pQGLContext(pContext)
, m_pCurrentMesh(NULL)
, m_CurVertexIndex(0)
, m_CurNormalIndex(0)
, m_pLib3dsFile(NULL)
, m_Materials()
, m_MaterialsIndex()
, m_NextMaterialIndex(0)
, m_pCurrentMaterial(NULL)
{
}

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

// Create an GLC_World from an input 3DS File
GLC_World* GLC_3dsToWorld::CreateWorldFrom3ds(QFile &file)
{
	clear();
	m_FileName= file.fileName();
	//////////////////////////////////////////////////////////////////
	// Test if the file exist and can be opened
	//////////////////////////////////////////////////////////////////
	if (!file.open(QIODevice::ReadOnly))
	{
		QString message(QString("GLC_3dsToWorld::CreateWorldFrom3ds File ") + m_FileName + QString(" doesn't exist"));
		GLC_FileFormatException fileFormatException(message, m_FileName);
		throw(fileFormatException);
	}
	//////////////////////////////////////////////////////////////////
	// Init member
	//////////////////////////////////////////////////////////////////
	m_pWorld= new GLC_World;

	//Load 3ds File
	m_pLib3dsFile=lib3ds_file_load(m_FileName.toStdString().c_str());
	if (!m_pLib3dsFile)
	{
		QString message= "GLC_3dsToWorld::CreateWorldFrom3ds : Loading Failed";
		GLC_FileFormatException fileFormatException(message, m_FileName);
		clear();
		throw(fileFormatException);
	}

	/* No nodes?  Fabricate nodes to display all the meshes. */
	if(!m_pLib3dsFile->nodes)
	{
		qDebug() << "No nodes";
		Lib3dsMesh *pMesh;
		Lib3dsNode *pNode;

		for(pMesh= m_pLib3dsFile->meshes; pMesh != NULL; pMesh = pMesh->next)
		{
			pNode= lib3ds_node_new_object();
			strcpy(pNode->name, pMesh->name);
			pNode->parent_id= LIB3DS_NO_PARENT;
			lib3ds_file_insert_node(m_pLib3dsFile, pNode);
		}
	  }
	  // Create GLC_Instance with Node
	   for (Lib3dsNode *pNode=m_pLib3dsFile->nodes; pNode!=0; pNode=pNode->next)
	   {
		   createMeshes(m_pWorld->rootProduct(), pNode);
	   }

	// Free Lib3dsFile and all its ressources
	lib3ds_file_free(m_pLib3dsFile);
	m_pLib3dsFile= NULL;
	if (m_pWorld->collection()->isEmpty())
	{
		QString message= "GLC_3dsToWorld::CreateWorldFrom3ds : No mesh found !";
		GLC_FileFormatException fileFormatException(message, m_FileName);
		clear();
		throw(fileFormatException);

	}

	return m_pWorld;
}

/////////////////////////////////////////////////////////////////////
// Private services Functions
//////////////////////////////////////////////////////////////////////

// clear stlToWorld allocate memmory and reset member
void GLC_3dsToWorld::clear()
{
	if (NULL != m_pCurrentMesh)
	{
		delete m_pCurrentMesh;
		m_pCurrentMesh= NULL;
	}
	m_pWorld= NULL;
	m_FileName.clear();
	m_pCurrentMesh= NULL;
	m_CurVertexIndex= 0;
	m_CurNormalIndex= 0;
	if (NULL != m_pLib3dsFile)
	{
		lib3ds_file_free(m_pLib3dsFile);
		m_pLib3dsFile= NULL;
	}

	// Remove unused material
	QHash<QString, GLC_Material*>::iterator i;
	for (i= m_Materials.begin(); i != m_Materials.end(); ++i)
	{
		if (i.value()->isUnused()) delete i.value();
	}
	m_Materials.clear();
	// Clear the material index hash table
	m_MaterialsIndex.clear();
	m_NextMaterialIndex= 0;
	// Set the current material to NULL
	m_pCurrentMaterial= NULL;

}

// Create meshes from the 3ds File
void GLC_3dsToWorld::createMeshes(GLC_Product* pProduct, Lib3dsNode* pFatherNode)
{
	GLC_Product* pChildProduct= NULL;
	Lib3dsMesh *pMesh= NULL;

	if (pFatherNode->type == LIB3DS_OBJECT_NODE)
	{
		// Check if the node is a mesh or dummy
		if (strcmp(pFatherNode->name,"$$$DUMMY")==0)
		{
			return;
		}
		pMesh = lib3ds_file_mesh_by_name(m_pLib3dsFile, pFatherNode->data.object.morph);
	    if( pMesh == NULL )
	    {
	    	pMesh = lib3ds_file_mesh_by_name(m_pLib3dsFile, pFatherNode->name);
	    }

	    GLC_Instance instance(createInstance(pMesh));
	    pProduct->addChildPart(instance);
	}

	// If there is a child, create a child product
	if (NULL != pFatherNode->childs)
	{
		pChildProduct= pProduct->addNewChildProduct();
		if (NULL != pMesh)
		{
			Lib3dsMatrix M;
			lib3ds_matrix_copy(M, pMesh->matrix);
			lib3ds_matrix_inv(M);

			GLC_Matrix4x4 mat(&M[0][0]);

			//pChildProduct->move(mat);
		}
	}
	// Create Childs meshes if exists
	for (Lib3dsNode* pNode= pFatherNode->childs; pNode!=0; pNode= pNode->next)
	{
		createMeshes(pChildProduct, pNode);
	}

}
//! Create Instance from a Lib3dsNode
GLC_Instance GLC_3dsToWorld::createInstance(Lib3dsMesh* p3dsMesh)
{
	GLC_Mesh2 * pMesh= new GLC_Mesh2();
	pMesh->setName(p3dsMesh->name);
	// The mesh normals
	const int normalsNumber= p3dsMesh->faces * 3;
	Lib3dsVector *normalL= static_cast<Lib3dsVector*>(malloc(normalsNumber * sizeof(Lib3dsVector)));
	lib3ds_mesh_calculate_normals(p3dsMesh, normalL);
	// Add the normal to the mesh
	for (int i= 0; i < normalsNumber; ++i)
	{
		pMesh->addNormal(i, GLC_Vector3d(normalL[i][0], normalL[i][1], normalL[i][2]));
	}
	delete normalL;

	// Add Vertex to the mesh
	const int vertexNumber= p3dsMesh->points;
	for (int i= 0; i < vertexNumber; ++i)
	{
		pMesh->addVertex(i, GLC_Vector3d(p3dsMesh->pointL[i].pos[0], p3dsMesh->pointL[i].pos[1], p3dsMesh->pointL[i].pos[2]));
	}

	int normalIndex= 0;
	int textureIndex= 0;

	for (unsigned int i= 0; i < p3dsMesh->faces; ++i)
	{
		//Add the Normal
		QVector<int> normal;
		normal.append(normalIndex++);
		normal.append(normalIndex++);
		normal.append(normalIndex++);
		QVector<int> vertex;
		Lib3dsFace *p3dsFace=&p3dsMesh->faceL[i];
		// Load the material
		// The material current face index
		QVector<int> material;

		if (p3dsFace->material[0])
		{
			Lib3dsMaterial* p3dsMat=lib3ds_file_material_by_name(m_pLib3dsFile, p3dsFace->material);
			// Check it this material as already been loaded
			const QString materialName(p3dsFace->material);
			if (!m_Materials.contains(materialName))
			{ // Material not already loaded, load it
				loadMaterial(p3dsMat);
				// Add the loaded material to the mesh
				pMesh->addMaterial(m_MaterialsIndex[materialName], m_Materials[materialName]);
			}
			// Set the current material
			m_pCurrentMaterial= m_Materials[materialName];

			const int index= m_MaterialsIndex[materialName];
			material << index << index << index;
		}
		else // No material
		{
			if (NULL != m_pCurrentMaterial)
			{
				const int index= m_MaterialsIndex[m_pCurrentMaterial->getName()];
				pMesh->addMaterial(index, m_pCurrentMaterial);
				material << index << index << index;
			}
			else
			{
				material << -1 << -1 << -1;
			}
		}
		// End of loading material
		vertex << p3dsFace->points[0] << p3dsFace->points[1] << p3dsFace->points[2];
		// check if the mesh have texture coordinate
		if (p3dsMesh->texels > 0)
		{
			QVector<int> texture;
			for (int i= 0; i < 3; ++i)
			{
				// Load texture coordinate
				float x= p3dsMesh->texelL[p3dsFace->points[i]][0];
				float y= p3dsMesh->texelL[p3dsFace->points[i]][1];
				GLC_Vector2d texel(static_cast<double>(x), static_cast<double>(y));
				texture.append(textureIndex);
				pMesh->addTextureCoordinate(textureIndex++, texel);
			}
			pMesh->addFace(material, vertex, normal, texture);

		}
		else
		{
			pMesh->addFace(material, vertex, normal);
		}

	}
	return GLC_Instance(pMesh);
}

// Load Material
void GLC_3dsToWorld::loadMaterial(Lib3dsMaterial* p3dsMaterial)
{
	GLC_Material* pMaterial= new GLC_Material;
	// Set the material name
	const QString materialName(p3dsMaterial->name);
	pMaterial->setName(materialName);
	// Check if there is a texture
	if (p3dsMaterial->texture1_map.name[0])
	{
		const QString textureName(p3dsMaterial->texture1_map.name);
		// Retrieve the .3ds file path
		QFileInfo fileInfo(m_FileName);
		QString textureFileName(fileInfo.absolutePath() + QDir::separator());
		textureFileName.append(textureName);
		QFile textureFile(textureFileName);

		if (textureFile.open(QIODevice::ReadOnly))
		{
			// Create the texture and assign it to the material
			GLC_Texture *pTexture = new GLC_Texture(m_pQGLContext, textureFile);
			pMaterial->setTexture(pTexture);
		}
		textureFile.close();
	}
	// Ambient Color
	QColor ambient;
	ambient.setRgbF(p3dsMaterial->ambient[0], p3dsMaterial->ambient[1], p3dsMaterial->ambient[2]);
	ambient.setAlphaF(p3dsMaterial->ambient[3]);
	pMaterial->setAmbientColor(ambient);
	// Diffuse Color
	QColor diffuse;
	diffuse.setRgbF(p3dsMaterial->diffuse[0], p3dsMaterial->diffuse[1], p3dsMaterial->diffuse[2]);
	diffuse.setAlphaF(p3dsMaterial->diffuse[3]);
	pMaterial->setDiffuseColor(diffuse);
	// Specular Color
	QColor specular;
	specular.setRgbF(p3dsMaterial->specular[0], p3dsMaterial->specular[1], p3dsMaterial->specular[2]);
	specular.setAlphaF(p3dsMaterial->specular[3]);
	pMaterial->setSpecularColor(specular);
	// Shininess
	//pMaterial->setShininess(pow(2, 10.0 * p3dsMaterial->shininess));
	// Transparency
	pMaterial->setTransparency(1.0 - p3dsMaterial->transparency);

	// Add the material to the hash table
	m_Materials.insert(materialName, pMaterial);
	// Add the material index
	m_MaterialsIndex.insert(materialName, m_NextMaterialIndex++);
}

<commit_msg>Fix Node position matrix bug.<commit_after>/****************************************************************************

 This file is part of the GLC-lib library.
 Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net)
 Version 0.9.9, packaged on May, 2008.

 http://glc-lib.sourceforge.net

 GLC-lib 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 2 of the License, or
 (at your option) any later version.

 GLC-lib 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 GLC-lib; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*****************************************************************************/

//! \file glc_3dstoworld.cpp implementation of the GLC_3dsToWorld class.

#include "glc_3dstoworld.h"
#include <QFileInfo>
#include <QGLContext>

#include "glc_mesh2.h"
#include "glc_world.h"
#include "glc_fileformatexception.h"
#include "glc_circle.h"
#include "glc_material.h"
// Lib3ds Header
#include "lib3ds/file.h"
#include "lib3ds/mesh.h"
#include "lib3ds/node.h"
#include "lib3ds/matrix.h"
#include "lib3ds/material.h"

GLC_3dsToWorld::GLC_3dsToWorld(const QGLContext *pContext)
: m_pWorld(NULL)
, m_FileName()
, m_pQGLContext(pContext)
, m_pCurrentMesh(NULL)
, m_CurVertexIndex(0)
, m_CurNormalIndex(0)
, m_pLib3dsFile(NULL)
, m_Materials()
, m_MaterialsIndex()
, m_NextMaterialIndex(0)
, m_pCurrentMaterial(NULL)
{
}

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

// Create an GLC_World from an input 3DS File
GLC_World* GLC_3dsToWorld::CreateWorldFrom3ds(QFile &file)
{
	clear();
	m_FileName= file.fileName();
	//////////////////////////////////////////////////////////////////
	// Test if the file exist and can be opened
	//////////////////////////////////////////////////////////////////
	if (!file.open(QIODevice::ReadOnly))
	{
		QString message(QString("GLC_3dsToWorld::CreateWorldFrom3ds File ") + m_FileName + QString(" doesn't exist"));
		GLC_FileFormatException fileFormatException(message, m_FileName);
		throw(fileFormatException);
	}
	//////////////////////////////////////////////////////////////////
	// Init member
	//////////////////////////////////////////////////////////////////
	m_pWorld= new GLC_World;

	//Load 3ds File
	m_pLib3dsFile=lib3ds_file_load(m_FileName.toStdString().c_str());
	if (!m_pLib3dsFile)
	{
		QString message= "GLC_3dsToWorld::CreateWorldFrom3ds : Loading Failed";
		GLC_FileFormatException fileFormatException(message, m_FileName);
		clear();
		throw(fileFormatException);
	}

	// Create GLC_Instance with Node
	for (Lib3dsNode *pNode=m_pLib3dsFile->nodes; pNode!=0; pNode=pNode->next)
	{
		createMeshes(m_pWorld->rootProduct(), pNode);
	}

	/* No nodes?  Fabricate nodes to display all the meshes. */
	if(m_pWorld->collection()->isEmpty())
	{
		qDebug() << "No nodes founds";
		Lib3dsMesh *pMesh;
		Lib3dsNode *pNode;

		for(pMesh= m_pLib3dsFile->meshes; pMesh != NULL; pMesh = pMesh->next)
		{
			pNode= lib3ds_node_new_object();
			strcpy(pNode->name, pMesh->name);
			pNode->parent_id= LIB3DS_NO_PARENT;
			lib3ds_file_insert_node(m_pLib3dsFile, pNode);
		}
	}

	// Free Lib3dsFile and all its ressources
	lib3ds_file_free(m_pLib3dsFile);
	m_pLib3dsFile= NULL;
	if (m_pWorld->collection()->isEmpty())
	{
		QString message= "GLC_3dsToWorld::CreateWorldFrom3ds : No mesh found !";
		GLC_FileFormatException fileFormatException(message, m_FileName);
		clear();
		throw(fileFormatException);
	}
	return m_pWorld;
}

/////////////////////////////////////////////////////////////////////
// Private services Functions
//////////////////////////////////////////////////////////////////////

// clear stlToWorld allocate memmory and reset member
void GLC_3dsToWorld::clear()
{
	if (NULL != m_pCurrentMesh)
	{
		delete m_pCurrentMesh;
		m_pCurrentMesh= NULL;
	}
	m_pWorld= NULL;
	m_FileName.clear();
	m_pCurrentMesh= NULL;
	m_CurVertexIndex= 0;
	m_CurNormalIndex= 0;
	if (NULL != m_pLib3dsFile)
	{
		lib3ds_file_free(m_pLib3dsFile);
		m_pLib3dsFile= NULL;
	}

	// Remove unused material
	QHash<QString, GLC_Material*>::iterator i;
	for (i= m_Materials.begin(); i != m_Materials.end(); ++i)
	{
		if (i.value()->isUnused()) delete i.value();
	}
	m_Materials.clear();
	// Clear the material index hash table
	m_MaterialsIndex.clear();
	m_NextMaterialIndex= 0;
	// Set the current material to NULL
	m_pCurrentMaterial= NULL;

}

// Create meshes from the 3ds File
void GLC_3dsToWorld::createMeshes(GLC_Product* pProduct, Lib3dsNode* pFatherNode)
{
	GLC_Product* pChildProduct= NULL;
	Lib3dsMesh *pMesh= NULL;

	if (pFatherNode->type == LIB3DS_OBJECT_NODE)
	{
		// Check if the node is a mesh or dummy

		if (strcmp(pFatherNode->name,"$$$DUMMY")==0)
		{
			return;
		}
		pMesh = lib3ds_file_mesh_by_name(m_pLib3dsFile, pFatherNode->data.object.morph);
	    if( pMesh == NULL )
	    {
	    	pMesh = lib3ds_file_mesh_by_name(m_pLib3dsFile, pFatherNode->name);
	    }
	    if( pMesh != NULL )
	    {
	    	GLC_Instance instance(createInstance(pMesh));
			Lib3dsMatrix matrix;
			lib3ds_matrix_copy(matrix, pMesh->matrix);
			GLC_Matrix4x4 mat(&matrix[0][0]);
			GLC_Matrix4x4 matInv(&matrix[0][0]);
			matInv.invert();

			Lib3dsObjectData *pObjectData;
			pObjectData= &pFatherNode->data.object;
			GLC_Matrix4x4 trans(-pObjectData->pivot[0], -pObjectData->pivot[1], -pObjectData->pivot[2]);

			mat= mat * trans * matInv;

			pProduct->addChildPart(instance)->move(mat);
	    }
	}

	// If there is a child, create a child product
	if (NULL != pFatherNode->childs)
	{

		pChildProduct= pProduct->addNewChildProduct();

		// Create Childs meshes if exists
		for (Lib3dsNode* pNode= pFatherNode->childs; pNode!=0; pNode= pNode->next)
		{
			createMeshes(pChildProduct, pNode);
		}
	}

}
//! Create Instance from a Lib3dsNode
GLC_Instance GLC_3dsToWorld::createInstance(Lib3dsMesh* p3dsMesh)
{
	GLC_Mesh2 * pMesh= new GLC_Mesh2();
	pMesh->setName(p3dsMesh->name);
	// The mesh normals
	const int normalsNumber= p3dsMesh->faces * 3;
	Lib3dsVector *normalL= static_cast<Lib3dsVector*>(malloc(normalsNumber * sizeof(Lib3dsVector)));
	lib3ds_mesh_calculate_normals(p3dsMesh, normalL);
	// Add the normal to the mesh
	for (int i= 0; i < normalsNumber; ++i)
	{
		pMesh->addNormal(i, GLC_Vector3d(normalL[i][0], normalL[i][1], normalL[i][2]));
	}
	delete normalL;

	// Add Vertex to the mesh
	const int vertexNumber= p3dsMesh->points;
	for (int i= 0; i < vertexNumber; ++i)
	{
		pMesh->addVertex(i, GLC_Vector3d(p3dsMesh->pointL[i].pos[0], p3dsMesh->pointL[i].pos[1], p3dsMesh->pointL[i].pos[2]));
	}

	int normalIndex= 0;
	int textureIndex= 0;

	for (unsigned int i= 0; i < p3dsMesh->faces; ++i)
	{
		//Add the Normal
		QVector<int> normal;
		normal.append(normalIndex++);
		normal.append(normalIndex++);
		normal.append(normalIndex++);
		QVector<int> vertex;
		Lib3dsFace *p3dsFace=&p3dsMesh->faceL[i];
		// Load the material
		// The material current face index
		QVector<int> material;

		if (p3dsFace->material[0])
		{
			Lib3dsMaterial* p3dsMat=lib3ds_file_material_by_name(m_pLib3dsFile, p3dsFace->material);
			// Check it this material as already been loaded
			const QString materialName(p3dsFace->material);
			if (!m_Materials.contains(materialName))
			{ // Material not already loaded, load it
				loadMaterial(p3dsMat);
				// Add the loaded material to the mesh
				pMesh->addMaterial(m_MaterialsIndex[materialName], m_Materials[materialName]);
			}
			// Set the current material
			m_pCurrentMaterial= m_Materials[materialName];

			const int index= m_MaterialsIndex[materialName];
			material << index << index << index;
		}
		else // No material
		{
			if (NULL != m_pCurrentMaterial)
			{
				const int index= m_MaterialsIndex[m_pCurrentMaterial->getName()];
				pMesh->addMaterial(index, m_pCurrentMaterial);
				material << index << index << index;
			}
			else
			{
				material << -1 << -1 << -1;
			}
		}
		// End of loading material
		vertex << p3dsFace->points[0] << p3dsFace->points[1] << p3dsFace->points[2];
		// check if the mesh have texture coordinate
		if (p3dsMesh->texels > 0)
		{
			QVector<int> texture;
			for (int i= 0; i < 3; ++i)
			{
				// Load texture coordinate
				float x= p3dsMesh->texelL[p3dsFace->points[i]][0];
				float y= p3dsMesh->texelL[p3dsFace->points[i]][1];
				GLC_Vector2d texel(static_cast<double>(x), static_cast<double>(y));
				texture.append(textureIndex);
				pMesh->addTextureCoordinate(textureIndex++, texel);
			}
			pMesh->addFace(material, vertex, normal, texture);

		}
		else
		{
			pMesh->addFace(material, vertex, normal);
		}

	}
	return GLC_Instance(pMesh);
}

// Load Material
void GLC_3dsToWorld::loadMaterial(Lib3dsMaterial* p3dsMaterial)
{
	GLC_Material* pMaterial= new GLC_Material;
	// Set the material name
	const QString materialName(p3dsMaterial->name);
	pMaterial->setName(materialName);
	// Check if there is a texture
	if (p3dsMaterial->texture1_map.name[0])
	{
		const QString textureName(p3dsMaterial->texture1_map.name);
		// Retrieve the .3ds file path
		QFileInfo fileInfo(m_FileName);
		QString textureFileName(fileInfo.absolutePath() + QDir::separator());
		textureFileName.append(textureName);
		QFile textureFile(textureFileName);

		if (textureFile.open(QIODevice::ReadOnly))
		{
			// Create the texture and assign it to the material
			GLC_Texture *pTexture = new GLC_Texture(m_pQGLContext, textureFile);
			pMaterial->setTexture(pTexture);
		}
		textureFile.close();
	}
	// Ambient Color
	QColor ambient;
	ambient.setRgbF(p3dsMaterial->ambient[0], p3dsMaterial->ambient[1], p3dsMaterial->ambient[2]);
	ambient.setAlphaF(p3dsMaterial->ambient[3]);
	pMaterial->setAmbientColor(ambient);
	// Diffuse Color
	QColor diffuse;
	diffuse.setRgbF(p3dsMaterial->diffuse[0], p3dsMaterial->diffuse[1], p3dsMaterial->diffuse[2]);
	diffuse.setAlphaF(p3dsMaterial->diffuse[3]);
	pMaterial->setDiffuseColor(diffuse);
	// Specular Color
	QColor specular;
	specular.setRgbF(p3dsMaterial->specular[0], p3dsMaterial->specular[1], p3dsMaterial->specular[2]);
	specular.setAlphaF(p3dsMaterial->specular[3]);
	pMaterial->setSpecularColor(specular);
	// Shininess
	//pMaterial->setShininess(pow(2, 10.0 * p3dsMaterial->shininess));
	// Transparency
	pMaterial->setTransparency(1.0 - p3dsMaterial->transparency);

	// Add the material to the hash table
	m_Materials.insert(materialName, pMaterial);
	// Add the material index
	m_MaterialsIndex.insert(materialName, m_NextMaterialIndex++);
}

<|endoftext|>


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

#include "gm.h"
#include "SkFlattenableBuffers.h"
#include "SkLayerRasterizer.h"
#include "SkBlurMaskFilter.h"

static void r0(SkLayerRasterizer* rast, SkPaint& p) {
    p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
                                             SkBlurMaskFilter::kNormal_BlurStyle))->unref();
    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));

    p.setMaskFilter(NULL);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1);
    rast->addLayer(p);

    p.setAlpha(0x11);
    p.setStyle(SkPaint::kFill_Style);
    p.setXfermodeMode(SkXfermode::kSrc_Mode);
    rast->addLayer(p);
}

static void r1(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    p.setAlpha(0x40);
    p.setXfermodeMode(SkXfermode::kSrc_Mode);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1*2);
    rast->addLayer(p);
}

static void r2(SkLayerRasterizer* rast, SkPaint& p) {
    p.setStyle(SkPaint::kStrokeAndFill_Style);
    p.setStrokeWidth(SK_Scalar1*4);
    rast->addLayer(p);

    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1*3/2);
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    rast->addLayer(p);
}

static void r3(SkLayerRasterizer* rast, SkPaint& p) {
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1*3);
    rast->addLayer(p);

    p.setAlpha(0x20);
    p.setStyle(SkPaint::kFill_Style);
    p.setXfermodeMode(SkXfermode::kSrc_Mode);
    rast->addLayer(p);
}

static void r4(SkLayerRasterizer* rast, SkPaint& p) {
    p.setAlpha(0x60);
    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));

    p.setAlpha(0xFF);
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);

    p.setXfermode(NULL);
    rast->addLayer(p);
}

#include "SkDiscretePathEffect.h"

static void r5(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
    p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
    rast->addLayer(p);
}

static void r6(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    p.setAntiAlias(false);
    SkLayerRasterizer* rast2 = new SkLayerRasterizer;
    r5(rast2, p);
    p.setRasterizer(rast2)->unref();
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    rast->addLayer(p);
}

#include "Sk2DPathEffect.h"

static SkPathEffect* MakeDotEffect(SkScalar radius, const SkMatrix& matrix) {
    SkPath path;
    path.addCircle(0, 0, radius);
    return new SkPath2DPathEffect(matrix, path);
}

static void r7(SkLayerRasterizer* rast, SkPaint& p) {
    SkMatrix    lattice;
    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
    p.setPathEffect(MakeDotEffect(SK_Scalar1*4, lattice))->unref();
    rast->addLayer(p);
}

static void r8(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    SkMatrix    lattice;
    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
    p.setPathEffect(MakeDotEffect(SK_Scalar1*2, lattice))->unref();
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    rast->addLayer(p);

    p.setPathEffect(NULL);
    p.setXfermode(NULL);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1);
    rast->addLayer(p);
}

class Line2DPathEffect : public Sk2DPathEffect {
public:
    Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
    : Sk2DPathEffect(matrix), fWidth(width) {}

	virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec) SK_OVERRIDE {
        if (this->INHERITED::filterPath(dst, src, rec)) {
            rec->setStrokeStyle(fWidth);
            return true;
        }
        return false;
    }

    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Line2DPathEffect)

protected:
	virtual void nextSpan(int u, int v, int ucount, SkPath* dst) {
        if (ucount > 1) {
            SkPoint	src[2], dstP[2];

            src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
                       SkIntToScalar(v) + SK_ScalarHalf);
            src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
                       SkIntToScalar(v) + SK_ScalarHalf);
            this->getMatrix().mapPoints(dstP, src, 2);

            dst->moveTo(dstP[0]);
            dst->lineTo(dstP[1]);
        }
    }

    Line2DPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
        fWidth = buffer.readScalar();
    }
    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
        this->INHERITED::flatten(buffer);
        buffer.writeScalar(fWidth);
    }

private:
    SkScalar fWidth;

    typedef Sk2DPathEffect INHERITED;
};

static void r9(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    SkMatrix    lattice;
    lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
    lattice.postRotate(SkIntToScalar(30), 0, 0);
    p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    rast->addLayer(p);

    p.setPathEffect(NULL);
    p.setXfermode(NULL);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1);
    rast->addLayer(p);
}

typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);

static const raster_proc gRastProcs[] = {
    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
};

static const struct {
    SkColor fMul, fAdd;
} gLightingColors[] = {
    { 0x808080, 0x800000 }, // general case
    { 0x707070, 0x707070 }, // no-pin case
    { 0xFFFFFF, 0x800000 }, // just-add case
    { 0x808080, 0x000000 }, // just-mul case
    { 0xFFFFFF, 0x000000 }  // identity case
};

#include "SkXfermode.h"

static void apply_shader(SkPaint* paint, int index) {
    raster_proc proc = gRastProcs[index];
    if (proc)
    {
        SkPaint p;
        SkLayerRasterizer*  rast = new SkLayerRasterizer;

        p.setAntiAlias(true);
        proc(rast, p);
        paint->setRasterizer(rast)->unref();
    }

#if 0
    SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 };
    paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref();
#endif
    paint->setColor(SK_ColorBLUE);
}

class TextEffectsGM : public skiagm::GM {
public:
	TextEffectsGM() {}

protected:
    virtual SkString onShortName() SK_OVERRIDE {
        return SkString("texteffects");
    }

    virtual SkISize onISize() SK_OVERRIDE {
        return SkISize::Make(640, 480);
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        canvas->save();

        SkPaint     paint;

        paint.setAntiAlias(true);
        paint.setTextSize(SkIntToScalar(56));

        SkScalar    x = SkIntToScalar(20);
        SkScalar    y = paint.getTextSize();

        SkString str("Hamburgefons");

        for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
            apply_shader(&paint, i);

            //  paint.setMaskFilter(NULL);
            //  paint.setColor(SK_ColorBLACK);

            canvas->drawText(str.c_str(), str.size(), x, y, paint);

            y += paint.getFontSpacing();
        }

        canvas->restore();
    }

private:
    typedef skiagm::GM INHERITED;
};

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

static skiagm::GM* MyFactory(void*) { return new TextEffectsGM; }
static skiagm::GMRegistry reg(MyFactory);

<commit_msg>adjust size to match the content<commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm.h"
#include "SkFlattenableBuffers.h"
#include "SkLayerRasterizer.h"
#include "SkBlurMaskFilter.h"

static void r0(SkLayerRasterizer* rast, SkPaint& p) {
    p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
                                             SkBlurMaskFilter::kNormal_BlurStyle))->unref();
    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));

    p.setMaskFilter(NULL);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1);
    rast->addLayer(p);

    p.setAlpha(0x11);
    p.setStyle(SkPaint::kFill_Style);
    p.setXfermodeMode(SkXfermode::kSrc_Mode);
    rast->addLayer(p);
}

static void r1(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    p.setAlpha(0x40);
    p.setXfermodeMode(SkXfermode::kSrc_Mode);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1*2);
    rast->addLayer(p);
}

static void r2(SkLayerRasterizer* rast, SkPaint& p) {
    p.setStyle(SkPaint::kStrokeAndFill_Style);
    p.setStrokeWidth(SK_Scalar1*4);
    rast->addLayer(p);

    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1*3/2);
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    rast->addLayer(p);
}

static void r3(SkLayerRasterizer* rast, SkPaint& p) {
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1*3);
    rast->addLayer(p);

    p.setAlpha(0x20);
    p.setStyle(SkPaint::kFill_Style);
    p.setXfermodeMode(SkXfermode::kSrc_Mode);
    rast->addLayer(p);
}

static void r4(SkLayerRasterizer* rast, SkPaint& p) {
    p.setAlpha(0x60);
    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));

    p.setAlpha(0xFF);
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);

    p.setXfermode(NULL);
    rast->addLayer(p);
}

#include "SkDiscretePathEffect.h"

static void r5(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
    p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
    rast->addLayer(p);
}

static void r6(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    p.setAntiAlias(false);
    SkLayerRasterizer* rast2 = new SkLayerRasterizer;
    r5(rast2, p);
    p.setRasterizer(rast2)->unref();
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    rast->addLayer(p);
}

#include "Sk2DPathEffect.h"

static SkPathEffect* MakeDotEffect(SkScalar radius, const SkMatrix& matrix) {
    SkPath path;
    path.addCircle(0, 0, radius);
    return new SkPath2DPathEffect(matrix, path);
}

static void r7(SkLayerRasterizer* rast, SkPaint& p) {
    SkMatrix    lattice;
    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
    p.setPathEffect(MakeDotEffect(SK_Scalar1*4, lattice))->unref();
    rast->addLayer(p);
}

static void r8(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    SkMatrix    lattice;
    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
    p.setPathEffect(MakeDotEffect(SK_Scalar1*2, lattice))->unref();
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    rast->addLayer(p);

    p.setPathEffect(NULL);
    p.setXfermode(NULL);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1);
    rast->addLayer(p);
}

class Line2DPathEffect : public Sk2DPathEffect {
public:
    Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
    : Sk2DPathEffect(matrix), fWidth(width) {}

	virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec) SK_OVERRIDE {
        if (this->INHERITED::filterPath(dst, src, rec)) {
            rec->setStrokeStyle(fWidth);
            return true;
        }
        return false;
    }

    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Line2DPathEffect)

protected:
	virtual void nextSpan(int u, int v, int ucount, SkPath* dst) {
        if (ucount > 1) {
            SkPoint	src[2], dstP[2];

            src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
                       SkIntToScalar(v) + SK_ScalarHalf);
            src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
                       SkIntToScalar(v) + SK_ScalarHalf);
            this->getMatrix().mapPoints(dstP, src, 2);

            dst->moveTo(dstP[0]);
            dst->lineTo(dstP[1]);
        }
    }

    Line2DPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
        fWidth = buffer.readScalar();
    }
    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
        this->INHERITED::flatten(buffer);
        buffer.writeScalar(fWidth);
    }

private:
    SkScalar fWidth;

    typedef Sk2DPathEffect INHERITED;
};

static void r9(SkLayerRasterizer* rast, SkPaint& p) {
    rast->addLayer(p);

    SkMatrix    lattice;
    lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
    lattice.postRotate(SkIntToScalar(30), 0, 0);
    p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
    p.setXfermodeMode(SkXfermode::kClear_Mode);
    rast->addLayer(p);

    p.setPathEffect(NULL);
    p.setXfermode(NULL);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(SK_Scalar1);
    rast->addLayer(p);
}

typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);

static const raster_proc gRastProcs[] = {
    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
};

static const struct {
    SkColor fMul, fAdd;
} gLightingColors[] = {
    { 0x808080, 0x800000 }, // general case
    { 0x707070, 0x707070 }, // no-pin case
    { 0xFFFFFF, 0x800000 }, // just-add case
    { 0x808080, 0x000000 }, // just-mul case
    { 0xFFFFFF, 0x000000 }  // identity case
};

#include "SkXfermode.h"

static void apply_shader(SkPaint* paint, int index) {
    raster_proc proc = gRastProcs[index];
    if (proc)
    {
        SkPaint p;
        SkLayerRasterizer*  rast = new SkLayerRasterizer;

        p.setAntiAlias(true);
        proc(rast, p);
        paint->setRasterizer(rast)->unref();
    }

#if 0
    SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 };
    paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref();
#endif
    paint->setColor(SK_ColorBLUE);
}

class TextEffectsGM : public skiagm::GM {
public:
	TextEffectsGM() {}

protected:
    virtual SkString onShortName() SK_OVERRIDE {
        return SkString("texteffects");
    }

    virtual SkISize onISize() SK_OVERRIDE {
        return SkISize::Make(460, 680);
    }

    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
        canvas->save();

        SkPaint     paint;
        paint.setAntiAlias(true);
        paint.setTextSize(SkIntToScalar(56));

        SkScalar    x = SkIntToScalar(20);
        SkScalar    y = paint.getTextSize();

        SkString str("Hamburgefons");

        for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
            apply_shader(&paint, i);

            //  paint.setMaskFilter(NULL);
            //  paint.setColor(SK_ColorBLACK);

            canvas->drawText(str.c_str(), str.size(), x, y, paint);

            y += paint.getFontSpacing();
        }

        canvas->restore();
    }

private:
    typedef skiagm::GM INHERITED;
};

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

static skiagm::GM* MyFactory(void*) { return new TextEffectsGM; }
static skiagm::GMRegistry reg(MyFactory);

<|endoftext|>


<commit_before>/* GNE - Game Networking Engine, a portable multithreaded networking library.
 * Copyright (C) 2001 Jason Winnebeck (gillius@mail.rit.edu)
 * Project website: http://www.rit.edu/~jpw9607/
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "../include/gnelib/gneintern.h"
#include "../include/gnelib/ConnectionEventGenerator.h"
#include "../include/gnelib/ConnectionStats.h"
#include "../include/gnelib/PacketParser.h"
#include "../include/gnelib/GNE.h"
#include "../include/gnelib/Address.h"
#include "../include/gnelib/Error.h"
#include "../include/gnelib/Errors.h"
#include "../include/gnelib/PingPacket.h"
#include "../include/gnelib/ObjectBrokerClient.h"
#include "../include/gnelib/Timer.h"
#include "../include/gnelib/Connection.h"
#include "../include/gnelib/Console.h"
#include "../include/gnelib/ServerConnectionListener.h"

#ifndef WIN32
#include <signal.h>
#endif

namespace GNE {
  namespace PacketParser {
    //this is declared here only so the user cannot access it, and the "real"
    //function can do checking on the ID given to it.
    void registerGNEPackets();
  }

char gameNameBuf[32] = {0};
guint32 userVersion = 0;
ConnectionEventGenerator::sptr eGen;

static bool initialized = false;
static int timeToWait = 10000;

bool initGNE(NLenum networkType, int (*atexit_ptr)(void (*func)(void)), int timeToClose ) {
  if (!initialized) {
    gnedbg(1, "GNE initialized");
    PacketParser::registerGNEPackets();
    ObjectBrokerClient::staticInit();
    timeToWait = timeToClose;

    if (networkType != NO_NET) {
      if (nlInit() == NL_FALSE)
        return true;
      if (nlSelectNetwork(networkType) == NL_FALSE)
        return true;
      nlEnable(NL_BLOCKING_IO);
      nlEnable(NL_TCP_NO_DELAY);
      //GNE sends its data in little endian format.
      nlEnable(NL_LITTLE_ENDIAN_DATA);
      nlDisable(NL_SOCKET_STATS);
      eGen = ConnectionEventGenerator::create();
      eGen->start();
      initialized = true; //We need only to set this to true if we are using HawkNL
    }

#ifndef WIN32
    signal(SIGPIPE, SIG_IGN);
#endif

#ifdef _DEBUG
    //Register killDebug separately, so we can still print out debugging
    //messages until the last minute possible.
    atexit_ptr(killDebug);
#endif

    atexit_ptr(shutdownGNE);

    return false;
  }
  return false;
}

void shutdownGNE() {
  if ( eGen ) {
    gnedbg( 1, "Shutting down CEG." );
    eGen->shutDown();
    //I'd like to use a join because that's cleaner, but I want to make sure
    //the program does not block indefinitely when closing.
  }

  gnedbg( 1, "GNE Shutdown begin: Closing all listeners." );
  ServerConnectionListener::closeAllListeners();
  gnedbg( 1, "Shutting down all connections." );
  Connection::disconnectAll();
  gnedbg( 1, "Stopping all timers." );
  Timer::stopAll();
  gnedbg( 1, "Shutting down all user threads." );
  Thread::requestAllShutdown( Thread::USER );

  gnedbg1( 1, "Waiting up to %d ms for all threads to shutdown.", timeToWait );
  bool timeout = Thread::waitForAllThreads( timeToWait );
  if ( timeout ) {
    gnedbg( 1, "Wait timeout: NOT ALL THREADS SHUT DOWN!" );
  }

  if ( eGen && eGen->isRunning() ) {
    gnedbg( 1, "CEG failed to shut down properly!  Please file a bug report." );
  }
  eGen.reset();

  if (initialized) {
    gnedbg( 1, "Shutting down HawkNL." );
    nlShutdown();
    initialized = false;
  }

  Console::shutdownConsole();

  gnedbg(1, "GNE Shutdown");
}

Address getLocalAddress() {
  assert(initialized);
  NLaddress nlAddr;

  NLsocket temp = nlOpen(0, NL_RELIABLE);
  nlGetLocalAddr(temp, &nlAddr);
  nlClose(temp);

  Address ret(nlAddr);
  ret.setPort(0);
  return ret;
}

ConnectionStats getGlobalStats() {
  assert(initialized);
  ConnectionStats ret;
  ret.packetsSent = nlGetInteger(NL_PACKETS_SENT);
  ret.bytesSent = nlGetInteger(NL_BYTES_SENT);
  ret.avgBytesSent = nlGetInteger(NL_AVE_BYTES_SENT);
  ret.maxAvgBytesSent = nlGetInteger(NL_HIGH_BYTES_SENT);
  ret.packetsRecv = nlGetInteger(NL_PACKETS_RECEIVED);
  ret.bytesRecv = nlGetInteger(NL_BYTES_RECEIVED);
  ret.avgBytesRecv = nlGetInteger(NL_AVE_BYTES_RECEIVED);
  ret.maxAvgBytesRecv = nlGetInteger(NL_HIGH_BYTES_RECEIVED);
  ret.openSockets = nlGetInteger(NL_OPEN_SOCKETS);
  return ret;
}

void enableStats() {
  assert(initialized);
  nlEnable(NL_SOCKET_STATS);
}

void disableStats() {
  assert(initialized);
  nlDisable(NL_SOCKET_STATS);
}

void clearStats() {
  assert(initialized);
  nlClear(NL_ALL_STATS);
}

int getOpenConnections() {
  assert(initialized);
  return nlGetInteger(NL_OPEN_SOCKETS);
}

GNEProtocolVersionNumber getGNEProtocolVersion() {
  assert(initialized);

  GNEProtocolVersionNumber ret;
  ret.version = 0;
  ret.subVersion = 0;
  ret.build = 6;

  return ret;
}

const char* getGameName() {
  return gameNameBuf;
}

guint32 getUserVersion() {
  assert(initialized);
  return userVersion;
}

void setGameInformation(std::string gameName, guint32 version) {
  assert(initialized);
  assert(gameName.length() <= (std::string::size_type)GNE::MAX_GAME_NAME_LEN);
  //We do this assert since this function should only be called once.
  assert(gameNameBuf[0] == 0);

  userVersion = version;
  strncpy(gameNameBuf, gameName.c_str(), MAX_GAME_NAME_LEN);
}

void checkVersions(const GNEProtocolVersionNumber& otherGNE,
                   std::string otherName, guint32 otherUser) {
  GNEProtocolVersionNumber us = getGNEProtocolVersion();

  //Check the GNE version numbers
  if (otherGNE.version != us.version ||
      otherGNE.subVersion != us.subVersion ||
      otherGNE.build != us.build) {
    if ((otherGNE.version > us.version) ||
        (otherGNE.version == us.version && otherGNE.subVersion > us.subVersion) ||
        (otherGNE.subVersion == us.subVersion && otherGNE.build > us.build))
      throw Error(Error::GNETheirVersionHigh);
    else
      throw Error(Error::GNETheirVersionLow);
  }

  //Check the game name
  if (otherName != gameNameBuf)
    throw WrongGame(otherName);

  //Check the user version numbers
  if (userVersion != otherUser)
    throw UserVersionMismatch(otherUser);
}

}
<commit_msg>Set NL_LITTLE_ENDIAN even if NO_NET.<commit_after>/* GNE - Game Networking Engine, a portable multithreaded networking library.
 * Copyright (C) 2001 Jason Winnebeck (gillius@mail.rit.edu)
 * Project website: http://www.rit.edu/~jpw9607/
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "../include/gnelib/gneintern.h"
#include "../include/gnelib/ConnectionEventGenerator.h"
#include "../include/gnelib/ConnectionStats.h"
#include "../include/gnelib/PacketParser.h"
#include "../include/gnelib/GNE.h"
#include "../include/gnelib/Address.h"
#include "../include/gnelib/Error.h"
#include "../include/gnelib/Errors.h"
#include "../include/gnelib/PingPacket.h"
#include "../include/gnelib/ObjectBrokerClient.h"
#include "../include/gnelib/Timer.h"
#include "../include/gnelib/Connection.h"
#include "../include/gnelib/Console.h"
#include "../include/gnelib/ServerConnectionListener.h"

#ifndef WIN32
#include <signal.h>
#endif

namespace GNE {
  namespace PacketParser {
    //this is declared here only so the user cannot access it, and the "real"
    //function can do checking on the ID given to it.
    void registerGNEPackets();
  }

char gameNameBuf[32] = {0};
guint32 userVersion = 0;
ConnectionEventGenerator::sptr eGen;

static bool initialized = false;
static int timeToWait = 10000;

bool initGNE(NLenum networkType, int (*atexit_ptr)(void (*func)(void)), int timeToClose ) {
  if (!initialized) {
    gnedbg(1, "GNE initialized");
    PacketParser::registerGNEPackets();
    ObjectBrokerClient::staticInit();
    timeToWait = timeToClose;

    //This is a little hacky, but I checked the HawkNL source to make sure this
    //worked before I did this.
    nlEnable(NL_LITTLE_ENDIAN_DATA);

    if (networkType != NO_NET) {
      if (nlInit() == NL_FALSE)
        return true;
      if (nlSelectNetwork(networkType) == NL_FALSE)
        return true;
      nlEnable(NL_BLOCKING_IO);
      nlEnable(NL_TCP_NO_DELAY);
      //GNE sends its data in little endian format.
      nlDisable(NL_SOCKET_STATS);
      eGen = ConnectionEventGenerator::create();
      eGen->start();
      initialized = true; //We need only to set this to true if we are using HawkNL
    }

#ifndef WIN32
    signal(SIGPIPE, SIG_IGN);
#endif

#ifdef _DEBUG
    //Register killDebug separately, so we can still print out debugging
    //messages until the last minute possible.
    atexit_ptr(killDebug);
#endif

    atexit_ptr(shutdownGNE);

    return false;
  }
  return false;
}

void shutdownGNE() {
  if ( eGen ) {
    gnedbg( 1, "Shutting down CEG." );
    eGen->shutDown();
    //I'd like to use a join because that's cleaner, but I want to make sure
    //the program does not block indefinitely when closing.
  }

  gnedbg( 1, "GNE Shutdown begin: Closing all listeners." );
  ServerConnectionListener::closeAllListeners();
  gnedbg( 1, "Shutting down all connections." );
  Connection::disconnectAll();
  gnedbg( 1, "Stopping all timers." );
  Timer::stopAll();
  gnedbg( 1, "Shutting down all user threads." );
  Thread::requestAllShutdown( Thread::USER );

  gnedbg1( 1, "Waiting up to %d ms for all threads to shutdown.", timeToWait );
  bool timeout = Thread::waitForAllThreads( timeToWait );
  if ( timeout ) {
    gnedbg( 1, "Wait timeout: NOT ALL THREADS SHUT DOWN!" );
  }

  if ( eGen && eGen->isRunning() ) {
    gnedbg( 1, "CEG failed to shut down properly!  Please file a bug report." );
  }
  eGen.reset();

  if (initialized) {
    gnedbg( 1, "Shutting down HawkNL." );
    nlShutdown();
    initialized = false;
  }

  Console::shutdownConsole();

  gnedbg(1, "GNE Shutdown");
}

Address getLocalAddress() {
  assert(initialized);
  NLaddress nlAddr;

  NLsocket temp = nlOpen(0, NL_RELIABLE);
  nlGetLocalAddr(temp, &nlAddr);
  nlClose(temp);

  Address ret(nlAddr);
  ret.setPort(0);
  return ret;
}

ConnectionStats getGlobalStats() {
  assert(initialized);
  ConnectionStats ret;
  ret.packetsSent = nlGetInteger(NL_PACKETS_SENT);
  ret.bytesSent = nlGetInteger(NL_BYTES_SENT);
  ret.avgBytesSent = nlGetInteger(NL_AVE_BYTES_SENT);
  ret.maxAvgBytesSent = nlGetInteger(NL_HIGH_BYTES_SENT);
  ret.packetsRecv = nlGetInteger(NL_PACKETS_RECEIVED);
  ret.bytesRecv = nlGetInteger(NL_BYTES_RECEIVED);
  ret.avgBytesRecv = nlGetInteger(NL_AVE_BYTES_RECEIVED);
  ret.maxAvgBytesRecv = nlGetInteger(NL_HIGH_BYTES_RECEIVED);
  ret.openSockets = nlGetInteger(NL_OPEN_SOCKETS);
  return ret;
}

void enableStats() {
  assert(initialized);
  nlEnable(NL_SOCKET_STATS);
}

void disableStats() {
  assert(initialized);
  nlDisable(NL_SOCKET_STATS);
}

void clearStats() {
  assert(initialized);
  nlClear(NL_ALL_STATS);
}

int getOpenConnections() {
  assert(initialized);
  return nlGetInteger(NL_OPEN_SOCKETS);
}

GNEProtocolVersionNumber getGNEProtocolVersion() {
  assert(initialized);

  GNEProtocolVersionNumber ret;
  ret.version = 0;
  ret.subVersion = 0;
  ret.build = 6;

  return ret;
}

const char* getGameName() {
  return gameNameBuf;
}

guint32 getUserVersion() {
  assert(initialized);
  return userVersion;
}

void setGameInformation(std::string gameName, guint32 version) {
  assert(initialized);
  assert(gameName.length() <= (std::string::size_type)GNE::MAX_GAME_NAME_LEN);
  //We do this assert since this function should only be called once.
  assert(gameNameBuf[0] == 0);

  userVersion = version;
  strncpy(gameNameBuf, gameName.c_str(), MAX_GAME_NAME_LEN);
}

void checkVersions(const GNEProtocolVersionNumber& otherGNE,
                   std::string otherName, guint32 otherUser) {
  GNEProtocolVersionNumber us = getGNEProtocolVersion();

  //Check the GNE version numbers
  if (otherGNE.version != us.version ||
      otherGNE.subVersion != us.subVersion ||
      otherGNE.build != us.build) {
    if ((otherGNE.version > us.version) ||
        (otherGNE.version == us.version && otherGNE.subVersion > us.subVersion) ||
        (otherGNE.subVersion == us.subVersion && otherGNE.build > us.build))
      throw Error(Error::GNETheirVersionHigh);
    else
      throw Error(Error::GNETheirVersionLow);
  }

  //Check the game name
  if (otherName != gameNameBuf)
    throw WrongGame(otherName);

  //Check the user version numbers
  if (userVersion != otherUser)
    throw UserVersionMismatch(otherUser);
}

}
<|endoftext|>


<commit_before>
// For conditions of distribution and use, see copyright notice in license.txt

#include "UIController.h"
#include <QApplication>
#include <QDebug>
#include <cmath>
#include <QKeyEvent>

#include "MemoryLeakCheck.h"

namespace QtUI
{

UIController::UIController() : mouseDown_(false), arrange_(false), responseTimeLimit_(500)
{}

UIController::~UIController()
{}

void UIController::Update()
{

    QList<boost::shared_ptr<UICanvas> >::iterator iter = canvases_.begin();
    for(; iter != canvases_.end(); ++iter)
        (*iter)->Render();

    //Update Z-order if needed.

    Arrange();
}

void UIController::Arrange()
{
    if (arrange_)
    {
        ///todo Arrange canvases to z-order.
    }
    arrange_ = false;

}

void UIController::RemoveCanvas(const QString& id)
{
    QList<boost::shared_ptr<UICanvas> >::iterator iter = canvases_.begin();
    int index = 0;
    for(; iter != canvases_.end(); ++iter, ++index)
    {
        QString canvas_id = (*iter)->GetID();
        if ( canvas_id == id)
        {
            canvases_.removeAt(index);
            break;
        }
    }
}

boost::weak_ptr<UICanvas> UIController::CreateCanvas(UICanvas::Mode mode)
{
    boost::shared_ptr<UICanvas> canvas(new UICanvas(mode, parentWindowSize_));

    QObject::connect(this,SIGNAL(RenderWindowSizeChanged(const QSize&)),canvas.get(), SLOT(SetRenderWindowSize(const QSize&)));
    QObject::connect(canvas.get(), SIGNAL(RequestArrange()), this, SLOT(RequestArrange()));

    canvases_.append(canvas);
    return canvas;
}

void UIController::InjectMouseMove(int x, int y)
{

    QPoint point(x,y);
    int index = GetCanvas(point);

    // Normal move.

    if (index != -1 && !mouseDown_)
    {

        // Location of mouse event in scene.
        QPoint p = canvases_[index]->MapToCanvas(x,y);
        QPointF pos = canvases_[index]->mapToScene(p);

        //QPointF pos = canvases_[index]->mapToScene(point);

        QPoint currentMousePos((int)pos.x(), (int)pos.y());

        QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);

        if (mouseDown_)
        {
            mouseEvent.setButtonDownScenePos(Qt::LeftButton, mousePress_);
            mouseEvent.setButtonDownScreenPos(Qt::LeftButton, mousePress_);
        }
        else
        {
            mouseEvent.setButtonDownScenePos(Qt::NoButton, currentMousePos);
            mouseEvent.setButtonDownScreenPos(Qt::NoButton, currentMousePos);
        }

        mouseEvent.setScenePos(currentMousePos);
        mouseEvent.setScreenPos(currentMousePos);
        mouseEvent.setLastScenePos(currentMousePos);
        mouseEvent.setLastScreenPos(currentMousePos);
        mouseEvent.setButtons(mouseDown_ ? Qt::LeftButton : Qt::NoButton);
        mouseEvent.setButton(mouseDown_ ? Qt::LeftButton : Qt::NoButton);
        mouseEvent.setModifiers(0);
        mouseEvent.setAccepted(false);

        QApplication::sendEvent(canvases_[index]->scene(), &mouseEvent);

    }
    else if ( mouseDown_ )
    {
        // Drag event.

        // Find active canvas, move it to this location (if canvas is not locked.)

        index = GetCanvas(lastPosition_);

        if ( index != -1)
        {
            if ( !canvases_[index]->IsCanvasPositionLocked() )
            {
                QPoint pos = canvases_[index]->GetPosition().toPoint();

                int xPos = point.x()-(lastPosition_.x()-pos.x());
                int yPos = point.y()-(lastPosition_.y()-pos.y());

                canvases_[index]->SetPosition(xPos, yPos);
            }
        }

    }

    lastPosition_ = point;

}

void UIController::InjectMousePress(int x, int y)
{

    QPoint point(x,y);

    int index = GetCanvas(point);

    if ( timer_.isNull() )
    {
        // First press.
        timer_.start();
    }
    else if ( timer_.elapsed() <= responseTimeLimit_)
    {
        // Double click has happen.

        // Restart to zero.
        timer_.setHMS(0,0,0,-1);
        QPoint loc = lastPosition_ - point;

        if ( loc.manhattanLength() < 5)
            InjectDoubleClick(x,y);

        lastPosition_ = point;
        return;
    }
    else
    {
        // Restart to zero.

        timer_.setHMS(0,0,0,-1);
    }

    if (index != -1)
    {

        // Translate the mouse position from QGraphicsView coordinate frame onto
        // the QGraphicsScene coordinate frame.
        QPoint p = canvases_[index]->MapToCanvas(x,y);
        QPointF pos = canvases_[index]->mapToScene(p);
        QPoint currentMousePos((int)pos.x(), (int)pos.y());

        // For future use save press state.
        mouseDown_ = true;
        mousePress_ = point;

        QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress);

        mouseEvent.setButtonDownScenePos(Qt::LeftButton, currentMousePos);
        mouseEvent.setButtonDownScreenPos(Qt::LeftButton, currentMousePos);
        mouseEvent.setScenePos(currentMousePos);
        mouseEvent.setScreenPos(currentMousePos);
        mouseEvent.setLastScenePos(currentMousePos);
        mouseEvent.setLastScreenPos(currentMousePos);
        mouseEvent.setButtons(Qt::LeftButton);
        mouseEvent.setButton(Qt::LeftButton);

        mouseEvent.setModifiers(0);
        mouseEvent.setAccepted(false);

        QApplication::sendEvent(canvases_[index]->scene(), &mouseEvent);

    }
    lastPosition_ = point;
}

void UIController::InjectMouseRelease(int x, int y)
{
    ////todo what to do release after double click?

    QPoint point(x,y);
    int index = GetCanvas(point);

    if (index != -1)
    {

        // Translate the mouse position from QGraphicsView coordinate frame onto
        // the QGraphicsScene coordinate frame.
        QPoint p = canvases_[index]->MapToCanvas(x,y);
        QPointF pos = canvases_[index]->mapToScene(p);
        QPoint currentMousePos((int)pos.x(), (int)pos.y());

        QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease);
        mouseEvent.setButtonDownScenePos(Qt::NoButton, currentMousePos);
        mouseEvent.setButtonDownScreenPos(Qt::NoButton, currentMousePos);
        mouseEvent.setScenePos(currentMousePos);
        mouseEvent.setScreenPos(currentMousePos);
        mouseEvent.setLastScenePos(currentMousePos);
        mouseEvent.setLastScreenPos(currentMousePos);
        mouseEvent.setButtons(Qt::NoButton);
        mouseEvent.setButton(Qt::LeftButton);
        mouseEvent.setModifiers(0);
        mouseEvent.setAccepted(false);

        QApplication::sendEvent(canvases_[index]->scene(), &mouseEvent);

    }

    mouseDown_ = false;
    lastPosition_ = point;
}

void UIController::InjectDoubleClick(int x, int y)
{
    QPoint point(x,y);
    lastPosition_ = point;
    int index = GetCanvas(point);

    if (index != -1)
    {

        // Translate the mouse position from QGraphicsView coordinate frame onto
        // the QGraphicsScene coordinate frame.
        QPoint p = canvases_[index]->MapToCanvas(x,y);
        QPointF pos = canvases_[index]->mapToScene(p);
        QPoint currentMousePos((int)pos.x(), (int)pos.y());

        mouseDown_ = false;

        // In case of double click we set that left button generated click -> so it is set down.

        QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseDoubleClick);

        mouseEvent.setButtonDownScenePos(Qt::LeftButton, currentMousePos);
        mouseEvent.setButtonDownScreenPos(Qt::LeftButton, currentMousePos);
        mouseEvent.setScenePos(currentMousePos);
        mouseEvent.setScreenPos(currentMousePos);
        mouseEvent.setLastScenePos(currentMousePos);
        mouseEvent.setLastScreenPos(currentMousePos);
        mouseEvent.setButtons(Qt::LeftButton);
        mouseEvent.setButton(Qt::LeftButton);

        mouseEvent.setModifiers(0);
        mouseEvent.setAccepted(false);

        QApplication::sendEvent(canvases_[index]->scene(), &mouseEvent);

    }

}

void UIController::InjectKeyPressed(Qt::Key keyCode, const Qt::KeyboardModifier& modifier)
{
    QKeySequence sequence(keyCode);
    QKeyEvent keyEvent(QEvent::KeyPress, keyCode, modifier, sequence.toString().toLower());
    keyEvent.setAccepted(false);

    // Take a location of last known mouse press and send it to that canvas.

    int index = GetCanvas(mousePress_);
    if ( index != -1 )
    {
         QApplication::sendEvent(canvases_[index]->scene(), &keyEvent);
    }

}

void UIController::SetParentWindowSize(const QSize& size)
{
    parentWindowSize_ = size;
    emit RenderWindowSizeChanged(size);
}

int UIController::GetCanvas(const QPoint& point)
{
    QList<boost::shared_ptr<UICanvas> >::iterator iter = canvases_.begin();
    int index = 0;
    for (; iter != canvases_.end(); ++iter, ++index)
    {
        UICanvas::Mode mode = (*iter)->GetMode();

        switch(mode)
        {
            // If mode is external we let the Qt own window manager to things.
            case UICanvas::External:
                break;
            case UICanvas::Internal:
                {
                   if (Contains(*iter, point))
                        return index;
                    break;
                }

            default:
                break;

        }

    }

    // Did not find any canvases so let's return -1.

    return -1;
}

bool UIController::Contains(const boost::shared_ptr<UICanvas>& canvas, const QPoint& point) const
{
    QPoint pos = canvas->GetPosition().toPoint();
    return pos.x() <= point.x() &&
           pos.y() <= point.y() &&
           (pos.x() + canvas->width()) > point.x() &&
           (pos.y() + canvas->height()) > point.y();
}

}

      <commit_msg>Fix for double-click now it works as it should work. <commit_after>
// For conditions of distribution and use, see copyright notice in license.txt

#include "UIController.h"
#include <QApplication>
#include <QDebug>
#include <cmath>
#include <QKeyEvent>

#include "MemoryLeakCheck.h"

namespace QtUI
{

UIController::UIController() : mouseDown_(false), arrange_(false), responseTimeLimit_(500)
{}

UIController::~UIController()
{}

void UIController::Update()
{

    QList<boost::shared_ptr<UICanvas> >::iterator iter = canvases_.begin();
    for(; iter != canvases_.end(); ++iter)
        (*iter)->Render();

    //Update Z-order if needed.

    Arrange();
}

void UIController::Arrange()
{
    if (arrange_)
    {
        ///todo Arrange canvases to z-order.
    }
    arrange_ = false;

}

void UIController::RemoveCanvas(const QString& id)
{
    QList<boost::shared_ptr<UICanvas> >::iterator iter = canvases_.begin();
    int index = 0;
    for(; iter != canvases_.end(); ++iter, ++index)
    {
        QString canvas_id = (*iter)->GetID();
        if ( canvas_id == id)
        {
            canvases_.removeAt(index);
            break;
        }
    }
}

boost::weak_ptr<UICanvas> UIController::CreateCanvas(UICanvas::Mode mode)
{
    boost::shared_ptr<UICanvas> canvas(new UICanvas(mode, parentWindowSize_));

    QObject::connect(this,SIGNAL(RenderWindowSizeChanged(const QSize&)),canvas.get(), SLOT(SetRenderWindowSize(const QSize&)));
    QObject::connect(canvas.get(), SIGNAL(RequestArrange()), this, SLOT(RequestArrange()));

    canvases_.append(canvas);
    return canvas;
}

void UIController::InjectMouseMove(int x, int y)
{

    QPoint point(x,y);
    int index = GetCanvas(point);

    // Normal move.

    if (index != -1 && !mouseDown_)
    {

        // Location of mouse event in scene.
        QPoint p = canvases_[index]->MapToCanvas(x,y);
        QPointF pos = canvases_[index]->mapToScene(p);

        //QPointF pos = canvases_[index]->mapToScene(point);

        QPoint currentMousePos((int)pos.x(), (int)pos.y());

        QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);

        if (mouseDown_)
        {
            mouseEvent.setButtonDownScenePos(Qt::LeftButton, mousePress_);
            mouseEvent.setButtonDownScreenPos(Qt::LeftButton, mousePress_);
        }
        else
        {
            mouseEvent.setButtonDownScenePos(Qt::NoButton, currentMousePos);
            mouseEvent.setButtonDownScreenPos(Qt::NoButton, currentMousePos);
        }

        mouseEvent.setScenePos(currentMousePos);
        mouseEvent.setScreenPos(currentMousePos);
        mouseEvent.setLastScenePos(currentMousePos);
        mouseEvent.setLastScreenPos(currentMousePos);
        mouseEvent.setButtons(mouseDown_ ? Qt::LeftButton : Qt::NoButton);
        mouseEvent.setButton(mouseDown_ ? Qt::LeftButton : Qt::NoButton);
        mouseEvent.setModifiers(0);
        mouseEvent.setAccepted(false);

        QApplication::sendEvent(canvases_[index]->scene(), &mouseEvent);

    }
    else if ( mouseDown_ )
    {
        // Drag event.

        // Find active canvas, move it to this location (if canvas is not locked.)

        index = GetCanvas(lastPosition_);

        if ( index != -1)
        {
            if ( !canvases_[index]->IsCanvasPositionLocked() )
            {
                QPoint pos = canvases_[index]->GetPosition().toPoint();

                int xPos = point.x()-(lastPosition_.x()-pos.x());
                int yPos = point.y()-(lastPosition_.y()-pos.y());

                canvases_[index]->SetPosition(xPos, yPos);
            }
        }

    }

    lastPosition_ = point;

}

void UIController::InjectMousePress(int x, int y)
{

    QPoint point(x,y);

    int index = GetCanvas(point);

    if ( timer_.isNull() )
    {
        // First press.
        timer_.start();
    }
    else if ( timer_.elapsed() <= responseTimeLimit_)
    {
        // Double click has happen.

        // Restart to zero.
        timer_ = QTime();

        QPoint loc = lastPosition_ - point;
        lastPosition_ = point;
        if ( loc.manhattanLength() < 2)
        {
            InjectDoubleClick(x,y);
            return;
        }

    }
    else
    {
        // Restart to zero.
        timer_ = QTime();
        // And start again.
        timer_.start();
    }

    if (index != -1)
    {

        // Translate the mouse position from QGraphicsView coordinate frame onto
        // the QGraphicsScene coordinate frame.
        QPoint p = canvases_[index]->MapToCanvas(x,y);
        QPointF pos = canvases_[index]->mapToScene(p);
        QPoint currentMousePos((int)pos.x(), (int)pos.y());

        // For future use save press state.
        mouseDown_ = true;
        mousePress_ = point;

        QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress);

        mouseEvent.setButtonDownScenePos(Qt::LeftButton, currentMousePos);
        mouseEvent.setButtonDownScreenPos(Qt::LeftButton, currentMousePos);
        mouseEvent.setScenePos(currentMousePos);
        mouseEvent.setScreenPos(currentMousePos);
        mouseEvent.setLastScenePos(currentMousePos);
        mouseEvent.setLastScreenPos(currentMousePos);
        mouseEvent.setButtons(Qt::LeftButton);
        mouseEvent.setButton(Qt::LeftButton);

        mouseEvent.setModifiers(0);
        mouseEvent.setAccepted(false);

        QApplication::sendEvent(canvases_[index]->scene(), &mouseEvent);

    }
    lastPosition_ = point;
}

void UIController::InjectMouseRelease(int x, int y)
{
    ////todo what to do release after double click?

    QPoint point(x,y);
    int index = GetCanvas(point);

    if (index != -1)
    {

        // Translate the mouse position from QGraphicsView coordinate frame onto
        // the QGraphicsScene coordinate frame.
        QPoint p = canvases_[index]->MapToCanvas(x,y);
        QPointF pos = canvases_[index]->mapToScene(p);
        QPoint currentMousePos((int)pos.x(), (int)pos.y());

        QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease);
        mouseEvent.setButtonDownScenePos(Qt::NoButton, currentMousePos);
        mouseEvent.setButtonDownScreenPos(Qt::NoButton, currentMousePos);
        mouseEvent.setScenePos(currentMousePos);
        mouseEvent.setScreenPos(currentMousePos);
        mouseEvent.setLastScenePos(currentMousePos);
        mouseEvent.setLastScreenPos(currentMousePos);
        mouseEvent.setButtons(Qt::NoButton);
        mouseEvent.setButton(Qt::LeftButton);
        mouseEvent.setModifiers(0);
        mouseEvent.setAccepted(false);

        QApplication::sendEvent(canvases_[index]->scene(), &mouseEvent);

    }

    mouseDown_ = false;
    lastPosition_ = point;
}

void UIController::InjectDoubleClick(int x, int y)
{
    QPoint point(x,y);
    lastPosition_ = point;
    int index = GetCanvas(point);

    if (index != -1)
    {

        // Translate the mouse position from QGraphicsView coordinate frame onto
        // the QGraphicsScene coordinate frame.
        QPoint p = canvases_[index]->MapToCanvas(x,y);
        QPointF pos = canvases_[index]->mapToScene(p);
        QPoint currentMousePos((int)pos.x(), (int)pos.y());

        mouseDown_ = false;

        // In case of double click we set that left button generated click -> so it is set down.

        QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseDoubleClick);

        mouseEvent.setButtonDownScenePos(Qt::LeftButton, currentMousePos);
        mouseEvent.setButtonDownScreenPos(Qt::LeftButton, currentMousePos);
        mouseEvent.setScenePos(currentMousePos);
        mouseEvent.setScreenPos(currentMousePos);
        mouseEvent.setLastScenePos(currentMousePos);
        mouseEvent.setLastScreenPos(currentMousePos);
        mouseEvent.setButtons(Qt::LeftButton);
        mouseEvent.setButton(Qt::LeftButton);

        mouseEvent.setModifiers(0);
        mouseEvent.setAccepted(false);

        QApplication::sendEvent(canvases_[index]->scene(), &mouseEvent);

    }

}

void UIController::InjectKeyPressed(Qt::Key keyCode, const Qt::KeyboardModifier& modifier)
{
    QKeySequence sequence(keyCode);
    QKeyEvent keyEvent(QEvent::KeyPress, keyCode, modifier, sequence.toString().toLower());
    keyEvent.setAccepted(false);

    // Take a location of last known mouse press and send it to that canvas.

    int index = GetCanvas(mousePress_);
    if ( index != -1 )
    {
         QApplication::sendEvent(canvases_[index]->scene(), &keyEvent);
    }

}

void UIController::SetParentWindowSize(const QSize& size)
{
    parentWindowSize_ = size;
    emit RenderWindowSizeChanged(size);
}

int UIController::GetCanvas(const QPoint& point)
{
    QList<boost::shared_ptr<UICanvas> >::iterator iter = canvases_.begin();
    int index = 0;
    for (; iter != canvases_.end(); ++iter, ++index)
    {
        UICanvas::Mode mode = (*iter)->GetMode();

        switch(mode)
        {
            // If mode is external we let the Qt own window manager to things.
            case UICanvas::External:
                break;
            case UICanvas::Internal:
                {
                   if (Contains(*iter, point))
                        return index;
                    break;
                }

            default:
                break;

        }

    }

    // Did not find any canvases so let's return -1.

    return -1;
}

bool UIController::Contains(const boost::shared_ptr<UICanvas>& canvas, const QPoint& point) const
{
    QPoint pos = canvas->GetPosition().toPoint();
    return pos.x() <= point.x() &&
           pos.y() <= point.y() &&
           (pos.x() + canvas->width()) > point.x() &&
           (pos.y() + canvas->height()) > point.y();
}

}

      <|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iterator>
#include <limits>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <thrift/conformance/data/PatchGenerator.h>

#include <boost/mp11.hpp>
#include <fmt/core.h>

#include <thrift/conformance/cpp2/Object.h>
#include <thrift/conformance/data/ValueGenerator.h>
#include <thrift/lib/cpp2/op/Clear.h>
#include <thrift/lib/cpp2/op/Get.h>
#include <thrift/lib/cpp2/op/Patch.h>
#include <thrift/lib/cpp2/type/BaseType.h>
#include <thrift/lib/cpp2/type/Name.h>
#include <thrift/lib/cpp2/type/Tag.h>
#include <thrift/lib/thrift/gen-cpp2/patch_types_custom_protocol.h>

namespace apache::thrift::conformance::data {

namespace mp11 = boost::mp11;

namespace {

template <typename TT, typename GeneratedValue>
auto toValue(const GeneratedValue& value) {
  if constexpr (std::is_same_v<TT, type::binary_t>) {
    return *folly::IOBuf::copyBuffer(value.data());
  } else {
    return value;
  }
}

template <typename TT, typename GeneratedValue>
PatchOpTestCase makeAssignTest(
    const GeneratedValue& value,
    const AnyRegistry& registry,
    const Protocol& protocol) {
  PatchOpTestCase opTest;
  PatchOpRequest req;
  auto initialValue = value.value;
  op::clear<TT>(initialValue);
  req.value() = registry.store(asValueStruct<TT>(initialValue), protocol);

  auto patch = op::patch_type<TT>();
  patch = toValue<TT>(value.value);
  req.patch() =
      registry.store(asValueStruct<type::struct_c>(patch.toThrift()), protocol);
  opTest.request() = req;
  opTest.result() = registry.store(asValueStruct<TT>(value.value), protocol);
  return opTest;
}

template <typename TT, typename GeneratedValue>
PatchOpTestCase makeClearTest(
    const GeneratedValue& value,
    const AnyRegistry& registry,
    const Protocol& protocol) {
  PatchOpTestCase opTest;
  PatchOpRequest req;
  req.value() = registry.store(asValueStruct<TT>(value.value), protocol);

  auto patch = op::patch_type<TT>();
  patch.clear();
  req.patch() =
      registry.store(asValueStruct<type::struct_c>(patch.toThrift()), protocol);

  auto clearValue = value.value;
  op::clear<TT>(clearValue);
  opTest.request() = req;
  opTest.result() = registry.store(asValueStruct<TT>(clearValue), protocol);
  return opTest;
}

template <typename TT, typename T>
T makeAddExpectedResult(T value, int add = 1) {
  if constexpr (std::is_convertible_v<TT, type::bool_t>) {
    return !value;
  } else {
    if (std::numeric_limits<T>::max() == value && add > 0) {
      return value;
    } else if (std::numeric_limits<T>::min() == value && add < 0) {
      return value;
    }
    return value + add;
  }
}

template <typename TT, typename GeneratedValue, typename T>
PatchOpTestCase makeAddTest(
    const GeneratedValue& value,
    const AnyRegistry& registry,
    const Protocol& protocol,
    T toAdd) {
  PatchOpTestCase opTest;
  PatchOpRequest req;
  req.value() = registry.store(asValueStruct<TT>(value.value), protocol);

  auto addOp = [&](auto& patch) {
    if constexpr (std::is_same_v<TT, type::bool_t>) {
      patch.invert();
    } else {
      patch += toAdd;
    }
  };

  auto patch = op::patch_type<TT>();
  addOp(patch);
  req.patch() =
      registry.store(asValueStruct<type::struct_c>(patch.toThrift()), protocol);
  opTest.request() = req;
  opTest.result() = registry.store(
      asValueStruct<TT>(makeAddExpectedResult<TT>(value.value)), protocol);
  return opTest;
}

template <typename TT, typename GeneratedValue>
PatchOpTestCase makePrependTest(
    const GeneratedValue& value,
    const AnyRegistry& registry,
    const Protocol& protocol) {
  PatchOpTestCase opTest;
  PatchOpRequest req;
  req.value() = registry.store(asValueStruct<TT>(value.value), protocol);

  auto patch = op::patch_type<TT>();
  patch.prepend(toValue<TT>(value.value));
  req.patch() =
      registry.store(asValueStruct<type::struct_c>(patch.toThrift()), protocol);
  opTest.request() = req;
  auto expected = value.value + value.value;
  opTest.result() =
      registry.store(asValueStruct<TT>(toValue<TT>(expected)), protocol);
  return opTest;
}

template <typename TT, typename GeneratedValue>
PatchOpTestCase makeAppendTest(
    const GeneratedValue& value,
    const AnyRegistry& registry,
    const Protocol& protocol) {
  PatchOpTestCase opTest;
  PatchOpRequest req;
  req.value() = registry.store(asValueStruct<TT>(value.value), protocol);

  auto patch = op::patch_type<TT>();
  patch.append(toValue<TT>(value.value));
  req.patch() =
      registry.store(asValueStruct<type::struct_c>(patch.toThrift()), protocol);
  opTest.request() = req;
  auto expected = value.value + value.value;
  opTest.result() =
      registry.store(asValueStruct<TT>(toValue<TT>(expected)), protocol);
  return opTest;
}

template <typename TT>
Test createNumericPatchTest(
    const AnyRegistry& registry, const Protocol& protocol) {
  Test test;
  test.name() = protocol.name();

  for (const auto& value : ValueGenerator<TT>::getInterestingValues()) {
    auto& assignCase = test.testCases()->emplace_back();
    assignCase.name() =
        fmt::format("{}/assign.{}", type::getName<TT>(), value.name);
    auto& tascase = assignCase.test().emplace().objectPatch_ref().emplace();
    tascase = makeAssignTest<TT>(value, registry, protocol);

    // TODO(afuller): decide if bool and numeric should have clear()

    using ValueType = decltype(value.value);
    auto addAddTestCase = [&](ValueType toAdd) {
      auto& addCase = test.testCases()->emplace_back();
      addCase.name() =
          fmt::format("{}/add.{}_{}", type::getName<TT>(), value.name, toAdd);
      auto& tadcase = addCase.test().emplace().objectPatch_ref().emplace();
      tadcase = makeAddTest<TT>(value, registry, protocol, toAdd);
    };

    addAddTestCase(1);
    if constexpr (!std::is_same_v<TT, type::bool_t>) {
      addAddTestCase(-1);
      if (value.value > 0) {
        addAddTestCase(-value.value);
      }
    }
  }

  return test;
}

template <typename TT>
Test createStringLikePatchTest(
    const AnyRegistry& registry, const Protocol& protocol) {
  Test test;
  test.name() = protocol.name();

  for (const auto& value : ValueGenerator<TT>::getInterestingValues()) {
    auto& assignCase = test.testCases()->emplace_back();
    assignCase.name() =
        fmt::format("{}/assign.{}", type::getName<TT>(), value.name);
    auto& tascase = assignCase.test().emplace().objectPatch_ref().emplace();
    tascase = makeAssignTest<TT>(value, registry, protocol);

    auto& clearCase = test.testCases()->emplace_back();
    clearCase.name() = fmt::format("{}/clear", type::getName<TT>());
    auto& tclcase = clearCase.test().emplace().objectPatch_ref().emplace();
    tclcase = makeClearTest<TT>(value, registry, protocol);

    auto& prependCase = test.testCases()->emplace_back();
    prependCase.name() =
        fmt::format("{}/prepend.{}", type::getName<TT>(), value.name);
    auto& tprcase = prependCase.test().emplace().objectPatch_ref().emplace();
    tprcase = makePrependTest<TT>(value, registry, protocol);

    auto& appendCase = test.testCases()->emplace_back();
    appendCase.name() =
        fmt::format("{}/append.{}", type::getName<TT>(), value.name);
    auto& tapcase = appendCase.test().emplace().objectPatch_ref().emplace();
    tapcase = makeAppendTest<TT>(value, registry, protocol);
  }

  return test;
}

namespace {
template <typename T>
struct container_from_class {
  static_assert(std::is_same_v<T, type::container_c>, "Not a container class");
};

template <>
struct container_from_class<type::list_c> {
  template <typename ValueType>
  using type = type::list<ValueType>;
};

template <>
struct container_from_class<type::set_c> {
  template <typename ValueType>
  using type = type::set<ValueType>;
};

template <typename C, typename... Types>
using container_from_class_t =
    typename container_from_class<C>::template type<Types...>;

template <typename Tag>
Object patchAddOperation(Object&& patch, op::PatchOp operation, auto value) {
  auto opId = static_cast<int16_t>(operation);
  patch.members().ensure()[opId] = asValueStruct<Tag>(value);
  return std::move(patch);
}

template <typename Tag>
Value makePatchObject(auto operation, auto value) {
  Value result;
  result.objectValue_ref() = patchAddOperation<Tag>(Object{}, operation, value);
  return result;
}

template <typename ContainerTag>
PatchOpTestCase makeValueContainerAssignTC(
    const AnyRegistry& registry, const Protocol& protocol, auto value) {
  PatchOpTestCase tascase;
  PatchOpRequest req;

  using Container = type::standard_type<ContainerTag>;

  Container initial;
  Container expected = {value.value};

  req.value() = registry.store(asValueStruct<ContainerTag>(initial), protocol);
  req.patch() = registry.store(
      makePatchObject<ContainerTag>(op::PatchOp::Assign, expected), protocol);
  tascase.request() = req;
  tascase.result() =
      registry.store(asValueStruct<ContainerTag>(expected), protocol);
  return tascase;
}

template <typename ContainerTag>
PatchOpTestCase makeValueContainerClearTC(
    const AnyRegistry& registry, const Protocol& protocol, auto value) {
  PatchOpTestCase tascase;
  PatchOpRequest req;

  using Container = type::standard_type<ContainerTag>;

  Container initial = {value.value};
  Container expected;

  req.value() = registry.store(asValueStruct<ContainerTag>(initial), protocol);
  req.patch() = registry.store(
      makePatchObject<type::bool_t>(op::PatchOp::Clear, true), protocol);
  tascase.request() = req;
  tascase.result() =
      registry.store(asValueStruct<ContainerTag>(expected), protocol);
  return tascase;
}

} // namespace

template <typename TT>
Test createListSetPatchTest(
    const AnyRegistry& registry, const Protocol& protocol) {
  Test test;
  test.name() = protocol.name();

  using ValueContainers = boost::mp11::mp_list<type::list_c, type::set_c>;
  mp11::mp_for_each<ValueContainers>([&](auto cc) {
    using CC = decltype(cc);
    for (const auto& value : ValueGenerator<TT>::getInterestingValues()) {
      using ContainerTag = container_from_class_t<CC, TT>;
      auto& assignCase = test.testCases()->emplace_back();
      assignCase.name() = fmt::format(
          "{}<{}>/assign.{}",
          type::getName<CC>(),
          type::getName<TT>(),
          value.name);
      assignCase.test().emplace().objectPatch_ref() =
          makeValueContainerAssignTC<ContainerTag>(registry, protocol, value);

      auto& clearCase = test.testCases()->emplace_back();
      clearCase.name() = fmt::format(
          "{}<{}>/clear.{}",
          type::getName<CC>(),
          type::getName<TT>(),
          value.name);
      clearCase.test().emplace().objectPatch_ref() =
          makeValueContainerClearTC<ContainerTag>(registry, protocol, value);
    }
  });

  return test;
}

void addPatchToSuite(
    const AnyRegistry& registry, const Protocol& protocol, TestSuite& suite) {
  mp11::mp_for_each<detail::PrimaryTypeTags>([&](auto tt) {
    using TT = decltype(tt);
    if constexpr (
        !std::is_same_v<TT, type::string_t> &&
        !std::is_same_v<TT, type::binary_t>) {
      suite.tests()->emplace_back(
          createNumericPatchTest<TT>(registry, protocol));
    } else {
      suite.tests()->emplace_back(
          createStringLikePatchTest<TT>(registry, protocol));
    }

    if constexpr (!std::is_same_v<TT, type::bool_t>) {
      suite.tests()->emplace_back(
          createListSetPatchTest<TT>(registry, protocol));
    }
  });
}
} // namespace

TestSuite createPatchSuite(
    const std::set<Protocol>& protocols, const AnyRegistry& registry) {
  TestSuite suite;
  suite.name() = "PatchTest";
  for (const auto& protocol : protocols) {
    addPatchToSuite(registry, protocol, suite);
  }
  return suite;
}

} // namespace apache::thrift::conformance::data
<commit_msg>Add remove() conformance test cases for list and set patching.<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iterator>
#include <limits>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <thrift/conformance/data/PatchGenerator.h>

#include <boost/mp11.hpp>
#include <fmt/core.h>

#include <thrift/conformance/cpp2/Object.h>
#include <thrift/conformance/data/ValueGenerator.h>
#include <thrift/lib/cpp2/op/Clear.h>
#include <thrift/lib/cpp2/op/Get.h>
#include <thrift/lib/cpp2/op/Patch.h>
#include <thrift/lib/cpp2/type/BaseType.h>
#include <thrift/lib/cpp2/type/Name.h>
#include <thrift/lib/cpp2/type/Tag.h>
#include <thrift/lib/thrift/gen-cpp2/patch_types_custom_protocol.h>

namespace apache::thrift::conformance::data {

namespace mp11 = boost::mp11;

namespace {

template <typename TT, typename GeneratedValue>
auto toValue(const GeneratedValue& value) {
  if constexpr (std::is_same_v<TT, type::binary_t>) {
    return *folly::IOBuf::copyBuffer(value.data());
  } else {
    return value;
  }
}

template <typename TT, typename GeneratedValue>
PatchOpTestCase makeAssignTest(
    const GeneratedValue& value,
    const AnyRegistry& registry,
    const Protocol& protocol) {
  PatchOpTestCase opTest;
  PatchOpRequest req;
  auto initialValue = value.value;
  op::clear<TT>(initialValue);
  req.value() = registry.store(asValueStruct<TT>(initialValue), protocol);

  auto patch = op::patch_type<TT>();
  patch = toValue<TT>(value.value);
  req.patch() =
      registry.store(asValueStruct<type::struct_c>(patch.toThrift()), protocol);
  opTest.request() = req;
  opTest.result() = registry.store(asValueStruct<TT>(value.value), protocol);
  return opTest;
}

template <typename TT, typename GeneratedValue>
PatchOpTestCase makeClearTest(
    const GeneratedValue& value,
    const AnyRegistry& registry,
    const Protocol& protocol) {
  PatchOpTestCase opTest;
  PatchOpRequest req;
  req.value() = registry.store(asValueStruct<TT>(value.value), protocol);

  auto patch = op::patch_type<TT>();
  patch.clear();
  req.patch() =
      registry.store(asValueStruct<type::struct_c>(patch.toThrift()), protocol);

  auto clearValue = value.value;
  op::clear<TT>(clearValue);
  opTest.request() = req;
  opTest.result() = registry.store(asValueStruct<TT>(clearValue), protocol);
  return opTest;
}

template <typename TT, typename T>
T makeAddExpectedResult(T value, int add = 1) {
  if constexpr (std::is_convertible_v<TT, type::bool_t>) {
    return !value;
  } else {
    if (std::numeric_limits<T>::max() == value && add > 0) {
      return value;
    } else if (std::numeric_limits<T>::min() == value && add < 0) {
      return value;
    }
    return value + add;
  }
}

template <typename TT, typename GeneratedValue, typename T>
PatchOpTestCase makeAddTest(
    const GeneratedValue& value,
    const AnyRegistry& registry,
    const Protocol& protocol,
    T toAdd) {
  PatchOpTestCase opTest;
  PatchOpRequest req;
  req.value() = registry.store(asValueStruct<TT>(value.value), protocol);

  auto addOp = [&](auto& patch) {
    if constexpr (std::is_same_v<TT, type::bool_t>) {
      patch.invert();
    } else {
      patch += toAdd;
    }
  };

  auto patch = op::patch_type<TT>();
  addOp(patch);
  req.patch() =
      registry.store(asValueStruct<type::struct_c>(patch.toThrift()), protocol);
  opTest.request() = req;
  opTest.result() = registry.store(
      asValueStruct<TT>(makeAddExpectedResult<TT>(value.value)), protocol);
  return opTest;
}

template <typename TT, typename GeneratedValue>
PatchOpTestCase makePrependTest(
    const GeneratedValue& value,
    const AnyRegistry& registry,
    const Protocol& protocol) {
  PatchOpTestCase opTest;
  PatchOpRequest req;
  req.value() = registry.store(asValueStruct<TT>(value.value), protocol);

  auto patch = op::patch_type<TT>();
  patch.prepend(toValue<TT>(value.value));
  req.patch() =
      registry.store(asValueStruct<type::struct_c>(patch.toThrift()), protocol);
  opTest.request() = req;
  auto expected = value.value + value.value;
  opTest.result() =
      registry.store(asValueStruct<TT>(toValue<TT>(expected)), protocol);
  return opTest;
}

template <typename TT, typename GeneratedValue>
PatchOpTestCase makeAppendTest(
    const GeneratedValue& value,
    const AnyRegistry& registry,
    const Protocol& protocol) {
  PatchOpTestCase opTest;
  PatchOpRequest req;
  req.value() = registry.store(asValueStruct<TT>(value.value), protocol);

  auto patch = op::patch_type<TT>();
  patch.append(toValue<TT>(value.value));
  req.patch() =
      registry.store(asValueStruct<type::struct_c>(patch.toThrift()), protocol);
  opTest.request() = req;
  auto expected = value.value + value.value;
  opTest.result() =
      registry.store(asValueStruct<TT>(toValue<TT>(expected)), protocol);
  return opTest;
}

template <typename TT>
Test createNumericPatchTest(
    const AnyRegistry& registry, const Protocol& protocol) {
  Test test;
  test.name() = protocol.name();

  for (const auto& value : ValueGenerator<TT>::getInterestingValues()) {
    auto& assignCase = test.testCases()->emplace_back();
    assignCase.name() =
        fmt::format("{}/assign.{}", type::getName<TT>(), value.name);
    auto& tascase = assignCase.test().emplace().objectPatch_ref().emplace();
    tascase = makeAssignTest<TT>(value, registry, protocol);

    // TODO(afuller): decide if bool and numeric should have clear()

    using ValueType = decltype(value.value);
    auto addAddTestCase = [&](ValueType toAdd) {
      auto& addCase = test.testCases()->emplace_back();
      addCase.name() =
          fmt::format("{}/add.{}_{}", type::getName<TT>(), value.name, toAdd);
      auto& tadcase = addCase.test().emplace().objectPatch_ref().emplace();
      tadcase = makeAddTest<TT>(value, registry, protocol, toAdd);
    };

    addAddTestCase(1);
    if constexpr (!std::is_same_v<TT, type::bool_t>) {
      addAddTestCase(-1);
      if (value.value > 0) {
        addAddTestCase(-value.value);
      }
    }
  }

  return test;
}

template <typename TT>
Test createStringLikePatchTest(
    const AnyRegistry& registry, const Protocol& protocol) {
  Test test;
  test.name() = protocol.name();

  for (const auto& value : ValueGenerator<TT>::getInterestingValues()) {
    auto& assignCase = test.testCases()->emplace_back();
    assignCase.name() =
        fmt::format("{}/assign.{}", type::getName<TT>(), value.name);
    auto& tascase = assignCase.test().emplace().objectPatch_ref().emplace();
    tascase = makeAssignTest<TT>(value, registry, protocol);

    auto& clearCase = test.testCases()->emplace_back();
    clearCase.name() = fmt::format("{}/clear", type::getName<TT>());
    auto& tclcase = clearCase.test().emplace().objectPatch_ref().emplace();
    tclcase = makeClearTest<TT>(value, registry, protocol);

    auto& prependCase = test.testCases()->emplace_back();
    prependCase.name() =
        fmt::format("{}/prepend.{}", type::getName<TT>(), value.name);
    auto& tprcase = prependCase.test().emplace().objectPatch_ref().emplace();
    tprcase = makePrependTest<TT>(value, registry, protocol);

    auto& appendCase = test.testCases()->emplace_back();
    appendCase.name() =
        fmt::format("{}/append.{}", type::getName<TT>(), value.name);
    auto& tapcase = appendCase.test().emplace().objectPatch_ref().emplace();
    tapcase = makeAppendTest<TT>(value, registry, protocol);
  }

  return test;
}

namespace {
template <typename T>
struct container_from_class {
  static_assert(std::is_same_v<T, type::container_c>, "Not a container class");
};

template <>
struct container_from_class<type::list_c> {
  template <typename ValueType>
  using type = type::list<ValueType>;
};

template <>
struct container_from_class<type::set_c> {
  template <typename ValueType>
  using type = type::set<ValueType>;
};

template <typename C, typename... Types>
using container_from_class_t =
    typename container_from_class<C>::template type<Types...>;

template <typename Tag>
Object patchAddOperation(Object&& patch, op::PatchOp operation, auto value) {
  auto opId = static_cast<int16_t>(operation);
  patch.members().ensure()[opId] = asValueStruct<Tag>(value);
  return std::move(patch);
}

template <typename Tag>
Value makePatchObject(auto operation, auto value) {
  Value result;
  result.objectValue_ref() = patchAddOperation<Tag>(Object{}, operation, value);
  return result;
}

template <typename ContainerTag>
PatchOpTestCase makeValueContainerAssignTC(
    const AnyRegistry& registry, const Protocol& protocol, auto value) {
  PatchOpTestCase tascase;
  PatchOpRequest req;

  using Container = type::standard_type<ContainerTag>;

  Container initial;
  Container expected = {value.value};

  req.value() = registry.store(asValueStruct<ContainerTag>(initial), protocol);
  req.patch() = registry.store(
      makePatchObject<ContainerTag>(op::PatchOp::Assign, expected), protocol);
  tascase.request() = req;
  tascase.result() =
      registry.store(asValueStruct<ContainerTag>(expected), protocol);
  return tascase;
}

template <typename ContainerTag>
PatchOpTestCase makeValueContainerClearTC(
    const AnyRegistry& registry, const Protocol& protocol, auto value) {
  PatchOpTestCase tascase;
  PatchOpRequest req;

  using Container = type::standard_type<ContainerTag>;

  Container initial = {value.value};
  Container expected;

  req.value() = registry.store(asValueStruct<ContainerTag>(initial), protocol);
  req.patch() = registry.store(
      makePatchObject<type::bool_t>(op::PatchOp::Clear, true), protocol);
  tascase.request() = req;
  tascase.result() =
      registry.store(asValueStruct<ContainerTag>(expected), protocol);
  return tascase;
}

template <typename ContainerTag, typename ValueTag>
PatchOpTestCase makeValueContainerRemoveTC(
    const AnyRegistry& registry, const Protocol& protocol, auto value) {
  PatchOpTestCase tascase;
  PatchOpRequest req;

  using Container = type::native_type<ContainerTag>;

  Container initial = {value.value};
  Container expected;

  req.value() = registry.store(asValueStruct<ContainerTag>(initial), protocol);
  req.patch() = registry.store(
      makePatchObject<type::set<ValueTag>>(
          op::PatchOp::Remove,
          std::set<type::standard_type<ValueTag>>{value.value}),
      protocol);
  tascase.request() = req;
  tascase.result() =
      registry.store(asValueStruct<ContainerTag>(expected), protocol);
  return tascase;
}

} // namespace

template <typename TT>
Test createListSetPatchTest(
    const AnyRegistry& registry, const Protocol& protocol) {
  Test test;
  test.name() = protocol.name();

  using ValueContainers = boost::mp11::mp_list<type::list_c, type::set_c>;
  mp11::mp_for_each<ValueContainers>([&](auto cc) {
    using CC = decltype(cc);
    for (const auto& value : ValueGenerator<TT>::getInterestingValues()) {
      using ContainerTag = container_from_class_t<CC, TT>;
      auto& assignCase = test.testCases()->emplace_back();
      assignCase.name() = fmt::format(
          "{}<{}>/assign.{}",
          type::getName<CC>(),
          type::getName<TT>(),
          value.name);
      assignCase.test().emplace().objectPatch_ref() =
          makeValueContainerAssignTC<ContainerTag>(registry, protocol, value);

      auto& clearCase = test.testCases()->emplace_back();
      clearCase.name() = fmt::format(
          "{}<{}>/clear.{}",
          type::getName<CC>(),
          type::getName<TT>(),
          value.name);
      clearCase.test().emplace().objectPatch_ref() =
          makeValueContainerClearTC<ContainerTag>(registry, protocol, value);

      auto& removeCase = test.testCases()->emplace_back();
      removeCase.name() = fmt::format(
          "{}<{}>/remove.{}",
          type::getName<CC>(),
          type::getName<TT>(),
          value.name);
      removeCase.test().emplace().objectPatch_ref() =
          makeValueContainerRemoveTC<ContainerTag, TT>(
              registry, protocol, value);
    }
  });

  return test;
}

void addPatchToSuite(
    const AnyRegistry& registry, const Protocol& protocol, TestSuite& suite) {
  mp11::mp_for_each<detail::PrimaryTypeTags>([&](auto tt) {
    using TT = decltype(tt);
    if constexpr (
        !std::is_same_v<TT, type::string_t> &&
        !std::is_same_v<TT, type::binary_t>) {
      suite.tests()->emplace_back(
          createNumericPatchTest<TT>(registry, protocol));
    } else {
      suite.tests()->emplace_back(
          createStringLikePatchTest<TT>(registry, protocol));
    }

    if constexpr (!std::is_same_v<TT, type::bool_t>) {
      suite.tests()->emplace_back(
          createListSetPatchTest<TT>(registry, protocol));
    }
  });
}
} // namespace

TestSuite createPatchSuite(
    const std::set<Protocol>& protocols, const AnyRegistry& registry) {
  TestSuite suite;
  suite.name() = "PatchTest";
  for (const auto& protocol : protocols) {
    addPatchToSuite(registry, protocol, suite);
  }
  return suite;
}

} // namespace apache::thrift::conformance::data
<|endoftext|>