<commit_before>
#include "term.h"
#include "qutils.h"

#include <sstream>
#include <boost/foreach.hpp>

using namespace std;

Term::Term(const std::vector<string> components)
{
	bool first = true;

	foreach (const string &component, components)
	{
		if (first)
			first = false;
		else
			_asString.append(" \xE2\x9C\xBB ");  // star spoked asterisk

		_asString.append(component);
		_components.append(tq(component));
		_scomponents.push_back(component);
	}

	_asQString = tq(_asString);
}

Term::Term(const string component)
{
	_components.append(tq(component));
	_scomponents.push_back(component);
	_asString = component;
	_asQString = tq(component);
}

Term::Term(const QStringList components)
{
	bool first = true;

	foreach (const QString &component, components)
	{
		if (first)
			first = false;
		else
			_asQString += " \xE2\x9C\xBB ";

		_asQString += component;
		_components.append(component);
		_scomponents.push_back(fq(component));
	}

	_asString = fq(_asQString);
}

Term::Term(const QString component)
{
	_components.append(component);
	_scomponents.push_back(fq(component));
	_asQString = component;
	_asString = fq(component);
}

const QStringList &Term::components() const
{
	return _components;
}

const std::vector<string> &Term::scomponents() const
{
	return _scomponents;
}

const string &Term::asString() const
{
	return _asString;
}

bool Term::contains(const string &component) const
{
	BOOST_FOREACH(const string &termComponent, _scomponents)
	{
		if (component == termComponent)
			return true;
	}

	return false;
}

bool Term::containsAll(const Term &term) const
{
	BOOST_FOREACH(const string &termComponent, term._scomponents)
	{
		if ( ! contains(termComponent))
			return false;
	}

	return true;
}

bool Term::containsAny(const Term &term) const
{
	BOOST_FOREACH(const string &termComponent, _scomponents)
	{
		if (term.contains(termComponent))
			return true;
	}

	return false;
}

const QString &Term::asQString() const
{
	return _asQString;
}

Term::iterator Term::begin()
{
	return _components.begin();
}

Term::iterator Term::end()
{
	return _components.end();
}

const QString &Term::at(int index) const
{
	return _components.at(index);
}

bool Term::operator==(const Term &other) const
{
	if (this == &other)
		return true;

	return components() == other.components();
}

bool Term::operator!=(const Term &other) const
{
	return this->operator==(other) == false;
}

size_t Term::size() const
{
	return _components.size();
}
<commit_msg>Tweak to interaction separator<commit_after>
#include "term.h"
#include "qutils.h"

#include <sstream>
#include <boost/foreach.hpp>

using namespace std;

Term::Term(const std::vector<string> components)
{
	bool first = true;

	foreach (const string &component, components)
	{
		if (first)
			first = false;
		else
#ifdef __WIN32__
			_asString.append(" * ");
#else
			_asString.append(" \xE2\x88\x97 ");
#endif

		_asString.append(component);
		_components.append(tq(component));
		_scomponents.push_back(component);
	}

	_asQString = tq(_asString);
}

Term::Term(const string component)
{
	_components.append(tq(component));
	_scomponents.push_back(component);
	_asString = component;
	_asQString = tq(component);
}

Term::Term(const QStringList components)
{
	bool first = true;

	foreach (const QString &component, components)
	{
		if (first)
			first = false;
		else
#ifdef __WIN32__
			_asQString.append(" * ");
#else
			_asQString.append(" \xE2\x88\x97 ");
#endif

		_asQString += component;
		_components.append(component);
		_scomponents.push_back(fq(component));
	}

	_asString = fq(_asQString);
}

Term::Term(const QString component)
{
	_components.append(component);
	_scomponents.push_back(fq(component));
	_asQString = component;
	_asString = fq(component);
}

const QStringList &Term::components() const
{
	return _components;
}

const std::vector<string> &Term::scomponents() const
{
	return _scomponents;
}

const string &Term::asString() const
{
	return _asString;
}

bool Term::contains(const string &component) const
{
	BOOST_FOREACH(const string &termComponent, _scomponents)
	{
		if (component == termComponent)
			return true;
	}

	return false;
}

bool Term::containsAll(const Term &term) const
{
	BOOST_FOREACH(const string &termComponent, term._scomponents)
	{
		if ( ! contains(termComponent))
			return false;
	}

	return true;
}

bool Term::containsAny(const Term &term) const
{
	BOOST_FOREACH(const string &termComponent, _scomponents)
	{
		if (term.contains(termComponent))
			return true;
	}

	return false;
}

const QString &Term::asQString() const
{
	return _asQString;
}

Term::iterator Term::begin()
{
	return _components.begin();
}

Term::iterator Term::end()
{
	return _components.end();
}

const QString &Term::at(int index) const
{
	return _components.at(index);
}

bool Term::operator==(const Term &other) const
{
	if (this == &other)
		return true;

	return components() == other.components();
}

bool Term::operator!=(const Term &other) const
{
	return this->operator==(other) == false;
}

size_t Term::size() const
{
	return _components.size();
}
<|endoftext|>


<commit_before>#include <rai/node/node.hpp>
#include <rai/node/rpc_secure.hpp>

bool rai::rpc_secure::on_verify_certificate (bool preverified, boost::asio::ssl::verify_context & ctx)
{
	X509_STORE_CTX * cts = ctx.native_handle ();
	auto error (X509_STORE_CTX_get_error (cts));
	switch (error)
	{
		case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
			BOOST_LOG (node.log) << "TLS: Unable to get issuer";
			break;
		case X509_V_ERR_CERT_NOT_YET_VALID:
		case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
			BOOST_LOG (node.log) << "TLS: Certificate not yet valid";
			break;
		case X509_V_ERR_CERT_HAS_EXPIRED:
		case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
			BOOST_LOG (node.log) << "TLS: Certificate expired";
			break;
		case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
			if (config.secure.verbose_logging)
			{
				BOOST_LOG (node.log) << "TLS: self signed certificate in chain";
			}

			// Allow self-signed certificates
			preverified = true;
			break;
		case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
			BOOST_LOG (node.log) << "TLS: Self signed certificate not in the list of trusted certs (forgot to subject-hash certificate filename?)";
			break;
		default:
			break;
	}

	if (config.secure.verbose_logging)
	{
		if (error != 0)
		{
			BOOST_LOG (node.log) << "TLS: Error: " << X509_verify_cert_error_string (error);
			BOOST_LOG (node.log) << "TLS: Error chain depth : " << X509_STORE_CTX_get_error_depth (cts);
		}

		X509 * cert = X509_STORE_CTX_get_current_cert (cts);
		char subject_name[512];
		X509_NAME_oneline (X509_get_subject_name (cert), subject_name, sizeof (subject_name) - 1);
		BOOST_LOG (node.log) << "TLS: Verifying: " << subject_name;
		BOOST_LOG (node.log) << "TLS: Verification: " << preverified;
	}
	else if (!preverified)
	{
		BOOST_LOG (node.log) << "TLS: Pre-verification failed. Turn on verbose logging for more information.";
	}

	return preverified;
}

void rai::rpc_secure::load_certs (boost::asio::ssl::context & context_a)
{
	// This is called if the key is password protected
	context_a.set_password_callback (
	[this](std::size_t,
	boost::asio::ssl::context_base::password_purpose) {
		return config.secure.server_key_passphrase;
	});

	// The following two options disables the session cache and enables stateless session resumption.
	// This is necessary because of the way the RPC server abruptly terminate connections.
	SSL_CTX_set_session_cache_mode (context_a.native_handle (), SSL_SESS_CACHE_OFF);
	SSL_CTX_set_options (context_a.native_handle (), SSL_OP_NO_TICKET);

	context_a.set_options (
	boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::single_dh_use);

	context_a.use_certificate_chain_file (config.secure.server_cert_path);
	context_a.use_private_key_file (config.secure.server_key_path, boost::asio::ssl::context::pem);
	context_a.use_tmp_dh_file (config.secure.server_dh_path);

	// Verify client certificates?
	if (config.secure.client_certs_path.size () > 0)
	{
		context_a.set_verify_mode (boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_peer);
		context_a.add_verify_path (config.secure.client_certs_path);
		context_a.set_verify_callback (boost::bind (&rai::rpc_secure::on_verify_certificate, this, _1, _2));
	}
}

rai::rpc_secure::rpc_secure (boost::asio::io_service & service_a, rai::node & node_a, rai::rpc_config const & config_a) :
rpc (service_a, node_a, config_a),
ssl_context (boost::asio::ssl::context::tlsv12_server)
{
	load_certs (ssl_context);
}

void rai::rpc_secure::accept ()
{
	auto connection (std::make_shared<rai::rpc_connection_secure> (node, *this));
	acceptor.async_accept (connection->socket, [this, connection](boost::system::error_code const & ec) {
		if (acceptor.is_open ())
		{
			accept ();
		}
		if (!ec)
		{
			connection->parse_connection ();
		}
		else
		{
			BOOST_LOG (this->node.log) << boost::str (boost::format ("Error accepting RPC connections: %1%") % ec);
		}
	});
}

rai::rpc_connection_secure::rpc_connection_secure (rai::node & node_a, rai::rpc_secure & rpc_a) :
rai::rpc_connection (node_a, rpc_a),
stream (socket, rpc_a.ssl_context)
{
}

void rai::rpc_connection_secure::parse_connection ()
{
	// Perform the SSL handshake
	stream.async_handshake (boost::asio::ssl::stream_base::server,
	std::bind (
	&rai::rpc_connection_secure::handle_handshake,
	std::static_pointer_cast<rai::rpc_connection_secure> (shared_from_this ()),
	std::placeholders::_1));
}

void rai::rpc_connection_secure::on_shutdown (const boost::system::error_code & error)
{
	// No-op. We initiate the shutdown (since the RPC server kills the connection after each request)
	// and we'll thus get an expected EOF error. If the client disconnects, a short-read error will be expected.
}

void rai::rpc_connection_secure::handle_handshake (const boost::system::error_code & error)
{
	if (!error)
	{
		read ();
	}
	else
	{
		BOOST_LOG (node->log) << "TLS: Handshake error: " << error.message ();
	}
}

void rai::rpc_connection_secure::read ()
{
	auto this_l (std::static_pointer_cast<rai::rpc_connection_secure> (shared_from_this ()));
	boost::beast::http::async_read (stream, buffer, request, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) {
		if (!ec)
		{
			this_l->node->background ([this_l]() {
				auto start (std::chrono::steady_clock::now ());
				auto version (this_l->request.version ());
				std::string request_id (boost::str (boost::format ("%1%") % boost::io::group (std::hex, std::showbase, reinterpret_cast<uintptr_t> (this_l.get ()))));
				auto response_handler ([this_l, version, start, request_id](boost::property_tree::ptree const & tree_a) {
					std::stringstream ostream;
					boost::property_tree::write_json (ostream, tree_a);
					ostream.flush ();
					auto body (ostream.str ());
					this_l->write_result (body, version);
					boost::beast::http::async_write (this_l->stream, this_l->res, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) {
						// Perform the SSL shutdown
						this_l->stream.async_shutdown (
						std::bind (
						&rai::rpc_connection_secure::on_shutdown,
						this_l,
						std::placeholders::_1));
					});

					if (this_l->node->config.logging.log_rpc ())
					{
						BOOST_LOG (this_l->node->log) << boost::str (boost::format ("TLS: RPC request %2% completed in: %1% microseconds") % std::chrono::duration_cast<std::chrono::microseconds> (std::chrono::steady_clock::now () - start).count () % request_id);
					}
				});

				if (this_l->request.method () == boost::beast::http::verb::post)
				{
					auto handler (std::make_shared<rai::rpc_handler> (*this_l->node, this_l->rpc, this_l->request.body (), request_id, response_handler));
					handler->process_request ();
				}
				else
				{
					error_response (response_handler, "Can only POST requests");
				}
			});
		}
		else
		{
			BOOST_LOG (this_l->node->log) << "TLS: Read error: " << ec.message () << std::endl;
		}
	});
}
<commit_msg>Replace bind with lambda (#1498)<commit_after>#include <rai/node/node.hpp>
#include <rai/node/rpc_secure.hpp>

bool rai::rpc_secure::on_verify_certificate (bool preverified, boost::asio::ssl::verify_context & ctx)
{
	X509_STORE_CTX * cts = ctx.native_handle ();
	auto error (X509_STORE_CTX_get_error (cts));
	switch (error)
	{
		case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
			BOOST_LOG (node.log) << "TLS: Unable to get issuer";
			break;
		case X509_V_ERR_CERT_NOT_YET_VALID:
		case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
			BOOST_LOG (node.log) << "TLS: Certificate not yet valid";
			break;
		case X509_V_ERR_CERT_HAS_EXPIRED:
		case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
			BOOST_LOG (node.log) << "TLS: Certificate expired";
			break;
		case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
			if (config.secure.verbose_logging)
			{
				BOOST_LOG (node.log) << "TLS: self signed certificate in chain";
			}

			// Allow self-signed certificates
			preverified = true;
			break;
		case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
			BOOST_LOG (node.log) << "TLS: Self signed certificate not in the list of trusted certs (forgot to subject-hash certificate filename?)";
			break;
		default:
			break;
	}

	if (config.secure.verbose_logging)
	{
		if (error != 0)
		{
			BOOST_LOG (node.log) << "TLS: Error: " << X509_verify_cert_error_string (error);
			BOOST_LOG (node.log) << "TLS: Error chain depth : " << X509_STORE_CTX_get_error_depth (cts);
		}

		X509 * cert = X509_STORE_CTX_get_current_cert (cts);
		char subject_name[512];
		X509_NAME_oneline (X509_get_subject_name (cert), subject_name, sizeof (subject_name) - 1);
		BOOST_LOG (node.log) << "TLS: Verifying: " << subject_name;
		BOOST_LOG (node.log) << "TLS: Verification: " << preverified;
	}
	else if (!preverified)
	{
		BOOST_LOG (node.log) << "TLS: Pre-verification failed. Turn on verbose logging for more information.";
	}

	return preverified;
}

void rai::rpc_secure::load_certs (boost::asio::ssl::context & context_a)
{
	// This is called if the key is password protected
	context_a.set_password_callback (
	[this](std::size_t,
	boost::asio::ssl::context_base::password_purpose) {
		return config.secure.server_key_passphrase;
	});

	// The following two options disables the session cache and enables stateless session resumption.
	// This is necessary because of the way the RPC server abruptly terminate connections.
	SSL_CTX_set_session_cache_mode (context_a.native_handle (), SSL_SESS_CACHE_OFF);
	SSL_CTX_set_options (context_a.native_handle (), SSL_OP_NO_TICKET);

	context_a.set_options (
	boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::single_dh_use);

	context_a.use_certificate_chain_file (config.secure.server_cert_path);
	context_a.use_private_key_file (config.secure.server_key_path, boost::asio::ssl::context::pem);
	context_a.use_tmp_dh_file (config.secure.server_dh_path);

	// Verify client certificates?
	if (config.secure.client_certs_path.size () > 0)
	{
		context_a.set_verify_mode (boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_peer);
		context_a.add_verify_path (config.secure.client_certs_path);
		context_a.set_verify_callback ([this](auto preverified, auto & ctx) {
			return on_verify_certificate (preverified, ctx);
		});
	}
}

rai::rpc_secure::rpc_secure (boost::asio::io_service & service_a, rai::node & node_a, rai::rpc_config const & config_a) :
rpc (service_a, node_a, config_a),
ssl_context (boost::asio::ssl::context::tlsv12_server)
{
	load_certs (ssl_context);
}

void rai::rpc_secure::accept ()
{
	auto connection (std::make_shared<rai::rpc_connection_secure> (node, *this));
	acceptor.async_accept (connection->socket, [this, connection](boost::system::error_code const & ec) {
		if (acceptor.is_open ())
		{
			accept ();
		}
		if (!ec)
		{
			connection->parse_connection ();
		}
		else
		{
			BOOST_LOG (this->node.log) << boost::str (boost::format ("Error accepting RPC connections: %1%") % ec);
		}
	});
}

rai::rpc_connection_secure::rpc_connection_secure (rai::node & node_a, rai::rpc_secure & rpc_a) :
rai::rpc_connection (node_a, rpc_a),
stream (socket, rpc_a.ssl_context)
{
}

void rai::rpc_connection_secure::parse_connection ()
{
	// Perform the SSL handshake
	auto this_l = std::static_pointer_cast<rai::rpc_connection_secure> (shared_from_this ());
	stream.async_handshake (boost::asio::ssl::stream_base::server,
	[this_l](auto & ec) {
		this_l->handle_handshake (ec);
	});
}

void rai::rpc_connection_secure::on_shutdown (const boost::system::error_code & error)
{
	// No-op. We initiate the shutdown (since the RPC server kills the connection after each request)
	// and we'll thus get an expected EOF error. If the client disconnects, a short-read error will be expected.
}

void rai::rpc_connection_secure::handle_handshake (const boost::system::error_code & error)
{
	if (!error)
	{
		read ();
	}
	else
	{
		BOOST_LOG (node->log) << "TLS: Handshake error: " << error.message ();
	}
}

void rai::rpc_connection_secure::read ()
{
	auto this_l (std::static_pointer_cast<rai::rpc_connection_secure> (shared_from_this ()));
	boost::beast::http::async_read (stream, buffer, request, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) {
		if (!ec)
		{
			this_l->node->background ([this_l]() {
				auto start (std::chrono::steady_clock::now ());
				auto version (this_l->request.version ());
				std::string request_id (boost::str (boost::format ("%1%") % boost::io::group (std::hex, std::showbase, reinterpret_cast<uintptr_t> (this_l.get ()))));
				auto response_handler ([this_l, version, start, request_id](boost::property_tree::ptree const & tree_a) {
					std::stringstream ostream;
					boost::property_tree::write_json (ostream, tree_a);
					ostream.flush ();
					auto body (ostream.str ());
					this_l->write_result (body, version);
					boost::beast::http::async_write (this_l->stream, this_l->res, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) {
						// Perform the SSL shutdown
						this_l->stream.async_shutdown ([this_l](auto const & ec_shutdown) {
							this_l->on_shutdown (ec_shutdown);
						});
					});

					if (this_l->node->config.logging.log_rpc ())
					{
						BOOST_LOG (this_l->node->log) << boost::str (boost::format ("TLS: RPC request %2% completed in: %1% microseconds") % std::chrono::duration_cast<std::chrono::microseconds> (std::chrono::steady_clock::now () - start).count () % request_id);
					}
				});

				if (this_l->request.method () == boost::beast::http::verb::post)
				{
					auto handler (std::make_shared<rai::rpc_handler> (*this_l->node, this_l->rpc, this_l->request.body (), request_id, response_handler));
					handler->process_request ();
				}
				else
				{
					error_response (response_handler, "Can only POST requests");
				}
			});
		}
		else
		{
			BOOST_LOG (this_l->node->log) << "TLS: Read error: " << ec.message () << std::endl;
		}
	});
}
<|endoftext|>


<commit_before>/*
This file is part of cpp-ethereum.

cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

cpp-ethereum 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 cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file whisperMessage.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date July 2015
*/

#include <boost/test/unit_test.hpp>
#include <libwhisper/WhisperDB.h>

using namespace std;
using namespace dev;
using namespace dev::shh;

BOOST_AUTO_TEST_SUITE(whisperDB)

BOOST_AUTO_TEST_CASE(first)
{
	VerbosityHolder setTemporaryLevel(10);
	cnote << "Testing Whisper DB...";

	WhisperDB db;

	h256 h1(0x12345678);
	h256 h2(0xBADD00DE);

	string s;
	string const text1 = "lorem_ipsum";
	string const text2 = "dolor_sit_amet";

	db.erase(h1);
	db.erase(h2);

	db.put(h1, text2);
	s = db.get(h2);
	BOOST_REQUIRE(s.empty());
	s = db.get(h1);
	BOOST_REQUIRE(!s.compare(text2));

	db.put(h1, text1);
	s = db.get(h2);
	BOOST_REQUIRE(s.empty());
	s = db.get(h1);
	BOOST_REQUIRE(!s.compare(text1));

	db.put(h2, text2);
	s = db.get(h2);
	BOOST_REQUIRE(!s.compare(text2));
	s = db.get(h1);
	BOOST_REQUIRE(!s.compare(text1));

	db.erase(h1);
	db.erase(h2);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>naming conventions changed<commit_after>/*
This file is part of cpp-ethereum.

cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

cpp-ethereum 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 cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file whisperMessage.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date July 2015
*/

#include <boost/test/unit_test.hpp>
#include <libwhisper/WhisperDB.h>

using namespace std;
using namespace dev;
using namespace dev::shh;

BOOST_AUTO_TEST_SUITE(whisperDB)

BOOST_AUTO_TEST_CASE(first)
{
	VerbosityHolder setTemporaryLevel(10);
	cnote << "Testing Whisper DB...";

	WhisperDB db;

	h256 h1(0x12345678);
	h256 h2(0xBADD00DE);

	string s;
	string const text1 = "lorem_ipsum";
	string const text2 = "dolor_sit_amet";

	db.kill(h1);
	db.kill(h2);

	db.insert(h1, text2);
	s = db.lookup(h2);
	BOOST_REQUIRE(s.empty());
	s = db.lookup(h1);
	BOOST_REQUIRE(!s.compare(text2));

	db.insert(h1, text1);
	s = db.lookup(h2);
	BOOST_REQUIRE(s.empty());
	s = db.lookup(h1);
	BOOST_REQUIRE(!s.compare(text1));

	db.insert(h2, text2);
	s = db.lookup(h2);
	BOOST_REQUIRE(!s.compare(text2));
	s = db.lookup(h1);
	BOOST_REQUIRE(!s.compare(text1));

	db.kill(h1);
	db.kill(h2);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>#pragma once
/*
 * Copyright (c) 2012 Aldebaran Robotics. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the COPYING file.
 */

#ifndef _QI_ATOMIC_HPP_
#define _QI_ATOMIC_HPP_

#ifdef _MSC_VER
# include <windows.h>
# include <intrin.h>

extern "C" long __cdecl _InterlockedIncrement(long volatile *);
extern "C" long __cdecl _InterlockedDecrement(long volatile *);

# pragma intrinsic(_InterlockedIncrement)
# pragma intrinsic(_InterlockedDecrement)

#endif

#include <boost/static_assert.hpp>

#include <qi/config.hpp>
#include <qi/macro.hpp>

namespace qi
{
  inline long testAndSet(long* cond)
  {
#if defined __GNUC__
    return __sync_bool_compare_and_swap(cond, 0, 1);
#elif defined _MSC_VER
    return 1 - InterlockedCompareExchange(cond, 1, 0);
#else
  #error "Unknown platform, testAndSet not implemented"
#endif
  }

  /* /!\ WARNING
  * The 'volatile' is needed even though we use atomic compiler builtins.
  * Without the volatile, a thread doing
  *    while (!setIfEquals(1,1))
  * Is never unstuck by a thread doing
  *    setIfEquals(0,1)
  *
  * AtomicBase has public member so that it can be initialized at
  * static-initialization time (to make thread-safe static initialization inside
  * functions)
  */
  template <typename T>
  struct AtomicBase
  {
  public:

    /* prefix operators */
    inline T operator++();
    inline T operator--();
    inline AtomicBase<T>& operator=(T value);
    /** If value is \p testValue, replace it with \p setValue.
     * \return true if swap was performed
     */
    inline bool setIfEquals(T testValue, T setValue);

    inline T swap(T value);

    inline T operator*()
    {
      return _value;
    }

  public:
    BOOST_STATIC_ASSERT_MSG(sizeof(T) == sizeof(int), "qi::Atomic is only supprted for int-like types");

    volatile
#ifdef _MSC_VER
    long
#else
    T
#endif
       _value;
  };

  template <typename T>
  class Atomic: public AtomicBase<T>
  {
  public:
    Atomic()
    {
      this->_value = 0;
    }

    Atomic(T value)
    {
      this->_value = value;
    }
  };
#ifdef __GNUC__
    template <typename T>
    inline T AtomicBase<T>::operator++()
    {
      return __sync_add_and_fetch(&_value, 1);
    }

    template <typename T>
    inline T AtomicBase<T>::operator--()
    {
      return __sync_sub_and_fetch(&_value, 1);
    }

    template <typename T>
    inline AtomicBase<T>& AtomicBase<T>::operator=(T value)
    {
      __sync_lock_test_and_set(&_value, value);
      return *this;
    }

    template <typename T>
    inline T AtomicBase<T>::swap(T value)
    {
      return __sync_lock_test_and_set(&_value, value);
    }
    template <typename T>
    inline bool AtomicBase<T>::setIfEquals(T testValue, T setValue)
    {
      return __sync_bool_compare_and_swap(&_value, testValue, setValue);
    }
#endif

#ifdef _MSC_VER

  template <>
  inline int AtomicBase<int>::operator++()
  {
    return _InterlockedIncrement(&_value);
  }

  template <>
  inline int AtomicBase<int>::operator--()
  {
    return _InterlockedDecrement(&_value);
  }

  template<>
  inline AtomicBase<int>& AtomicBase<int>::operator=(int value)
  {
    InterlockedExchange(&_value, value);
    return *this;
  }

  template<>
  inline int AtomicBase<int>::swap(int value)
  {
    return InterlockedExchange(&_value, value);
  }

  template <>
  inline bool AtomicBase<int>::setIfEquals(int testValue, int setValue)
  {
    return _InterlockedCompareExchange(&_value, setValue, testValue) == testValue;
  }

  template <>
  inline unsigned int AtomicBase<unsigned int>::operator++()
  {
    return _InterlockedIncrement(&_value);
  }

  template <>
  inline unsigned int AtomicBase<unsigned int>::operator--()
  {
    return _InterlockedDecrement(&_value);
  }

  template<>
  inline AtomicBase<unsigned int>& AtomicBase<unsigned int>::operator=(unsigned int value)
  {
    InterlockedExchange(&_value, value);
    return *this;
  }

  template<>
  inline unsigned int AtomicBase<unsigned int>::swap(unsigned int value)
  {
    return InterlockedExchange(&_value, value);
  }

  template <>
  inline bool AtomicBase<unsigned int>::setIfEquals(unsigned int testValue, unsigned int setValue)
  {
    return _InterlockedCompareExchange(&_value, setValue, testValue) == testValue;
  }
#endif

}

#define _QI_INSTANCIATE(_, a, elem) ::qi::details::newAndAssign(&elem);

/* The code below relies on the fact that initialisation of the qi::Atomic
* can happen at static initialization time, and that proper memory barriers
* are setup by its ++, swap and get operations.
 */
/** Accept a list of pointers (expected to be static function variables)
 *  and new them once in a thrad-safe manner.
 *  Implementation aims for minimal overhead when initialization is done.
 */
#define QI_THREADSAFE_NEW(...)  \
 QI_ONCE(QI_VAARGS_APPLY(_QI_INSTANCIATE, _, __VA_ARGS__);)

/// Execute code once, parallel calls are blocked until code finishes.
#define QI_ONCE(code) \
 static qi::AtomicBase<int> QI_UNIQ_DEF(atomic_guard_a) = {0}; \
 static qi::AtomicBase<int> QI_UNIQ_DEF(atomic_guard_b) = {0}; \
 while (!QI_UNIQ_DEF(atomic_guard_a).setIfEquals(1, 1))       \
 {                                                           \
   bool tok = QI_UNIQ_DEF(atomic_guard_b).setIfEquals(0,1);  \
   if (tok)                                                  \
   {                                                    \
     code;                                              \
     ++QI_UNIQ_DEF(atomic_guard_a);                     \
   }                                                    \
 }

#endif  // _QI_ATOMIC_HPP_
<commit_msg>Atomic: mark dereference operator const.<commit_after>#pragma once
/*
 * Copyright (c) 2012 Aldebaran Robotics. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the COPYING file.
 */

#ifndef _QI_ATOMIC_HPP_
#define _QI_ATOMIC_HPP_

#ifdef _MSC_VER
# include <windows.h>
# include <intrin.h>

extern "C" long __cdecl _InterlockedIncrement(long volatile *);
extern "C" long __cdecl _InterlockedDecrement(long volatile *);

# pragma intrinsic(_InterlockedIncrement)
# pragma intrinsic(_InterlockedDecrement)

#endif

#include <boost/static_assert.hpp>

#include <qi/config.hpp>
#include <qi/macro.hpp>

namespace qi
{
  inline long testAndSet(long* cond)
  {
#if defined __GNUC__
    return __sync_bool_compare_and_swap(cond, 0, 1);
#elif defined _MSC_VER
    return 1 - InterlockedCompareExchange(cond, 1, 0);
#else
  #error "Unknown platform, testAndSet not implemented"
#endif
  }

  /* /!\ WARNING
  * The 'volatile' is needed even though we use atomic compiler builtins.
  * Without the volatile, a thread doing
  *    while (!setIfEquals(1,1))
  * Is never unstuck by a thread doing
  *    setIfEquals(0,1)
  *
  * AtomicBase has public member so that it can be initialized at
  * static-initialization time (to make thread-safe static initialization inside
  * functions)
  */
  template <typename T>
  struct AtomicBase
  {
  public:

    /* prefix operators */
    inline T operator++();
    inline T operator--();
    inline AtomicBase<T>& operator=(T value);
    /** If value is \p testValue, replace it with \p setValue.
     * \return true if swap was performed
     */
    inline bool setIfEquals(T testValue, T setValue);

    inline T swap(T value);

    inline T operator*() const
    {
      return _value;
    }

  public:
    BOOST_STATIC_ASSERT_MSG(sizeof(T) == sizeof(int), "qi::Atomic is only supprted for int-like types");

    volatile
#ifdef _MSC_VER
    long
#else
    T
#endif
       _value;
  };

  template <typename T>
  class Atomic: public AtomicBase<T>
  {
  public:
    Atomic()
    {
      this->_value = 0;
    }

    Atomic(T value)
    {
      this->_value = value;
    }
  };
#ifdef __GNUC__
    template <typename T>
    inline T AtomicBase<T>::operator++()
    {
      return __sync_add_and_fetch(&_value, 1);
    }

    template <typename T>
    inline T AtomicBase<T>::operator--()
    {
      return __sync_sub_and_fetch(&_value, 1);
    }

    template <typename T>
    inline AtomicBase<T>& AtomicBase<T>::operator=(T value)
    {
      __sync_lock_test_and_set(&_value, value);
      return *this;
    }

    template <typename T>
    inline T AtomicBase<T>::swap(T value)
    {
      return __sync_lock_test_and_set(&_value, value);
    }
    template <typename T>
    inline bool AtomicBase<T>::setIfEquals(T testValue, T setValue)
    {
      return __sync_bool_compare_and_swap(&_value, testValue, setValue);
    }
#endif

#ifdef _MSC_VER

  template <>
  inline int AtomicBase<int>::operator++()
  {
    return _InterlockedIncrement(&_value);
  }

  template <>
  inline int AtomicBase<int>::operator--()
  {
    return _InterlockedDecrement(&_value);
  }

  template<>
  inline AtomicBase<int>& AtomicBase<int>::operator=(int value)
  {
    InterlockedExchange(&_value, value);
    return *this;
  }

  template<>
  inline int AtomicBase<int>::swap(int value)
  {
    return InterlockedExchange(&_value, value);
  }

  template <>
  inline bool AtomicBase<int>::setIfEquals(int testValue, int setValue)
  {
    return _InterlockedCompareExchange(&_value, setValue, testValue) == testValue;
  }

  template <>
  inline unsigned int AtomicBase<unsigned int>::operator++()
  {
    return _InterlockedIncrement(&_value);
  }

  template <>
  inline unsigned int AtomicBase<unsigned int>::operator--()
  {
    return _InterlockedDecrement(&_value);
  }

  template<>
  inline AtomicBase<unsigned int>& AtomicBase<unsigned int>::operator=(unsigned int value)
  {
    InterlockedExchange(&_value, value);
    return *this;
  }

  template<>
  inline unsigned int AtomicBase<unsigned int>::swap(unsigned int value)
  {
    return InterlockedExchange(&_value, value);
  }

  template <>
  inline bool AtomicBase<unsigned int>::setIfEquals(unsigned int testValue, unsigned int setValue)
  {
    return _InterlockedCompareExchange(&_value, setValue, testValue) == testValue;
  }
#endif

}

#define _QI_INSTANCIATE(_, a, elem) ::qi::details::newAndAssign(&elem);

/* The code below relies on the fact that initialisation of the qi::Atomic
* can happen at static initialization time, and that proper memory barriers
* are setup by its ++, swap and get operations.
 */
/** Accept a list of pointers (expected to be static function variables)
 *  and new them once in a thrad-safe manner.
 *  Implementation aims for minimal overhead when initialization is done.
 */
#define QI_THREADSAFE_NEW(...)  \
 QI_ONCE(QI_VAARGS_APPLY(_QI_INSTANCIATE, _, __VA_ARGS__);)

/// Execute code once, parallel calls are blocked until code finishes.
#define QI_ONCE(code) \
 static qi::AtomicBase<int> QI_UNIQ_DEF(atomic_guard_a) = {0}; \
 static qi::AtomicBase<int> QI_UNIQ_DEF(atomic_guard_b) = {0}; \
 while (!QI_UNIQ_DEF(atomic_guard_a).setIfEquals(1, 1))       \
 {                                                           \
   bool tok = QI_UNIQ_DEF(atomic_guard_b).setIfEquals(0,1);  \
   if (tok)                                                  \
   {                                                    \
     code;                                              \
     ++QI_UNIQ_DEF(atomic_guard_a);                     \
   }                                                    \
 }

#endif  // _QI_ATOMIC_HPP_
<|endoftext|>


<commit_before>/*
  Q Light Controller Plus
  app.cpp

  Copyright (c) Massimo Callegari

  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.txt

  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 <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QFontDatabase>
#include <QQmlContext>
#include <QQuickItem>
#include <QSettings>
#include <QKeyEvent>
#include <QScreen>

#include "app.h"
#include "mainview2d.h"
#include "showmanager.h"
#include "actionmanager.h"
#include "modelselector.h"
#include "videoprovider.h"
#include "contextmanager.h"
#include "virtualconsole.h"
#include "fixturebrowser.h"
#include "fixturemanager.h"
#include "functionmanager.h"
#include "fixturegroupeditor.h"
#include "inputoutputmanager.h"

#include "qlcfixturedefcache.h"
#include "audioplugincache.h"
#include "rgbscriptscache.h"
#include "qlcfixturedef.h"
#include "qlcconfig.h"
#include "qlcfile.h"

#define SETTINGS_WORKINGPATH "workspace/workingpath"
#define SETTINGS_RECENTFILE "workspace/recent"

#define MAX_RECENT_FILES    10

App::App()
    : QQuickView()
    , m_fixtureBrowser(NULL)
    , m_fixtureManager(NULL)
    , m_contextManager(NULL)
    , m_ioManager(NULL)
    , m_videoProvider(NULL)
    , m_doc(NULL)
    , m_docLoaded(false)
{
    QSettings settings;

    updateRecentFilesList();

    QVariant dir = settings.value(SETTINGS_WORKINGPATH);
    if (dir.isValid() == true)
        m_workingPath = dir.toString();
}

App::~App()
{

}

void App::startup()
{
    qmlRegisterType<Fixture>("com.qlcplus.classes", 1, 0, "Fixture");
    qmlRegisterType<Function>("com.qlcplus.classes", 1, 0, "Function");
    qmlRegisterType<ModelSelector>("com.qlcplus.classes", 1, 0, "ModelSelector");
    qmlRegisterType<App>("com.qlcplus.classes", 1, 0, "App");

    setTitle("Q Light Controller Plus");
    setIcon(QIcon(":/qlcplus.svg"));

    if (QFontDatabase::addApplicationFont(":/RobotoCondensed-Regular.ttf") < 0)
        qWarning() << "Roboto font cannot be loaded !";

    rootContext()->setContextProperty("qlcplus", this);

    m_pixelDensity = screen()->physicalDotsPerInch() *  0.039370;
    qDebug() << "Pixel density:" << m_pixelDensity;

    rootContext()->setContextProperty("screenPixelDensity", m_pixelDensity);

    initDoc();

    m_ioManager = new InputOutputManager(this, m_doc);
    rootContext()->setContextProperty("ioManager", m_ioManager);

    m_fixtureBrowser = new FixtureBrowser(this, m_doc);
    rootContext()->setContextProperty("fixtureBrowser", m_fixtureBrowser);

    m_fixtureManager = new FixtureManager(this, m_doc);
    rootContext()->setContextProperty("fixtureManager", m_fixtureManager);

    m_fixtureGroupEditor = new FixtureGroupEditor(this, m_doc);
    rootContext()->setContextProperty("fixtureGroupEditor", m_fixtureGroupEditor);

    m_functionManager = new FunctionManager(this, m_doc);
    rootContext()->setContextProperty("functionManager", m_functionManager);

    m_contextManager = new ContextManager(this, m_doc, m_fixtureManager, m_functionManager);
    rootContext()->setContextProperty("contextManager", m_contextManager);

    m_virtualConsole = new VirtualConsole(this, m_doc, m_contextManager);
    rootContext()->setContextProperty("virtualConsole", m_virtualConsole);

    m_showManager = new ShowManager(this, m_doc);
    rootContext()->setContextProperty("showManager", m_showManager);

    // register an uncreatable type just to use the enums in QML
    qmlRegisterUncreatableType<ShowManager>("com.qlcplus.classes", 1, 0, "ShowManager", "Can't create a ShowManager !");

    m_actionManager = new ActionManager(this, m_functionManager, m_showManager, m_virtualConsole);
    rootContext()->setContextProperty("actionManager", m_actionManager);

    // register an uncreatable type just to use the enums in QML
    qmlRegisterUncreatableType<ActionManager>("com.qlcplus.classes", 1, 0,  "ActionManager", "Can't create an ActionManager !");

    m_contextManager->registerContext(m_virtualConsole);
    m_contextManager->registerContext(m_showManager);
    m_contextManager->registerContext(m_ioManager);

    // Start up in non-modified state
    m_doc->resetModified();

    // and here we go !
    setSource(QUrl("qrc:/MainView.qml"));
}

void App::toggleFullscreen()
{
    static int wstate = windowState();

    if (windowState() & Qt::WindowFullScreen)
    {
        if (wstate & Qt::WindowMaximized)
            showMaximized();
        else
            showNormal();
        wstate = windowState();
    }
    else
    {
        wstate = windowState();
        showFullScreen();
    }
}

void App::show()
{
    setGeometry(0, 0, 800, 600);
    //setGeometry(0, 0, 1272, 689); // youtube recording
    showMaximized();
}

qreal App::pixelDensity() const
{
    return m_pixelDensity;
}

void App::keyPressEvent(QKeyEvent *e)
{
    if (m_contextManager)
        m_contextManager->handleKeyPress(e);

    QQuickView::keyPressEvent(e);
}

void App::keyReleaseEvent(QKeyEvent *e)
{
    if (m_contextManager)
        m_contextManager->handleKeyRelease(e);

    QQuickView::keyReleaseEvent(e);
}

void App::clearDocument()
{
    if (m_videoProvider)
    {
        delete m_videoProvider;
        m_videoProvider = NULL;
    }

    m_doc->masterTimer()->stop();
    m_doc->clearContents();
    m_virtualConsole->resetContents();
    //SimpleDesk::instance()->clearContents();
    m_showManager->resetContents();
    m_doc->inputOutputMap()->resetUniverses();
    setFileName(QString());
    m_doc->resetModified();
    m_doc->masterTimer()->start();

}

Doc *App::doc()
{
    return m_doc;
}

void App::slotDocModified(bool state)
{
    Q_UNUSED(state)
}

void App::initDoc()
{
    Q_ASSERT(m_doc == NULL);
    m_doc = new Doc(this);

    connect(m_doc, &Doc::modified, this, &App::slotDocModified);

    /* Load user fixtures first so that they override system fixtures */
    m_doc->fixtureDefCache()->load(QLCFixtureDefCache::userDefinitionDirectory());
    m_doc->fixtureDefCache()->loadMap(QLCFixtureDefCache::systemDefinitionDirectory());

    /* Load channel modifiers templates */
    m_doc->modifiersCache()->load(QLCModifiersCache::systemTemplateDirectory(), true);
    m_doc->modifiersCache()->load(QLCModifiersCache::userTemplateDirectory());

    /* Load RGB scripts */
    m_doc->rgbScriptsCache()->load(RGBScriptsCache::systemScriptsDirectory());
    m_doc->rgbScriptsCache()->load(RGBScriptsCache::userScriptsDirectory());

    /* Load plugins */
#if defined(__APPLE__) || defined(Q_OS_MAC)
    connect(m_doc->ioPluginCache(), SIGNAL(pluginLoaded(const QString&)),
            this, SLOT(slotSetProgressText(const QString&)));
#endif
#if defined Q_OS_ANDROID
    QString pluginsPath = QString("%1/../lib").arg(QDir::currentPath());
    m_doc->ioPluginCache()->load(QDir(pluginsPath));
#else
    m_doc->ioPluginCache()->load(IOPluginCache::systemPluginDirectory());
#endif

    /* Load audio decoder plugins
     * This doesn't use a AudioPluginCache::systemPluginDirectory() cause
     * otherwise the qlcconfig.h creation should have been moved into the
     * audio folder, which doesn't make much sense */
    m_doc->audioPluginCache()->load(QLCFile::systemDirectory(AUDIOPLUGINDIR, KExtPlugin));
    m_videoProvider = new VideoProvider(this, m_doc);

    Q_ASSERT(m_doc->inputOutputMap() != NULL);

    /* Load input plugins & profiles */
    m_doc->inputOutputMap()->loadProfiles(InputOutputMap::userProfileDirectory());
    m_doc->inputOutputMap()->loadProfiles(InputOutputMap::systemProfileDirectory());
    m_doc->inputOutputMap()->loadDefaults();

    m_doc->inputOutputMap()->setBeatGeneratorType(InputOutputMap::Internal);
    m_doc->masterTimer()->start();
}

void App::enableKioskMode()
{

}

void App::createKioskCloseButton(const QRect &rect)
{
    Q_UNUSED(rect)
}

/*********************************************************************
 * Load & Save
 *********************************************************************/

void App::setFileName(const QString &fileName)
{
    m_fileName = fileName;
}

QString App::fileName() const
{
    return m_fileName;
}

void App::updateRecentFilesList(QString filename)
{
    QSettings settings;
    if (filename.isEmpty() == false)
    {
        m_recentFiles.removeAll(filename); // in case the string is already present, remove it...
        m_recentFiles.prepend(filename); // and add it to the top
        for (int i = 0; i < m_recentFiles.count(); i++)
        {
            settings.setValue(QString("%1%2").arg(SETTINGS_RECENTFILE).arg(i), m_recentFiles.at(i));
            emit recentFilesChanged();
        }
    }
    else
    {
        for (int i = 0; i < MAX_RECENT_FILES; i++)
        {
            QVariant recent = settings.value(QString("%1%2").arg(SETTINGS_RECENTFILE).arg(i));
            if (recent.isValid() == true)
                m_recentFiles.append(recent.toString());
        }
    }
}

QStringList App::recentFiles() const
{
    return m_recentFiles;
}

QString App::workingPath() const
{
    return m_workingPath;
}

void App::setWorkingPath(QString workingPath)
{
    QString strippedPath = workingPath.replace("file://", "");

    if (m_workingPath == strippedPath)
        return;

    m_workingPath = strippedPath;

    QSettings settings;
    settings.setValue(SETTINGS_WORKINGPATH, m_workingPath);

    emit workingPathChanged(strippedPath);
}

bool App::newWorkspace()
{
    /*
    QString msg(tr("Do you wish to save the current workspace?\n" \
                   "Changes will be lost if you don't save them."));
    if (saveModifiedDoc(tr("New Workspace"), msg) == false)
    {
        return false;
    }
    */

    clearDocument();
    m_fixtureManager->slotDocLoaded();
    m_functionManager->slotDocLoaded();
    m_contextManager->resetContexts();
    return true;
}

bool App::loadWorkspace(const QString &fileName)
{
    /* Clear existing document data */
    clearDocument();
    m_docLoaded = false;
    emit docLoadedChanged();

    QString localFilename =  fileName;
    if (localFilename.startsWith("file:"))
        localFilename = QUrl(fileName).toLocalFile();

    if (loadXML(localFilename) == QFile::NoError)
    {
        setTitle(QString("Q Light Controller Plus - %1").arg(localFilename));
        setFileName(localFilename);
        m_docLoaded = true;
        updateRecentFilesList(localFilename);
        emit docLoadedChanged();
        m_contextManager->resetContexts();
        m_doc->resetModified();
        m_videoProvider = new VideoProvider(this, m_doc);
        return true;
    }
    return false;
}

QFileDevice::FileError App::loadXML(const QString &fileName)
{
    QFile::FileError retval = QFile::NoError;

    if (fileName.isEmpty() == true)
        return QFile::OpenError;

    QXmlStreamReader *doc = QLCFile::getXMLReader(fileName);
    if (doc == NULL || doc->device() == NULL || doc->hasError())
    {
        qWarning() << Q_FUNC_INFO << "Unable to read from" << fileName;
        return QFile::ReadError;
    }

    while (!doc->atEnd())
    {
        if (doc->readNext() == QXmlStreamReader::DTD)
            break;
    }
    if (doc->hasError())
    {
        QLCFile::releaseXMLReader(doc);
        return QFile::ResourceError;
    }

    /* Set the workspace path before loading the new XML. In this way local files
       can be loaded even if the workspace file has been moved */
    m_doc->setWorkspacePath(QFileInfo(fileName).absolutePath());

    if (doc->dtdName() == KXMLQLCWorkspace)
    {
        if (loadXML(*doc) == false)
        {
            retval = QFile::ReadError;
        }
        else
        {
            setFileName(fileName);
            m_doc->resetModified();
            retval = QFile::NoError;
        }
    }
    else
    {
        retval = QFile::ReadError;
        qWarning() << Q_FUNC_INFO << fileName
                   << "is not a workspace file";
    }

    QLCFile::releaseXMLReader(doc);

    return retval;
}

bool App::loadXML(QXmlStreamReader &doc, bool goToConsole, bool fromMemory)
{
    Q_UNUSED(goToConsole) // TODO
    if (doc.readNextStartElement() == false)
        return false;

    if (doc.name() != KXMLQLCWorkspace)
    {
        qWarning() << Q_FUNC_INFO << "Workspace node not found";
        return false;
    }

    //QString activeWindowName = doc.attributes().value(KXMLQLCWorkspaceWindow).toString();

    while (doc.readNextStartElement())
    {
        if (doc.name() == KXMLQLCEngine)
        {
            m_doc->loadXML(doc);
        }
        else if (doc.name() == KXMLQLCVirtualConsole)
        {
            m_virtualConsole->loadXML(doc);
        }
#if 0
        else if (doc.name() == KXMLQLCSimpleDesk)
        {
            SimpleDesk::instance()->loadXML(doc);
        }
#endif
        else if (doc.name() == KXMLQLCCreator)
        {
            /* Ignore creator information */
            doc.skipCurrentElement();
        }
        else
        {
            qWarning() << Q_FUNC_INFO << "Unknown Workspace tag:" << doc.name().toString();
            doc.skipCurrentElement();
        }
    }

/*
    if (goToConsole == true)
        // Force the active window to be Virtual Console
        setActiveWindow(VirtualConsole::staticMetaObject.className());
    else
        // Set the active window to what was saved in the workspace file
        setActiveWindow(activeWindowName);
*/
    // Perform post-load operations
    m_virtualConsole->postLoad();

    if (m_doc->errorLog().isEmpty() == false &&
        fromMemory == false)
    {
        // emit a signal to inform the QML UI to display an error message
        /*
        QMessageBox msg(QMessageBox::Warning, tr("Warning"),
                        tr("Some errors occurred while loading the project:") + "\n\n" + m_doc->errorLog(),
                        QMessageBox::Ok);
        msg.exec();
        */
    }

    return true;
}

<commit_msg>qmlui: use the APPNAME define instead of hardcoded strings<commit_after>/*
  Q Light Controller Plus
  app.cpp

  Copyright (c) Massimo Callegari

  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.txt

  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 <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QFontDatabase>
#include <QQmlContext>
#include <QQuickItem>
#include <QSettings>
#include <QKeyEvent>
#include <QScreen>

#include "app.h"
#include "mainview2d.h"
#include "showmanager.h"
#include "actionmanager.h"
#include "modelselector.h"
#include "videoprovider.h"
#include "contextmanager.h"
#include "virtualconsole.h"
#include "fixturebrowser.h"
#include "fixturemanager.h"
#include "functionmanager.h"
#include "fixturegroupeditor.h"
#include "inputoutputmanager.h"

#include "qlcfixturedefcache.h"
#include "audioplugincache.h"
#include "rgbscriptscache.h"
#include "qlcfixturedef.h"
#include "qlcconfig.h"
#include "qlcfile.h"

#define SETTINGS_WORKINGPATH "workspace/workingpath"
#define SETTINGS_RECENTFILE "workspace/recent"

#define MAX_RECENT_FILES    10

App::App()
    : QQuickView()
    , m_fixtureBrowser(NULL)
    , m_fixtureManager(NULL)
    , m_contextManager(NULL)
    , m_ioManager(NULL)
    , m_videoProvider(NULL)
    , m_doc(NULL)
    , m_docLoaded(false)
{
    QSettings settings;

    updateRecentFilesList();

    QVariant dir = settings.value(SETTINGS_WORKINGPATH);
    if (dir.isValid() == true)
        m_workingPath = dir.toString();
}

App::~App()
{

}

void App::startup()
{
    qmlRegisterType<Fixture>("com.qlcplus.classes", 1, 0, "Fixture");
    qmlRegisterType<Function>("com.qlcplus.classes", 1, 0, "Function");
    qmlRegisterType<ModelSelector>("com.qlcplus.classes", 1, 0, "ModelSelector");
    qmlRegisterType<App>("com.qlcplus.classes", 1, 0, "App");

    setTitle(APPNAME);
    setIcon(QIcon(":/qlcplus.svg"));

    if (QFontDatabase::addApplicationFont(":/RobotoCondensed-Regular.ttf") < 0)
        qWarning() << "Roboto font cannot be loaded !";

    rootContext()->setContextProperty("qlcplus", this);

    m_pixelDensity = screen()->physicalDotsPerInch() *  0.039370;
    qDebug() << "Pixel density:" << m_pixelDensity;

    rootContext()->setContextProperty("screenPixelDensity", m_pixelDensity);

    initDoc();

    m_ioManager = new InputOutputManager(this, m_doc);
    rootContext()->setContextProperty("ioManager", m_ioManager);

    m_fixtureBrowser = new FixtureBrowser(this, m_doc);
    rootContext()->setContextProperty("fixtureBrowser", m_fixtureBrowser);

    m_fixtureManager = new FixtureManager(this, m_doc);
    rootContext()->setContextProperty("fixtureManager", m_fixtureManager);

    m_fixtureGroupEditor = new FixtureGroupEditor(this, m_doc);
    rootContext()->setContextProperty("fixtureGroupEditor", m_fixtureGroupEditor);

    m_functionManager = new FunctionManager(this, m_doc);
    rootContext()->setContextProperty("functionManager", m_functionManager);

    m_contextManager = new ContextManager(this, m_doc, m_fixtureManager, m_functionManager);
    rootContext()->setContextProperty("contextManager", m_contextManager);

    m_virtualConsole = new VirtualConsole(this, m_doc, m_contextManager);
    rootContext()->setContextProperty("virtualConsole", m_virtualConsole);

    m_showManager = new ShowManager(this, m_doc);
    rootContext()->setContextProperty("showManager", m_showManager);

    // register an uncreatable type just to use the enums in QML
    qmlRegisterUncreatableType<ShowManager>("com.qlcplus.classes", 1, 0, "ShowManager", "Can't create a ShowManager !");

    m_actionManager = new ActionManager(this, m_functionManager, m_showManager, m_virtualConsole);
    rootContext()->setContextProperty("actionManager", m_actionManager);

    // register an uncreatable type just to use the enums in QML
    qmlRegisterUncreatableType<ActionManager>("com.qlcplus.classes", 1, 0,  "ActionManager", "Can't create an ActionManager !");

    m_contextManager->registerContext(m_virtualConsole);
    m_contextManager->registerContext(m_showManager);
    m_contextManager->registerContext(m_ioManager);

    // Start up in non-modified state
    m_doc->resetModified();

    // and here we go !
    setSource(QUrl("qrc:/MainView.qml"));
}

void App::toggleFullscreen()
{
    static int wstate = windowState();

    if (windowState() & Qt::WindowFullScreen)
    {
        if (wstate & Qt::WindowMaximized)
            showMaximized();
        else
            showNormal();
        wstate = windowState();
    }
    else
    {
        wstate = windowState();
        showFullScreen();
    }
}

void App::show()
{
    setGeometry(0, 0, 800, 600);
    //setGeometry(0, 0, 1272, 689); // youtube recording
    showMaximized();
}

qreal App::pixelDensity() const
{
    return m_pixelDensity;
}

void App::keyPressEvent(QKeyEvent *e)
{
    if (m_contextManager)
        m_contextManager->handleKeyPress(e);

    QQuickView::keyPressEvent(e);
}

void App::keyReleaseEvent(QKeyEvent *e)
{
    if (m_contextManager)
        m_contextManager->handleKeyRelease(e);

    QQuickView::keyReleaseEvent(e);
}

void App::clearDocument()
{
    if (m_videoProvider)
    {
        delete m_videoProvider;
        m_videoProvider = NULL;
    }

    m_doc->masterTimer()->stop();
    m_doc->clearContents();
    m_virtualConsole->resetContents();
    //SimpleDesk::instance()->clearContents();
    m_showManager->resetContents();
    m_doc->inputOutputMap()->resetUniverses();
    setFileName(QString());
    m_doc->resetModified();
    m_doc->masterTimer()->start();

}

Doc *App::doc()
{
    return m_doc;
}

void App::slotDocModified(bool state)
{
    Q_UNUSED(state)
}

void App::initDoc()
{
    Q_ASSERT(m_doc == NULL);
    m_doc = new Doc(this);

    connect(m_doc, &Doc::modified, this, &App::slotDocModified);

    /* Load user fixtures first so that they override system fixtures */
    m_doc->fixtureDefCache()->load(QLCFixtureDefCache::userDefinitionDirectory());
    m_doc->fixtureDefCache()->loadMap(QLCFixtureDefCache::systemDefinitionDirectory());

    /* Load channel modifiers templates */
    m_doc->modifiersCache()->load(QLCModifiersCache::systemTemplateDirectory(), true);
    m_doc->modifiersCache()->load(QLCModifiersCache::userTemplateDirectory());

    /* Load RGB scripts */
    m_doc->rgbScriptsCache()->load(RGBScriptsCache::systemScriptsDirectory());
    m_doc->rgbScriptsCache()->load(RGBScriptsCache::userScriptsDirectory());

    /* Load plugins */
#if defined(__APPLE__) || defined(Q_OS_MAC)
    connect(m_doc->ioPluginCache(), SIGNAL(pluginLoaded(const QString&)),
            this, SLOT(slotSetProgressText(const QString&)));
#endif
#if defined Q_OS_ANDROID
    QString pluginsPath = QString("%1/../lib").arg(QDir::currentPath());
    m_doc->ioPluginCache()->load(QDir(pluginsPath));
#else
    m_doc->ioPluginCache()->load(IOPluginCache::systemPluginDirectory());
#endif

    /* Load audio decoder plugins
     * This doesn't use a AudioPluginCache::systemPluginDirectory() cause
     * otherwise the qlcconfig.h creation should have been moved into the
     * audio folder, which doesn't make much sense */
    m_doc->audioPluginCache()->load(QLCFile::systemDirectory(AUDIOPLUGINDIR, KExtPlugin));
    m_videoProvider = new VideoProvider(this, m_doc);

    Q_ASSERT(m_doc->inputOutputMap() != NULL);

    /* Load input plugins & profiles */
    m_doc->inputOutputMap()->loadProfiles(InputOutputMap::userProfileDirectory());
    m_doc->inputOutputMap()->loadProfiles(InputOutputMap::systemProfileDirectory());
    m_doc->inputOutputMap()->loadDefaults();

    m_doc->inputOutputMap()->setBeatGeneratorType(InputOutputMap::Internal);
    m_doc->masterTimer()->start();
}

void App::enableKioskMode()
{

}

void App::createKioskCloseButton(const QRect &rect)
{
    Q_UNUSED(rect)
}

/*********************************************************************
 * Load & Save
 *********************************************************************/

void App::setFileName(const QString &fileName)
{
    m_fileName = fileName;
}

QString App::fileName() const
{
    return m_fileName;
}

void App::updateRecentFilesList(QString filename)
{
    QSettings settings;
    if (filename.isEmpty() == false)
    {
        m_recentFiles.removeAll(filename); // in case the string is already present, remove it...
        m_recentFiles.prepend(filename); // and add it to the top
        for (int i = 0; i < m_recentFiles.count(); i++)
        {
            settings.setValue(QString("%1%2").arg(SETTINGS_RECENTFILE).arg(i), m_recentFiles.at(i));
            emit recentFilesChanged();
        }
    }
    else
    {
        for (int i = 0; i < MAX_RECENT_FILES; i++)
        {
            QVariant recent = settings.value(QString("%1%2").arg(SETTINGS_RECENTFILE).arg(i));
            if (recent.isValid() == true)
                m_recentFiles.append(recent.toString());
        }
    }
}

QStringList App::recentFiles() const
{
    return m_recentFiles;
}

QString App::workingPath() const
{
    return m_workingPath;
}

void App::setWorkingPath(QString workingPath)
{
    QString strippedPath = workingPath.replace("file://", "");

    if (m_workingPath == strippedPath)
        return;

    m_workingPath = strippedPath;

    QSettings settings;
    settings.setValue(SETTINGS_WORKINGPATH, m_workingPath);

    emit workingPathChanged(strippedPath);
}

bool App::newWorkspace()
{
    /*
    QString msg(tr("Do you wish to save the current workspace?\n" \
                   "Changes will be lost if you don't save them."));
    if (saveModifiedDoc(tr("New Workspace"), msg) == false)
    {
        return false;
    }
    */

    clearDocument();
    m_fixtureManager->slotDocLoaded();
    m_functionManager->slotDocLoaded();
    m_contextManager->resetContexts();
    return true;
}

bool App::loadWorkspace(const QString &fileName)
{
    /* Clear existing document data */
    clearDocument();
    m_docLoaded = false;
    emit docLoadedChanged();

    QString localFilename =  fileName;
    if (localFilename.startsWith("file:"))
        localFilename = QUrl(fileName).toLocalFile();

    if (loadXML(localFilename) == QFile::NoError)
    {
        setTitle(QString("%1 - %2").arg(APPNAME).arg(localFilename));
        setFileName(localFilename);
        m_docLoaded = true;
        updateRecentFilesList(localFilename);
        emit docLoadedChanged();
        m_contextManager->resetContexts();
        m_doc->resetModified();
        m_videoProvider = new VideoProvider(this, m_doc);
        return true;
    }
    return false;
}

QFileDevice::FileError App::loadXML(const QString &fileName)
{
    QFile::FileError retval = QFile::NoError;

    if (fileName.isEmpty() == true)
        return QFile::OpenError;

    QXmlStreamReader *doc = QLCFile::getXMLReader(fileName);
    if (doc == NULL || doc->device() == NULL || doc->hasError())
    {
        qWarning() << Q_FUNC_INFO << "Unable to read from" << fileName;
        return QFile::ReadError;
    }

    while (!doc->atEnd())
    {
        if (doc->readNext() == QXmlStreamReader::DTD)
            break;
    }
    if (doc->hasError())
    {
        QLCFile::releaseXMLReader(doc);
        return QFile::ResourceError;
    }

    /* Set the workspace path before loading the new XML. In this way local files
       can be loaded even if the workspace file has been moved */
    m_doc->setWorkspacePath(QFileInfo(fileName).absolutePath());

    if (doc->dtdName() == KXMLQLCWorkspace)
    {
        if (loadXML(*doc) == false)
        {
            retval = QFile::ReadError;
        }
        else
        {
            setFileName(fileName);
            m_doc->resetModified();
            retval = QFile::NoError;
        }
    }
    else
    {
        retval = QFile::ReadError;
        qWarning() << Q_FUNC_INFO << fileName
                   << "is not a workspace file";
    }

    QLCFile::releaseXMLReader(doc);

    return retval;
}

bool App::loadXML(QXmlStreamReader &doc, bool goToConsole, bool fromMemory)
{
    Q_UNUSED(goToConsole) // TODO
    if (doc.readNextStartElement() == false)
        return false;

    if (doc.name() != KXMLQLCWorkspace)
    {
        qWarning() << Q_FUNC_INFO << "Workspace node not found";
        return false;
    }

    //QString activeWindowName = doc.attributes().value(KXMLQLCWorkspaceWindow).toString();

    while (doc.readNextStartElement())
    {
        if (doc.name() == KXMLQLCEngine)
        {
            m_doc->loadXML(doc);
        }
        else if (doc.name() == KXMLQLCVirtualConsole)
        {
            m_virtualConsole->loadXML(doc);
        }
#if 0
        else if (doc.name() == KXMLQLCSimpleDesk)
        {
            SimpleDesk::instance()->loadXML(doc);
        }
#endif
        else if (doc.name() == KXMLQLCCreator)
        {
            /* Ignore creator information */
            doc.skipCurrentElement();
        }
        else
        {
            qWarning() << Q_FUNC_INFO << "Unknown Workspace tag:" << doc.name().toString();
            doc.skipCurrentElement();
        }
    }

/*
    if (goToConsole == true)
        // Force the active window to be Virtual Console
        setActiveWindow(VirtualConsole::staticMetaObject.className());
    else
        // Set the active window to what was saved in the workspace file
        setActiveWindow(activeWindowName);
*/
    // Perform post-load operations
    m_virtualConsole->postLoad();

    if (m_doc->errorLog().isEmpty() == false &&
        fromMemory == false)
    {
        // emit a signal to inform the QML UI to display an error message
        /*
        QMessageBox msg(QMessageBox::Warning, tr("Warning"),
                        tr("Some errors occurred while loading the project:") + "\n\n" + m_doc->errorLog(),
                        QMessageBox::Ok);
        msg.exec();
        */
    }

    return true;
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 David Wicks, sansumbrella.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "puptent/SpriteSystem.h"
#include "puptent/RenderMesh.h"
#include "cinder/Json.h"

using namespace puptent;
using namespace cinder;
using namespace std;

SpriteAnimationSystemRef SpriteAnimationSystem::create( TextureAtlasRef atlas, const ci::JsonTree &animations )
{
  return SpriteAnimationSystemRef{ new SpriteAnimationSystem{ atlas, animations } };
}

SpriteAnimationSystem::SpriteAnimationSystem( TextureAtlasRef atlas, const JsonTree &animations ):
mAtlas( atlas )
{
  try
  {
    for( auto &anim : animations )
    {
      vector<Drawing> drawings;
      float frame_duration = 1.0f / anim.getChild("fps").getValue<float>();
      string key = anim.getKey();
      auto frames = anim.getChild("frames");
      for( auto &child : frames.getChildren() )
      { // stored in json as [ "id", duration ]
        drawings.emplace_back( mAtlas->get(child[0].getValue()), child[1].getValue<float>() );
      }
      mAnimations.emplace_back( Animation{ key, drawings, frame_duration } );
      mAnimationIds[key] = mAnimations.size() - 1;
    }
  }
  catch( JsonTree::Exception &exc )
  {
    std::cout << __FUNCTION__ << " error: " << exc.what() << std::endl;
  }
}

void SpriteAnimationSystem::configure( EventManagerRef events )
{
  std::cout << __PRETTY_FUNCTION__ << std::endl;
  events->subscribe<ComponentAddedEvent<SpriteAnimation>>( *this );
}

void SpriteAnimationSystem::addAnimation(const string &name, const Animation &animation)
{
  mAnimations.emplace_back( animation );
  mAnimationIds[name] = mAnimations.size() - 1;
}

AnimationId SpriteAnimationSystem::getAnimationId( const string &name ) const
{
  AnimationId index = 0;
  auto iter = mAnimationIds.find( name );
  if( iter != mAnimationIds.end() )
  {
    index = iter->second;
  }
  return index;
}

SpriteAnimationRef SpriteAnimationSystem::createSpriteAnimation(const string &name) const
{
  return createSpriteAnimation( getAnimationId( name ) );
}

SpriteAnimationRef SpriteAnimationSystem::createSpriteAnimation(AnimationId animation_id ) const
{
  return SpriteAnimationRef{ new SpriteAnimation{ animation_id } };
}

void SpriteAnimationSystem::receive(const ComponentAddedEvent<SpriteAnimation> &event)
{ // track the sprite
  auto entity = event.entity;
  auto mesh = entity.component<RenderMesh>();
  if( mesh )
  {
    auto sprite = event.component;
    auto drawings = mAnimations.at( sprite->animation ).drawings;
    sprite->current_index = math<int>::clamp( sprite->current_index, 0, drawings.size() - 1 );
    mesh->matchTexture( drawings.at( sprite->current_index ).drawing );
  }
}

void SpriteAnimationSystem::update( EntityManagerRef es, EventManagerRef events, double dt )
{
  for( auto entity : es->entities_with_components<SpriteAnimation, RenderMesh>() )
  {
    auto sprite = entity.component<SpriteAnimation>();

    const auto &anim = mAnimations.at( sprite->animation );
    const auto &current_drawing = anim.drawings.at( sprite->current_index );
    sprite->hold += dt; // this becomes a problem if many share the same sprite
    int next_index = sprite->current_index;
    // check timing
    if( sprite->hold > anim.frame_duration * current_drawing.hold )
    { // move to next frame
      next_index += 1;
      sprite->hold = 0.0f;
    }
    else if ( sprite->hold < 0.0f )
    { // step back a frame
      next_index -= 1;
      sprite->hold = anim.frame_duration * current_drawing.hold;
    }
    // handle wrapping around beginning and end
    if( next_index >= static_cast<int>( anim.drawings.size() ) )
    { // handle wraparound at end
      next_index = sprite->looping ? 0 : anim.drawings.size() - 1;
      if( sprite->finish_fn ){ sprite->finish_fn(); }
    }
    else if( next_index < 0 )
    { // handle wraparound at beginning
      next_index = sprite->looping ? anim.drawings.size() - 1 : 0;
      if( sprite->finish_fn ){ sprite->finish_fn(); }
    }
    // actually change the drawing
    if( next_index != sprite->current_index )
    { // the frame index has changed, update display
      sprite->current_index = next_index;
      const auto new_drawing = anim.drawings.at( sprite->current_index ).drawing;
      auto mesh = entity.component<RenderMesh>();
      if( mesh ){ mesh->matchTexture( new_drawing ); }
    }
  }
}
<commit_msg>Sprite update is safe in the case that finish_fn destroys entity.<commit_after>/*
 * Copyright (c) 2013 David Wicks, sansumbrella.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "puptent/SpriteSystem.h"
#include "puptent/RenderMesh.h"
#include "cinder/Json.h"

using namespace puptent;
using namespace cinder;
using namespace std;

SpriteAnimationSystemRef SpriteAnimationSystem::create( TextureAtlasRef atlas, const ci::JsonTree &animations )
{
  return SpriteAnimationSystemRef{ new SpriteAnimationSystem{ atlas, animations } };
}

SpriteAnimationSystem::SpriteAnimationSystem( TextureAtlasRef atlas, const JsonTree &animations ):
mAtlas( atlas )
{
  try
  {
    for( auto &anim : animations )
    {
      vector<Drawing> drawings;
      float frame_duration = 1.0f / anim.getChild("fps").getValue<float>();
      string key = anim.getKey();
      auto frames = anim.getChild("frames");
      for( auto &child : frames.getChildren() )
      { // stored in json as [ "id", duration ]
        drawings.emplace_back( mAtlas->get(child[0].getValue()), child[1].getValue<float>() );
      }
      mAnimations.emplace_back( Animation{ key, drawings, frame_duration } );
      mAnimationIds[key] = mAnimations.size() - 1;
    }
  }
  catch( JsonTree::Exception &exc )
  {
    std::cout << __FUNCTION__ << " error: " << exc.what() << std::endl;
  }
}

void SpriteAnimationSystem::configure( EventManagerRef events )
{
  std::cout << __PRETTY_FUNCTION__ << std::endl;
  events->subscribe<ComponentAddedEvent<SpriteAnimation>>( *this );
}

void SpriteAnimationSystem::addAnimation(const string &name, const Animation &animation)
{
  mAnimations.emplace_back( animation );
  mAnimationIds[name] = mAnimations.size() - 1;
}

AnimationId SpriteAnimationSystem::getAnimationId( const string &name ) const
{
  AnimationId index = 0;
  auto iter = mAnimationIds.find( name );
  if( iter != mAnimationIds.end() )
  {
    index = iter->second;
  }
  return index;
}

SpriteAnimationRef SpriteAnimationSystem::createSpriteAnimation(const string &name) const
{
  return createSpriteAnimation( getAnimationId( name ) );
}

SpriteAnimationRef SpriteAnimationSystem::createSpriteAnimation(AnimationId animation_id ) const
{
  return SpriteAnimationRef{ new SpriteAnimation{ animation_id } };
}

void SpriteAnimationSystem::receive(const ComponentAddedEvent<SpriteAnimation> &event)
{ // track the sprite
  auto entity = event.entity;
  auto mesh = entity.component<RenderMesh>();
  if( mesh )
  {
    auto sprite = event.component;
    auto drawings = mAnimations.at( sprite->animation ).drawings;
    sprite->current_index = math<int>::clamp( sprite->current_index, 0, drawings.size() - 1 );
    mesh->matchTexture( drawings.at( sprite->current_index ).drawing );
  }
}

void SpriteAnimationSystem::update( EntityManagerRef es, EventManagerRef events, double dt )
{
  for( auto entity : es->entities_with_components<SpriteAnimation, RenderMesh>() )
  {
    auto sprite = entity.component<SpriteAnimation>();
    auto mesh = entity.component<RenderMesh>();

    const auto &anim = mAnimations.at( sprite->animation );
    const auto &current_drawing = anim.drawings.at( sprite->current_index );
    sprite->hold += dt; // this becomes a problem if many share the same sprite
    int next_index = sprite->current_index;
    // check timing
    if( sprite->hold > anim.frame_duration * current_drawing.hold )
    { // move to next frame
      next_index += 1;
      sprite->hold = 0.0f;
    }
    else if ( sprite->hold < 0.0f )
    { // step back a frame
      next_index -= 1;
      sprite->hold = anim.frame_duration * current_drawing.hold;
    }
    // handle wrapping around beginning and end
    if( next_index >= static_cast<int>( anim.drawings.size() ) )
    { // handle wraparound at end
      next_index = sprite->looping ? 0 : anim.drawings.size() - 1;
      if( sprite->finish_fn ){ sprite->finish_fn(); }
    }
    else if( next_index < 0 )
    { // handle wraparound at beginning
      next_index = sprite->looping ? anim.drawings.size() - 1 : 0;
      if( sprite->finish_fn ){ sprite->finish_fn(); }
    }
    // actually change the drawing
    if( next_index != sprite->current_index )
    { // the frame index has changed, update display
      sprite->current_index = next_index;
      const auto &next_drawing = anim.drawings.at( sprite->current_index ).drawing;
      if( mesh ){ mesh->matchTexture( next_drawing ); }
    }
  }
}
<|endoftext|>


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

  This source file is part of the Avogadro project.

  Copyright 2015 Marcus Johansson <mcodev31@gmail.com>

  This source code is released under the New BSD License, (the "License").

  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 "symmetrywidget.h"
#include "richtextdelegate.h"
#include "symmetryutil.h"
#include "ui_symmetrywidget.h"

#include <avogadro/qtgui/molecule.h>

#include <QtCore/QDebug>
#include <QtCore/QRegExp>
#include <QtWidgets/QPlainTextEdit>

using Avogadro::QtGui::Molecule;

using namespace msym;
using namespace Avogadro::QtPlugins::SymmetryUtil;

namespace Avogadro {
namespace QtPlugins {

msym_thresholds_t tight_thresholds = { // all defaults
  /*.zero =*/1.0e-3,
  /*.geometry =*/1.0e-3,
  /*.angle =*/1.0e-3,
  /*.equivalence =*/5.0e-4,
  /*.eigfact =*/1.0e-3,
  /*.permutation =*/5.0e-3,
  /*.orthogonalization =*/0.1
};

msym_thresholds_t medium_thresholds = {
  /*.zero =*/1.0e-2,
  /*.geometry =*/1.0e-2,
  /*.angle =*/1.0e-2,
  /*.equivalence =*/6.3e-3,
  /*.eigfact =*/1.0e-3,
  /*.permutation =*/1.58e-2,
  /*.orthogonalization =*/0.1
};

msym_thresholds_t loose_thresholds = {
  /*.zero =*/0.06,
  /*.geometry =*/0.06,
  /*.angle =*/0.06,
  /*.equivalence =*/0.025,
  /*.eigfact =*/1.0e-3,
  /*.permutation =*/1.0e-1,
  /*.orthogonalization =*/0.1
};

msym_thresholds_t sloppy_thresholds = {
  /*.zero =*/0.08,
  /*.geometry =*/0.1,
  /*.angle =*/0.1,
  /*.equivalence =*/0.06,
  /*.eigfact =*/1.0e-3,
  /*.permutation =*/1.0e-1,
  /*.orthogonalization =*/0.1
};

SymmetryWidget::SymmetryWidget(QWidget* parent_)
  : QWidget(parent_), m_ui(new Ui::SymmetryWidget), m_molecule(NULL),
    m_equivalenceTreeModel(new QStandardItemModel(this)),
    m_operationsTableModel(new OperationsTableModel(this)),
    m_subgroupsTreeModel(new QStandardItemModel(this)),
    m_es(NULL), m_sops(NULL), m_sg(NULL), m_sopsl(0), m_sgl(0), m_radius(0.0)
{
  setWindowFlags(Qt::Dialog);
  m_ui->setupUi(this);

  m_ui->equivalenceTree->setModel(m_equivalenceTreeModel);

  m_ui->operationsTable->setModel(m_operationsTableModel);
  m_ui->operationsTable->setItemDelegateForColumn(
    OperationsTableModel::ColumnType, new RichTextDelegate(this));

  m_ui->subgroupsTree->setModel(m_subgroupsTreeModel);
  m_ui->subgroupsTree->setItemDelegateForColumn(0, new RichTextDelegate(this));

  connect(m_ui->detectSymmetryButton, SIGNAL(clicked()),
          SIGNAL(detectSymmetry()));
  connect(m_ui->symmetrizeMoleculeButton, SIGNAL(clicked()),
          SIGNAL(symmetrizeMolecule()));

  connect(
    m_ui->subgroupsTree->selectionModel(),
    SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
    SLOT(
      equivalenceSelectionChanged(const QItemSelection&, const QItemSelection&)));

  connect(
    m_ui->operationsTable->selectionModel(),
    SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
    SLOT(operationsSelectionChanged(const QItemSelection&,
                                    const QItemSelection&)));
  connect(
    m_ui->subgroupsTree->selectionModel(),
    SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
    SLOT(
      subgroupsSelectionChanged(const QItemSelection&, const QItemSelection&)));
}

SymmetryWidget::~SymmetryWidget()
{
  delete m_ui;
}

void SymmetryWidget::setMolecule(QtGui::Molecule* molecule)
{
  if (molecule != m_molecule) {
    if (m_molecule)
      m_molecule->disconnect(this);

    m_molecule = molecule;

    if (m_molecule) {
      connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint)));
    }
  }
}

void SymmetryWidget::moleculeChanged(unsigned int changes)
{
  /*
  if (changes & Molecule::UnitCell)
    revert();*/
}

void SymmetryWidget::operationsSelectionChanged(
  const QItemSelection& selected, const QItemSelection& deselected)
{

  if (!m_molecule)
    return;
  if (m_ui->tabWidget->currentWidget() != m_ui->subgroupsTab) {
    qDebug() << "subgroupsTab not selected";
    m_ui->subgroupsTree->selectionModel()->reset();
  } else
    qDebug() << "subgroupsTab selected";

  QModelIndexList selection =
    m_ui->operationsTable->selectionModel()->selectedRows();

  qDebug() << "operations changed";

  qDebug() << "selection " << selection.size();

  QVariantList reflectionVariantList;
  QVariantList properRotationVariantList;
  QVariantList improperRotationVariantList;

  m_molecule->setProperty("SymmetryOrigo", QVariant());
  m_molecule->setProperty("SymmetryRadius", QVariant());
  m_molecule->setProperty("SymmetryInversion", QVariant());
  m_molecule->setProperty("SymmetryProperRotationVariantList", QVariant());
  m_molecule->setProperty("SymmetryImproperRotationVariantList", QVariant());
  m_molecule->setProperty("SymmetryReflectionVariantList", QVariant());

  qDebug() << "cleared elements";

  if (m_sopsl > 0 && selection.size() > 0) {
    m_molecule->setProperty("SymmetryOrigo", m_cm);
    m_molecule->setProperty("SymmetryRadius", m_radius);
  }

  foreach (QModelIndex i, selection) {
    unsigned int row = i.row();
    if (!i.isValid() || row >= m_sopsl)
      continue;
    float x = m_sops[row].v[0], y = m_sops[row].v[1], z = m_sops[row].v[2];
    switch (m_sops[row].type) {
      case IDENTITY:
        break;
      case PROPER_ROTATION:
        properRotationVariantList.append(QVector3D(x, y, z));
        break;
      case IMPROPER_ROTATION:
        improperRotationVariantList.append(QVector3D(x, y, z));
        break;
      case REFLECTION:
        reflectionVariantList.append(QVector3D(x, y, z));
        break;
      case INVERSION:
        m_molecule->setProperty("SymmetryInversion", m_cm);
        break;
      default:
        break;
    }
  }
  if (properRotationVariantList.size() > 0)
    m_molecule->setProperty("SymmetryProperRotationVariantList",
                            properRotationVariantList);
  if (improperRotationVariantList.size() > 0)
    m_molecule->setProperty("SymmetryImproperRotationVariantList",
                            improperRotationVariantList);
  if (reflectionVariantList.size() > 0)
    m_molecule->setProperty("SymmetryReflectionVariantList",
                            reflectionVariantList);

  /* A little bit ugly, but it'll do for now */
  m_molecule->emitChanged(QtGui::Molecule::Atoms);
}

void SymmetryWidget::subgroupsSelectionChanged(const QItemSelection& selected,
                                               const QItemSelection& deselected)
{
  // QModelIndexList selection =
  // m_ui->subgroupsTree->selectionModel()->selectedIndexes();
  QModelIndex i =
    m_ui->subgroupsTree->selectionModel()->selectedIndexes().first();
  qDebug() << "subgroupsSelectionChanged";
  if (!i.isValid())
    return;
  qDebug() << "valid";
  int sgi = i.data(Qt::UserRole).value<int>();
  qDebug() << "index " << sgi;
  if (sgi < 0 || sgi >= m_sgl)
    return;
  const msym::msym_subgroup_t* sg = &m_sg[sgi];
  // m_ui->operationsTable->selectionModel()->clear();

  QItemSelectionModel* selectionModel = m_ui->operationsTable->selectionModel();
  // selectionModel->clear();

  QItemSelection selection;

  for (int j = 0; j < sg->order; j++) {
    int row = static_cast<int>(sg->sops[j] - m_sops);
    QModelIndex left = m_operationsTableModel->index(row, 0);
    QModelIndex right = m_operationsTableModel->index(
      row, m_operationsTableModel->columnCount(left) - 1);
    if (!left.isValid() || !right.isValid())
      qDebug() << "invalid index " << j;
    QItemSelection sel(left, right);

    selection.merge(sel, QItemSelectionModel::Select);
  }

  QModelIndexList tmp = selection.indexes();
  foreach (QModelIndex j, tmp) {
    qDebug() << "selecting " << j.row() << " " << j.column();
  }

  selectionModel->select(selection, QItemSelectionModel::ClearAndSelect);
}

void SymmetryWidget::equivalenceSelectionChanged(const QItemSelection& selected,
                                               const QItemSelection& deselected)
{
  QModelIndex i =
    m_ui->equivalenceTree->selectionModel()->selectedIndexes().first();
  qDebug() << "equivalenceSelectionChanged";
  if (!i.isValid())
    return;
  int atomInGroup = i.data(Qt::UserRole).value<int>();
  QModelIndex g = i.parent();
  if (!g.isValid())
    return;
  int group = g.data(Qt::UserRole).value<int>();

  qDebug() << "valid " << group << atomInGroup;
  if (group < 0 || group >= m_esl)
    return;

  // TODO: okay, now we have to find the atoms and select them
  if (!m_molecule)
    return;

  const msym_equivalence_set_t *smes = &m_es[group];
  const msym_element_t *a = smes->elements[atomInGroup];
  if (a == NULL)
    return;

  unsigned int length = m_molecule->atomCount();
  for (Index i = 0; i < length; ++i) {
    if (m_molecule->atomicNumbers()[i] != a->n)
      continue;

    Vector3 ipos = m_molecule->atomPositions3d()[i];
    if (a->v[0] == ipos[0]
        && a->v[1] == ipos[1]
        && a->v[2] == ipos[2])
      m_molecule->setAtomSelected(i, true);
  }
}

void SymmetryWidget::setRadius(double radius)
{
  m_radius = radius;
}

void SymmetryWidget::setCenterOfMass(double cm[3])
{
  m_cm = QVector3D(cm[0], cm[1], cm[2]);
}

void SymmetryWidget::setPointGroupSymbol(QString pg)
{
  m_ui->pointGroupLabel->setText(pg);
}

void SymmetryWidget::setSymmetryOperations(
  int sopsl, const msym::msym_symmetry_operation_t* sops)
{
  m_sops = sops;
  m_sopsl = sopsl;
  m_operationsTableModel->setOperations(sopsl, sops);
  m_molecule->setProperty("SymmetryOrigo", QVariant());
  m_molecule->setProperty("SymmetryRadius", QVariant());
  m_molecule->setProperty("SymmetryInversion", QVariant());
  m_molecule->setProperty("SymmetryProperRotationVariantList", QVariant());
  m_molecule->setProperty("SymmetryImproperRotationVariantList", QVariant());
  m_molecule->setProperty("SymmetryReflectionVariantList", QVariant());
  /* need another change event */
  m_molecule->emitChanged(QtGui::Molecule::Atoms);
}

void SymmetryWidget::setEquivalenceSets(int esl,
  const msym::msym_equivalence_set_t* es)
{
  m_esl = esl;
  m_es = es;
  m_equivalenceTreeModel->clear();
  for (int i = 0; i < esl; i++) {
    QStandardItem* const parent = new QStandardItem;
    QString label = tr("Group %1").arg(QString::number(i + 1));
    parent->setText(label);
    parent->setData(i, Qt::UserRole);
    m_equivalenceTreeModel->appendRow(parent);
    const msym_equivalence_set_t *smes = &es[i];
    for (int j = 0; j < smes->length; j++) {
      QStandardItem* const child = new QStandardItem;
      label = tr("%1 %2").arg(smes->elements[j]->name)
                 .arg(QString::number(j + 1));
      child->setText(label);
      child->setData(j, Qt::UserRole);
      parent->appendRow(child);
    }
  }
}

void SymmetryWidget::setSubgroups(int sgl, const msym::msym_subgroup_t* sg)
{
  m_sg = sg;
  m_sgl = sgl;
  m_subgroupsTreeModel->clear();
  for (int i = 0; i < sgl; i++) {
    if (sg[i].order <= 2)
      continue;
    QStandardItem* const parent = new QStandardItem;
    parent->setText(pointGroupSymbol(sg[i].name));
    parent->setData(i, Qt::UserRole);
    m_subgroupsTreeModel->appendRow(parent);
    for (int j = 0; j < 2; j++) {
      if (sg[i].generators[j] == NULL)
        continue;
      qDebug() << "child " << sg[i].generators[j] - m_sg << " "
               << sg[i].generators[j] << " " << m_sg;
      QStandardItem* const child = new QStandardItem;
      child->setText(pointGroupSymbol(sg[i].generators[j]->name));

      child->setData(static_cast<int>(sg[i].generators[j] - m_sg),
                     Qt::UserRole);
      parent->appendRow(child);
    }
  }
}

msym_thresholds_t* SymmetryWidget::getThresholds() const
{
  msym_thresholds_t* thresholds = NULL;
  switch (m_ui->toleranceCombo->currentIndex()) {
    case 2: // loose
      thresholds = &loose_thresholds;
      break;
    case 1: // normal
      thresholds = &medium_thresholds;
      break;
    case 0: // tight
    default:
      thresholds = &tight_thresholds;
  }
  return thresholds;
}

} // namespace QtPlugins
} // namespace Avogadro
<commit_msg>Add support for selecting individual atoms in a group.<commit_after>/******************************************************************************

  This source file is part of the Avogadro project.

  Copyright 2015 Marcus Johansson <mcodev31@gmail.com>

  This source code is released under the New BSD License, (the "License").

  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 "symmetrywidget.h"
#include "richtextdelegate.h"
#include "symmetryutil.h"
#include "ui_symmetrywidget.h"

#include <avogadro/qtgui/molecule.h>

#include <QtCore/QDebug>
#include <QtCore/QRegExp>
#include <QtWidgets/QPlainTextEdit>

using Avogadro::QtGui::Molecule;

using namespace msym;
using namespace Avogadro::QtPlugins::SymmetryUtil;

namespace Avogadro {
namespace QtPlugins {

msym_thresholds_t tight_thresholds = { // all defaults
  /*.zero =*/1.0e-3,
  /*.geometry =*/1.0e-3,
  /*.angle =*/1.0e-3,
  /*.equivalence =*/5.0e-4,
  /*.eigfact =*/1.0e-3,
  /*.permutation =*/5.0e-3,
  /*.orthogonalization =*/0.1
};

msym_thresholds_t medium_thresholds = {
  /*.zero =*/1.0e-2,
  /*.geometry =*/1.0e-2,
  /*.angle =*/1.0e-2,
  /*.equivalence =*/6.3e-3,
  /*.eigfact =*/1.0e-3,
  /*.permutation =*/1.58e-2,
  /*.orthogonalization =*/0.1
};

msym_thresholds_t loose_thresholds = {
  /*.zero =*/0.06,
  /*.geometry =*/0.06,
  /*.angle =*/0.06,
  /*.equivalence =*/0.025,
  /*.eigfact =*/1.0e-3,
  /*.permutation =*/1.0e-1,
  /*.orthogonalization =*/0.1
};

msym_thresholds_t sloppy_thresholds = {
  /*.zero =*/0.08,
  /*.geometry =*/0.1,
  /*.angle =*/0.1,
  /*.equivalence =*/0.06,
  /*.eigfact =*/1.0e-3,
  /*.permutation =*/1.0e-1,
  /*.orthogonalization =*/0.1
};

SymmetryWidget::SymmetryWidget(QWidget* parent_)
  : QWidget(parent_), m_ui(new Ui::SymmetryWidget), m_molecule(NULL),
    m_equivalenceTreeModel(new QStandardItemModel(this)),
    m_operationsTableModel(new OperationsTableModel(this)),
    m_subgroupsTreeModel(new QStandardItemModel(this)),
    m_es(NULL), m_sops(NULL), m_sg(NULL), m_sopsl(0), m_sgl(0), m_radius(0.0)
{
  setWindowFlags(Qt::Dialog);
  m_ui->setupUi(this);

  m_ui->equivalenceTree->setModel(m_equivalenceTreeModel);

  m_ui->operationsTable->setModel(m_operationsTableModel);
  m_ui->operationsTable->setItemDelegateForColumn(
    OperationsTableModel::ColumnType, new RichTextDelegate(this));

  m_ui->subgroupsTree->setModel(m_subgroupsTreeModel);
  m_ui->subgroupsTree->setItemDelegateForColumn(0, new RichTextDelegate(this));

  connect(m_ui->detectSymmetryButton, SIGNAL(clicked()),
          SIGNAL(detectSymmetry()));
  connect(m_ui->symmetrizeMoleculeButton, SIGNAL(clicked()),
          SIGNAL(symmetrizeMolecule()));

  connect(
    m_ui->equivalenceTree->selectionModel(),
    SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
    SLOT(
      equivalenceSelectionChanged(const QItemSelection&, const QItemSelection&)));

  connect(
    m_ui->operationsTable->selectionModel(),
    SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
    SLOT(operationsSelectionChanged(const QItemSelection&,
                                    const QItemSelection&)));
  connect(
    m_ui->subgroupsTree->selectionModel(),
    SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
    SLOT(
      subgroupsSelectionChanged(const QItemSelection&, const QItemSelection&)));
}

SymmetryWidget::~SymmetryWidget()
{
  delete m_ui;
}

void SymmetryWidget::setMolecule(QtGui::Molecule* molecule)
{
  if (molecule != m_molecule) {
    if (m_molecule)
      m_molecule->disconnect(this);

    m_molecule = molecule;

    if (m_molecule) {
      connect(m_molecule, SIGNAL(changed(uint)), SLOT(moleculeChanged(uint)));
    }
  }
}

void SymmetryWidget::moleculeChanged(unsigned int changes)
{
  /*
  if (changes & Molecule::UnitCell)
    revert();*/
}

void SymmetryWidget::operationsSelectionChanged(
  const QItemSelection& selected, const QItemSelection& deselected)
{

  if (!m_molecule)
    return;
  if (m_ui->tabWidget->currentWidget() != m_ui->subgroupsTab) {
    qDebug() << "subgroupsTab not selected";
    m_ui->subgroupsTree->selectionModel()->reset();
  } else
    qDebug() << "subgroupsTab selected";

  QModelIndexList selection =
    m_ui->operationsTable->selectionModel()->selectedRows();

  qDebug() << "operations changed";

  qDebug() << "selection " << selection.size();

  QVariantList reflectionVariantList;
  QVariantList properRotationVariantList;
  QVariantList improperRotationVariantList;

  m_molecule->setProperty("SymmetryOrigo", QVariant());
  m_molecule->setProperty("SymmetryRadius", QVariant());
  m_molecule->setProperty("SymmetryInversion", QVariant());
  m_molecule->setProperty("SymmetryProperRotationVariantList", QVariant());
  m_molecule->setProperty("SymmetryImproperRotationVariantList", QVariant());
  m_molecule->setProperty("SymmetryReflectionVariantList", QVariant());

  qDebug() << "cleared elements";

  if (m_sopsl > 0 && selection.size() > 0) {
    m_molecule->setProperty("SymmetryOrigo", m_cm);
    m_molecule->setProperty("SymmetryRadius", m_radius);
  }

  foreach (QModelIndex i, selection) {
    unsigned int row = i.row();
    if (!i.isValid() || row >= m_sopsl)
      continue;
    float x = m_sops[row].v[0], y = m_sops[row].v[1], z = m_sops[row].v[2];
    switch (m_sops[row].type) {
      case IDENTITY:
        break;
      case PROPER_ROTATION:
        properRotationVariantList.append(QVector3D(x, y, z));
        break;
      case IMPROPER_ROTATION:
        improperRotationVariantList.append(QVector3D(x, y, z));
        break;
      case REFLECTION:
        reflectionVariantList.append(QVector3D(x, y, z));
        break;
      case INVERSION:
        m_molecule->setProperty("SymmetryInversion", m_cm);
        break;
      default:
        break;
    }
  }
  if (properRotationVariantList.size() > 0)
    m_molecule->setProperty("SymmetryProperRotationVariantList",
                            properRotationVariantList);
  if (improperRotationVariantList.size() > 0)
    m_molecule->setProperty("SymmetryImproperRotationVariantList",
                            improperRotationVariantList);
  if (reflectionVariantList.size() > 0)
    m_molecule->setProperty("SymmetryReflectionVariantList",
                            reflectionVariantList);

  /* A little bit ugly, but it'll do for now */
  m_molecule->emitChanged(QtGui::Molecule::Atoms);
}

void SymmetryWidget::subgroupsSelectionChanged(const QItemSelection& selected,
                                               const QItemSelection& deselected)
{
  // QModelIndexList selection =
  // m_ui->subgroupsTree->selectionModel()->selectedIndexes();
  QModelIndex i =
    m_ui->subgroupsTree->selectionModel()->selectedIndexes().first();
  qDebug() << "subgroupsSelectionChanged";
  if (!i.isValid())
    return;
  qDebug() << "valid";
  int sgi = i.data(Qt::UserRole).value<int>();
  qDebug() << "index " << sgi;
  if (sgi < 0 || sgi >= m_sgl)
    return;
  const msym::msym_subgroup_t* sg = &m_sg[sgi];
  // m_ui->operationsTable->selectionModel()->clear();

  QItemSelectionModel* selectionModel = m_ui->operationsTable->selectionModel();
  // selectionModel->clear();

  QItemSelection selection;

  for (int j = 0; j < sg->order; j++) {
    int row = static_cast<int>(sg->sops[j] - m_sops);
    QModelIndex left = m_operationsTableModel->index(row, 0);
    QModelIndex right = m_operationsTableModel->index(
      row, m_operationsTableModel->columnCount(left) - 1);
    if (!left.isValid() || !right.isValid())
      qDebug() << "invalid index " << j;
    QItemSelection sel(left, right);

    selection.merge(sel, QItemSelectionModel::Select);
  }

  QModelIndexList tmp = selection.indexes();
  foreach (QModelIndex j, tmp) {
    qDebug() << "selecting " << j.row() << " " << j.column();
  }

  selectionModel->select(selection, QItemSelectionModel::ClearAndSelect);
}

void SymmetryWidget::equivalenceSelectionChanged(const QItemSelection& selected,
                                               const QItemSelection& deselected)
{
  QModelIndex i =
    m_ui->equivalenceTree->selectionModel()->selectedIndexes().first();
  qDebug() << "equivalenceSelectionChanged";
  if (!i.isValid())
    return;
  int atomInGroup = i.data(Qt::UserRole).value<int>();
  QModelIndex g = i.parent();
  if (!g.isValid())
    return;
  int group = g.data(Qt::UserRole).value<int>();

  qDebug() << "valid " << group << atomInGroup;
  if (group < 0 || group >= m_esl)
    return;

  // TODO: okay, now we have to find the atoms and select them
  if (!m_molecule)
    return;

  const msym_equivalence_set_t *smes = &m_es[group];
  const msym_element_t *a = smes->elements[atomInGroup];
  if (a == NULL)
    return;

  unsigned int length = m_molecule->atomCount();
  for (Index i = 0; i < length; ++i) {
    qDebug() << "checking atom" << i << " for " << a->n;
    m_molecule->setAtomSelected(i, false);
    if (m_molecule->atomicNumbers()[i] != a->n)
      continue;

    Vector3 ipos = m_molecule->atomPositions3d()[i];
    qDebug() << a->v[0] << ipos[0] - m_cm[0];
    qDebug() << a->v[1] << ipos[1] - m_cm[1];
    qDebug() << a->v[2] << ipos[2] - m_cm[2];
    if ( fabs(a->v[0] - (ipos[0] - m_cm[0])) < 0.05
        && fabs(a->v[0] - (ipos[0] - m_cm[0])) < 0.05
        && fabs(a->v[0] - (ipos[0] - m_cm[0])) < 0.05
       ) {
          m_molecule->setAtomSelected(i, true);
          qDebug() << " got one!";
        }
  }
  m_molecule->emitChanged(QtGui::Molecule::Atoms);
}

void SymmetryWidget::setRadius(double radius)
{
  m_radius = radius;
}

void SymmetryWidget::setCenterOfMass(double cm[3])
{
  m_cm = QVector3D(cm[0], cm[1], cm[2]);
}

void SymmetryWidget::setPointGroupSymbol(QString pg)
{
  m_ui->pointGroupLabel->setText(pg);
}

void SymmetryWidget::setSymmetryOperations(
  int sopsl, const msym::msym_symmetry_operation_t* sops)
{
  m_sops = sops;
  m_sopsl = sopsl;
  m_operationsTableModel->setOperations(sopsl, sops);
  m_molecule->setProperty("SymmetryOrigo", QVariant());
  m_molecule->setProperty("SymmetryRadius", QVariant());
  m_molecule->setProperty("SymmetryInversion", QVariant());
  m_molecule->setProperty("SymmetryProperRotationVariantList", QVariant());
  m_molecule->setProperty("SymmetryImproperRotationVariantList", QVariant());
  m_molecule->setProperty("SymmetryReflectionVariantList", QVariant());
  /* need another change event */
  m_molecule->emitChanged(QtGui::Molecule::Atoms);
}

void SymmetryWidget::setEquivalenceSets(int esl,
  const msym::msym_equivalence_set_t* es)
{
  m_esl = esl;
  m_es = es;
  m_equivalenceTreeModel->clear();
  for (int i = 0; i < esl; i++) {
    QStandardItem* const parent = new QStandardItem;
    QString label = tr("Group %1").arg(QString::number(i + 1));
    parent->setText(label);
    parent->setData(i, Qt::UserRole);
    m_equivalenceTreeModel->appendRow(parent);
    const msym_equivalence_set_t *smes = &es[i];
    for (int j = 0; j < smes->length; j++) {
      QStandardItem* const child = new QStandardItem;
      label = tr("%1 %2").arg(smes->elements[j]->name)
                 .arg(QString::number(j + 1));
      child->setText(label);
      child->setData(j, Qt::UserRole);
      parent->appendRow(child);
    }
  }
}

void SymmetryWidget::setSubgroups(int sgl, const msym::msym_subgroup_t* sg)
{
  m_sg = sg;
  m_sgl = sgl;
  m_subgroupsTreeModel->clear();
  for (int i = 0; i < sgl; i++) {
    if (sg[i].order <= 2)
      continue;
    QStandardItem* const parent = new QStandardItem;
    parent->setText(pointGroupSymbol(sg[i].name));
    parent->setData(i, Qt::UserRole);
    m_subgroupsTreeModel->appendRow(parent);
    for (int j = 0; j < 2; j++) {
      if (sg[i].generators[j] == NULL)
        continue;
      qDebug() << "child " << sg[i].generators[j] - m_sg << " "
               << sg[i].generators[j] << " " << m_sg;
      QStandardItem* const child = new QStandardItem;
      child->setText(pointGroupSymbol(sg[i].generators[j]->name));

      child->setData(static_cast<int>(sg[i].generators[j] - m_sg),
                     Qt::UserRole);
      parent->appendRow(child);
    }
  }
}

msym_thresholds_t* SymmetryWidget::getThresholds() const
{
  msym_thresholds_t* thresholds = NULL;
  switch (m_ui->toleranceCombo->currentIndex()) {
    case 2: // loose
      thresholds = &loose_thresholds;
      break;
    case 1: // normal
      thresholds = &medium_thresholds;
      break;
    case 0: // tight
    default:
      thresholds = &tight_thresholds;
  }
  return thresholds;
}

} // namespace QtPlugins
} // namespace Avogadro
<|endoftext|>