<commit_before>/*
 * libMaia - maiaObject.cpp
 * Copyright (c) 2003 Frerich Raabe <raabe@kde.org> and
 *                    Ian Reinhart Geiser <geiseri@kde.org>
 * Copyright (c) 2007 Sebastian Wiedenroth <wiedi@frubar.net>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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 AUTHOR ``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 AUTHOR 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 "maiaObject.h"

MaiaObject::MaiaObject(QObject* parent) : QObject(parent){
	QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
}

QDomElement MaiaObject::toXml(QVariant arg) {

	//dummy document
	QDomDocument doc;
	//value element, we need this in each case
	QDomElement tagValue = doc.createElement("value");

	switch(arg.type()) {
	case QVariant::String: {

		QDomElement tagString = doc.createElement("string");
		QDomText textString = doc.createTextNode(arg.toString());

		tagValue.appendChild(tagString);
		tagString.appendChild(textString);

		return tagValue;

	} case QVariant::Int: {

		QDomElement tagInt = doc.createElement("int");
		QDomText textInt = doc.createTextNode(QString::number(arg.toInt()));

		tagValue.appendChild(tagInt);
		tagInt.appendChild(textInt);

		return tagValue;

	} case QVariant::Double: {

		QDomElement tagDouble = doc.createElement("double");
		QDomText textDouble = doc.createTextNode(QString::number(arg.toDouble()));

		tagValue.appendChild(tagDouble);
		tagDouble.appendChild(textDouble);

		return tagValue;

	} case QVariant::Bool: {

		QString textValue = arg.toBool() ? "true" : "false";

		QDomElement tag = doc.createElement("boolean");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::ByteArray: {

		QString textValue = arg.toByteArray().toBase64();

		QDomElement tag = doc.createElement("base64");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::DateTime: {

		QString textValue = arg.toDateTime().toString("yyyyMMddThh:mm:ss");

		QDomElement tag = doc.createElement("datetime.iso8601");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::List: {

		QDomElement tagArray = doc.createElement("array");
		QDomElement tagData = doc.createElement("data");
		tagArray.appendChild(tagData);
		tagValue.appendChild(tagArray);

		const QList<QVariant> args = arg.toList();
		for(int i = 0; i < args.size(); ++i) {
			tagData.appendChild(toXml(args.at(i)));
		}

		return tagValue;

	} case QVariant::Map: {

		QDomElement tagStruct = doc.createElement("struct");
		QDomElement member;
		QDomElement name;

		tagValue.appendChild(tagStruct);

		QMap<QString, QVariant> map = arg.toMap();
		QMapIterator<QString, QVariant> i(map);
		while(i.hasNext()) {
			i.next();

			member = doc.createElement("member");
			name = doc.createElement("name");

			// (key) -> name -> member -> struct
			tagStruct.appendChild(member);
			member.appendChild(name);
			name.appendChild(doc.createTextNode(i.key()));

			// add variables by recursion
			member.appendChild(toXml(i.value()));
		}

		return tagValue;

	} default:
		qDebug() << "Failed to marshal unknown variant type: " << arg.type() << endl;
	}
	return QDomElement(); //QString::null;
}

QVariant MaiaObject::fromXml(const QDomElement &elem) {
	if(elem.tagName().toLower() != "value") {
		return QVariant();
	}

	const QDomElement typeElement = elem.firstChild().toElement();
	const QString typeName = typeElement.tagName().toLower();

	if(typeName == "string")
		return QVariant(typeElement.text());
	else if(typeName == "i4" || typeName == "int")
		return QVariant(typeElement.text().toInt());
	else if(typeName == "double")
		return QVariant(typeElement.text().toDouble());
	else if (typeName == "boolean") {
		if(typeElement.text().toLower() == "true" || typeElement.text() == "1")
			return QVariant(true);
		else
			return QVariant(false);
	} else if(typeName == "base64")
		return QVariant(QByteArray::fromBase64( typeElement.text().toLatin1()));
	else if(typeName == "datetime" || typeName == "datetime.iso8601")
		return QVariant(QDateTime::fromString(typeElement.text(), "yyyyMMddThh:mm:ss"));
	else if ( typeName == "array" ) {
		QList<QVariant> values;
		QDomNode valueNode = typeElement.firstChild().firstChild();
		while(!valueNode.isNull()) {
			values << fromXml(valueNode.toElement());
			valueNode = valueNode.nextSibling();
		}
		return QVariant(values);
	}
	else if ( typeName == "struct" ) {
		QMap<QString, QVariant> map;
		QDomNode memberNode = typeElement.firstChild();
		while(!memberNode.isNull())	{
			const QString key = memberNode.toElement().elementsByTagName("name").item(0).toElement().text();
			const QVariant data = fromXml(memberNode.toElement().elementsByTagName("value").item(0).toElement());
			map[key] = data;
			memberNode = memberNode.nextSibling();
		}
		return QVariant(map);
	} else {
		qDebug() << "Cannot demarshal unknown type " << typeElement.tagName().toLower();
	}
	return QVariant();
}

QString MaiaObject::prepareCall(QString method, QList<QVariant> args) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodCall = doc.createElement("methodCall");
	QDomElement methodName = doc.createElement("methodName");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodCall);
	methodCall.appendChild(methodName);
	methodName.appendChild(doc.createTextNode(method));

	methodCall.appendChild(params);

	for(int i = 0; i < args.size(); ++i) {
		param = doc.createElement("param");
		param.appendChild(toXml(args.at(i)));
		params.appendChild(param);
	}

	return doc.toString();
}

QString MaiaObject::prepareResponse(QVariant arg) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodResponse = doc.createElement("methodResponse");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodResponse);

	methodResponse.appendChild(params);

	if(!arg.isNull()) {
		param = doc.createElement("param");
		param.appendChild(toXml(arg));
		params.appendChild(param);
	}
	return doc.toString();
}

void MaiaObject::parseResponse(QString response, QNetworkReply* reply) {
	QDomDocument doc;
	QVariant arg;
	QString errorMsg;
	int errorLine;
	int errorColumn;
	if(!doc.setContent(response, &errorMsg, &errorLine, &errorColumn)) {
		emit fault(-32700, QString("parse error: response not well formed at line %1: %2").arg(errorLine).arg(errorMsg), reply);
		delete this;
		return;
	}
	if(doc.documentElement().firstChild().toElement().tagName().toLower() == "params") {
		QDomNode paramNode = doc.documentElement().firstChild().firstChild();
		if(!paramNode.isNull()) {
			arg = fromXml( paramNode.firstChild().toElement() );
		}
		emit aresponse(arg, reply);
	} else if(doc.documentElement().firstChild().toElement().tagName().toLower() == "fault") {
		const QVariant errorVariant = fromXml(doc.documentElement().firstChild().firstChild().toElement());
		emit fault(errorVariant.toMap() [ "faultCode" ].toInt(),
		           errorVariant.toMap() [ "faultString" ].toString(),
		           reply);
	} else {
		emit fault(-32600,
		           tr("parse error: invalid xml-rpc. not conforming to spec."),
		           reply);
	}
	delete this;
	return;
}

<commit_msg>compatibility with ws-xmlrpc (untested) - thanks to Martin Scheffler<commit_after>/*
 * libMaia - maiaObject.cpp
 * Copyright (c) 2003 Frerich Raabe <raabe@kde.org> and
 *                    Ian Reinhart Geiser <geiseri@kde.org>
 * Copyright (c) 2007 Sebastian Wiedenroth <wiedi@frubar.net>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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 AUTHOR ``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 AUTHOR 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 "maiaObject.h"

MaiaObject::MaiaObject(QObject* parent) : QObject(parent){
	QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
}

QDomElement MaiaObject::toXml(QVariant arg) {

	//dummy document
	QDomDocument doc;
	//value element, we need this in each case
	QDomElement tagValue = doc.createElement("value");

	switch(arg.type()) {
	case QVariant::String: {

		QDomElement tagString = doc.createElement("string");
		QDomText textString = doc.createTextNode(arg.toString());

		tagValue.appendChild(tagString);
		tagString.appendChild(textString);

		return tagValue;

	} case QVariant::Int: {

		QDomElement tagInt = doc.createElement("int");
		QDomText textInt = doc.createTextNode(QString::number(arg.toInt()));

		tagValue.appendChild(tagInt);
		tagInt.appendChild(textInt);

		return tagValue;

	} case QVariant::Double: {

		QDomElement tagDouble = doc.createElement("double");
		QDomText textDouble = doc.createTextNode(QString::number(arg.toDouble()));

		tagValue.appendChild(tagDouble);
		tagDouble.appendChild(textDouble);

		return tagValue;

	} case QVariant::Bool: {

		QString textValue = arg.toBool() ? "true" : "false";

		QDomElement tag = doc.createElement("boolean");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::ByteArray: {

		QString textValue = arg.toByteArray().toBase64();

		QDomElement tag = doc.createElement("base64");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::DateTime: {

		QString textValue = arg.toDateTime().toString("yyyyMMddThh:mm:ss");

		QDomElement tag = doc.createElement("datetime.iso8601");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::List: {

		QDomElement tagArray = doc.createElement("array");
		QDomElement tagData = doc.createElement("data");
		tagArray.appendChild(tagData);
		tagValue.appendChild(tagArray);

		const QList<QVariant> args = arg.toList();
		for(int i = 0; i < args.size(); ++i) {
			tagData.appendChild(toXml(args.at(i)));
		}

		return tagValue;

	} case QVariant::Map: {

		QDomElement tagStruct = doc.createElement("struct");
		QDomElement member;
		QDomElement name;

		tagValue.appendChild(tagStruct);

		QMap<QString, QVariant> map = arg.toMap();
		QMapIterator<QString, QVariant> i(map);
		while(i.hasNext()) {
			i.next();

			member = doc.createElement("member");
			name = doc.createElement("name");

			// (key) -> name -> member -> struct
			tagStruct.appendChild(member);
			member.appendChild(name);
			name.appendChild(doc.createTextNode(i.key()));

			// add variables by recursion
			member.appendChild(toXml(i.value()));
		}

		return tagValue;

	} default:
		qDebug() << "Failed to marshal unknown variant type: " << arg.type() << endl;
	}
	return QDomElement(); //QString::null;
}

QVariant MaiaObject::fromXml(const QDomElement &elem) {
	if(elem.tagName().toLower() != "value") {
		return QVariant();
	}

	// If no type is indicated, the type is string.
	if(!elem.firstChild().isElement()) {
		return QVariant(elem.text());
	}

	const QDomElement typeElement = elem.firstChild().toElement();
	const QString typeName = typeElement.tagName().toLower();

	if(typeName == "string")
		return QVariant(typeElement.text());
	else if(typeName == "i4" || typeName == "int")
		return QVariant(typeElement.text().toInt());
	else if(typeName == "double")
		return QVariant(typeElement.text().toDouble());
	else if (typeName == "boolean") {
		if(typeElement.text().toLower() == "true" || typeElement.text() == "1")
			return QVariant(true);
		else
			return QVariant(false);
	} else if(typeName == "base64")
		return QVariant(QByteArray::fromBase64( typeElement.text().toLatin1()));
	else if(typeName == "datetime" || typeName == "datetime.iso8601")
		return QVariant(QDateTime::fromString(typeElement.text(), "yyyyMMddThh:mm:ss"));
	else if ( typeName == "array" ) {
		QList<QVariant> values;
		QDomNode valueNode = typeElement.firstChild().firstChild();
		while(!valueNode.isNull()) {
			values << fromXml(valueNode.toElement());
			valueNode = valueNode.nextSibling();
		}
		return QVariant(values);
	}
	else if ( typeName == "struct" ) {
		QMap<QString, QVariant> map;
		QDomNode memberNode = typeElement.firstChild();
		while(!memberNode.isNull())	{
			const QString key = memberNode.toElement().elementsByTagName("name").item(0).toElement().text();
			const QVariant data = fromXml(memberNode.toElement().elementsByTagName("value").item(0).toElement());
			map[key] = data;
			memberNode = memberNode.nextSibling();
		}
		return QVariant(map);
	} else {
		qDebug() << "Cannot demarshal unknown type " << typeElement.tagName().toLower();
	}
	return QVariant();
}

QString MaiaObject::prepareCall(QString method, QList<QVariant> args) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodCall = doc.createElement("methodCall");
	QDomElement methodName = doc.createElement("methodName");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodCall);
	methodCall.appendChild(methodName);
	methodName.appendChild(doc.createTextNode(method));

	methodCall.appendChild(params);

	for(int i = 0; i < args.size(); ++i) {
		param = doc.createElement("param");
		param.appendChild(toXml(args.at(i)));
		params.appendChild(param);
	}

	return doc.toString();
}

QString MaiaObject::prepareResponse(QVariant arg) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodResponse = doc.createElement("methodResponse");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodResponse);

	methodResponse.appendChild(params);

	if(!arg.isNull()) {
		param = doc.createElement("param");
		param.appendChild(toXml(arg));
		params.appendChild(param);
	}
	return doc.toString();
}

void MaiaObject::parseResponse(QString response, QNetworkReply* reply) {
	QDomDocument doc;
	QVariant arg;
	QString errorMsg;
	int errorLine;
	int errorColumn;
	if(!doc.setContent(response, &errorMsg, &errorLine, &errorColumn)) {
		emit fault(-32700, QString("parse error: response not well formed at line %1: %2").arg(errorLine).arg(errorMsg), reply);
		delete this;
		return;
	}
	if(doc.documentElement().firstChild().toElement().tagName().toLower() == "params") {
		QDomNode paramNode = doc.documentElement().firstChild().firstChild();
		if(!paramNode.isNull()) {
			arg = fromXml( paramNode.firstChild().toElement() );
		}
		emit aresponse(arg, reply);
	} else if(doc.documentElement().firstChild().toElement().tagName().toLower() == "fault") {
		const QVariant errorVariant = fromXml(doc.documentElement().firstChild().firstChild().toElement());
		emit fault(errorVariant.toMap() [ "faultCode" ].toInt(),
		           errorVariant.toMap() [ "faultString" ].toString(),
		           reply);
	} else {
		emit fault(-32600,
		           tr("parse error: invalid xml-rpc. not conforming to spec."),
		           reply);
	}
	delete this;
	return;
}

<|endoftext|>


<commit_before>#include "thrusttest/testframework.h"
#include "thrusttest/exceptions.h"

#include <cuda_runtime.h>
#include <iostream>
#include <cstdlib>
#include <algorithm>

void UnitTestDriver::register_test(UnitTest *test)
{
    UnitTestDriver::s_driver()._test_list.push_back(test);
}

UnitTest::UnitTest(const char * _name) : name(_name)
{
  UnitTestDriver::s_driver().register_test(this);
}

bool UnitTestDriver::run_tests(const std::vector<UnitTest *> &tests_to_run, const bool verbose)
{
    bool any_failed = false;

    std::cout << "Running " << tests_to_run.size() << " unit tests." << std::endl;

    std::vector< std::pair<UnitTest *,thrusttest::UnitTestFailure> >      test_failures;
    std::vector< std::pair<UnitTest *,thrusttest::UnitTestKnownFailure> > test_known_failures;
    std::vector< std::pair<UnitTest *,thrusttest::UnitTestError>   >      test_errors;
    std::vector< UnitTest * >                                             test_exceptions;

    cudaError_t error = cudaGetLastError();
    if(error){
        std::cerr << "[ERROR] CUDA Error detected before running tests: [";
        std::cerr << std::string(cudaGetErrorString(error));
        std::cerr << "]" << std::endl;
        exit(EXIT_FAILURE);
    }

    for(size_t i = 0; i < tests_to_run.size(); i++){
        UnitTest * test = tests_to_run[i];

        if (verbose)
            std::cout << "Running " << test->name << "\r" << std::flush;

        try {
            test->run();
            if (verbose)
                std::cout << "[PASS]             ";
            else
                std::cout << ".";
        }
        catch (thrusttest::UnitTestFailure& f){
            any_failed = true;
            if (verbose)
                std::cout << "[FAILURE]          ";
            else
                std::cout << "F";
            test_failures.push_back(std::make_pair(test,f));
        }
        catch (thrusttest::UnitTestKnownFailure& f){
            if (verbose)
                std::cout << "[KNOWN FAILURE]    ";
            else
                std::cout << "K";
            test_known_failures.push_back(std::make_pair(test,f));
        }
        catch (thrusttest::UnitTestError& e){
            any_failed = true;
            if (verbose)
                std::cout << "[ERROR]            ";
            else
                std::cout << "E";
            test_errors.push_back(std::make_pair(test,e));
        }
        catch (...){
            any_failed = true;
            if (verbose)
                std::cout << "[UNKNOWN EXCEPTION] " << std::endl;
            else
                std::cout << "U";
            test_exceptions.push_back(test);
        }

        if (verbose)
            std::cout << " " << test->name << std::endl;

        error = cudaGetLastError();
        if(error){
            std::cerr << "\t[ERROR] CUDA Error detected after running " << test->name << ": [";
            std::cerr << std::string(cudaGetErrorString(error));
            std::cerr << "]" << std::endl;
            exit(EXIT_FAILURE);
        }

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

    std::string hline = "================================================================";

    for(size_t i = 0; i < test_failures.size(); i++){
        std::cout << hline << std::endl;
        std::cout << "FAILURE: " << test_failures[i].first->name << std::endl;
        std::cout << test_failures[i].second << std::endl;
    }
    for(size_t i = 0; i < test_known_failures.size(); i++){
        std::cout << hline << std::endl;
        std::cout << "KNOWN FAILURE: " << test_known_failures[i].first->name << std::endl;
        std::cout << test_known_failures[i].second << std::endl;
    }
    for(size_t i = 0; i < test_errors.size(); i++){
        std::cout << hline << std::endl;
        std::cout << "ERROR: " << test_errors[i].first->name << std::endl;
        std::cout << test_errors[i].second << std::endl;
    }
    for(size_t i = 0; i < test_exceptions.size(); i++){
        std::cout << hline << std::endl;
        std::cout << "UNKNOWN EXCEPTION: " << test_exceptions[i]->name << std::endl;
    }

    std::cout << hline << std::endl;

    std::cout << "Totals: ";
    std::cout << test_failures.size() << " failures, ";
    std::cout << test_known_failures.size() << " known failures, ";
    std::cout << test_errors.size() << " errors and ";
    std::cout << test_exceptions.size() << " unknown exceptions." << std::endl;

    return any_failed;
}

// for sorting UnitTests by name
struct UnitTest_name_cmp
{
    bool operator()(const UnitTest * a, const UnitTest * b) const {
        return a->name < b->name;
    }

};

bool UnitTestDriver::run_all_tests(const bool verbose)
{
  std::vector<UnitTest *> tests_to_run(_test_list);

  // sort tests by name for deterministic results
  std::sort(tests_to_run.begin(), tests_to_run.end(), UnitTest_name_cmp());

  return run_tests(tests_to_run, verbose);
}

bool
UnitTestDriver::run_tests(const std::vector<std::string> &tests, const bool verbose)
{
  int i, j;
  std::vector<UnitTest *> tests_to_run;

  for (j=0; j < tests.size(); j++) {

    bool found = false;
    for (i = 0; !found && i < _test_list.size(); i++)
      if (tests[j] == _test_list[i]->name) {

        tests_to_run.push_back(_test_list[i]);
        found = true;
      }

    if (!found) {
      printf("[WARNING] UnitTestDriver::run_tests - test %s not found\n", tests[j].c_str());
    }
  }

  return run_tests(tests_to_run, verbose);
}

UnitTestDriver &
UnitTestDriver::s_driver()
{
  static UnitTestDriver s_instance;
  return s_instance;
}

<commit_msg>Correctly return the status of unit tests in testframework.cpp.<commit_after>#include "thrusttest/testframework.h"
#include "thrusttest/exceptions.h"

#include <cuda_runtime.h>
#include <iostream>
#include <cstdlib>
#include <algorithm>

void UnitTestDriver::register_test(UnitTest *test)
{
    UnitTestDriver::s_driver()._test_list.push_back(test);
}

UnitTest::UnitTest(const char * _name) : name(_name)
{
  UnitTestDriver::s_driver().register_test(this);
}

bool UnitTestDriver::run_tests(const std::vector<UnitTest *> &tests_to_run, const bool verbose)
{
    bool all_passed = true;

    std::cout << "Running " << tests_to_run.size() << " unit tests." << std::endl;

    std::vector< std::pair<UnitTest *,thrusttest::UnitTestFailure> >      test_failures;
    std::vector< std::pair<UnitTest *,thrusttest::UnitTestKnownFailure> > test_known_failures;
    std::vector< std::pair<UnitTest *,thrusttest::UnitTestError>   >      test_errors;
    std::vector< UnitTest * >                                             test_exceptions;

    cudaError_t error = cudaGetLastError();
    if(error){
        std::cerr << "[ERROR] CUDA Error detected before running tests: [";
        std::cerr << std::string(cudaGetErrorString(error));
        std::cerr << "]" << std::endl;
        exit(EXIT_FAILURE);
    }

    for(size_t i = 0; i < tests_to_run.size(); i++){
        UnitTest * test = tests_to_run[i];

        if (verbose)
            std::cout << "Running " << test->name << "\r" << std::flush;

        try {
            test->run();
            if (verbose)
                std::cout << "[PASS]             ";
            else
                std::cout << ".";
        }
        catch (thrusttest::UnitTestFailure& f){
            all_passed = false;
            if (verbose)
                std::cout << "[FAILURE]          ";
            else
                std::cout << "F";
            test_failures.push_back(std::make_pair(test,f));
        }
        catch (thrusttest::UnitTestKnownFailure& f){
            if (verbose)
                std::cout << "[KNOWN FAILURE]    ";
            else
                std::cout << "K";
            test_known_failures.push_back(std::make_pair(test,f));
        }
        catch (thrusttest::UnitTestError& e){
            all_passed = false;
            if (verbose)
                std::cout << "[ERROR]            ";
            else
                std::cout << "E";
            test_errors.push_back(std::make_pair(test,e));
        }
        catch (...){
            all_passed = false;
            if (verbose)
                std::cout << "[UNKNOWN EXCEPTION] " << std::endl;
            else
                std::cout << "U";
            test_exceptions.push_back(test);
        }

        if (verbose)
            std::cout << " " << test->name << std::endl;

        error = cudaGetLastError();
        if(error){
            std::cerr << "\t[ERROR] CUDA Error detected after running " << test->name << ": [";
            std::cerr << std::string(cudaGetErrorString(error));
            std::cerr << "]" << std::endl;
            exit(EXIT_FAILURE);
        }

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

    std::string hline = "================================================================";

    for(size_t i = 0; i < test_failures.size(); i++){
        std::cout << hline << std::endl;
        std::cout << "FAILURE: " << test_failures[i].first->name << std::endl;
        std::cout << test_failures[i].second << std::endl;
    }
    for(size_t i = 0; i < test_known_failures.size(); i++){
        std::cout << hline << std::endl;
        std::cout << "KNOWN FAILURE: " << test_known_failures[i].first->name << std::endl;
        std::cout << test_known_failures[i].second << std::endl;
    }
    for(size_t i = 0; i < test_errors.size(); i++){
        std::cout << hline << std::endl;
        std::cout << "ERROR: " << test_errors[i].first->name << std::endl;
        std::cout << test_errors[i].second << std::endl;
    }
    for(size_t i = 0; i < test_exceptions.size(); i++){
        std::cout << hline << std::endl;
        std::cout << "UNKNOWN EXCEPTION: " << test_exceptions[i]->name << std::endl;
    }

    std::cout << hline << std::endl;

    std::cout << "Totals: ";
    std::cout << test_failures.size() << " failures, ";
    std::cout << test_known_failures.size() << " known failures, ";
    std::cout << test_errors.size() << " errors and ";
    std::cout << test_exceptions.size() << " unknown exceptions." << std::endl;

    return all_passed;
}

// for sorting UnitTests by name
struct UnitTest_name_cmp
{
    bool operator()(const UnitTest * a, const UnitTest * b) const {
        return a->name < b->name;
    }

};

bool UnitTestDriver::run_all_tests(const bool verbose)
{
  std::vector<UnitTest *> tests_to_run(_test_list);

  // sort tests by name for deterministic results
  std::sort(tests_to_run.begin(), tests_to_run.end(), UnitTest_name_cmp());

  return run_tests(tests_to_run, verbose);
}

bool
UnitTestDriver::run_tests(const std::vector<std::string> &tests, const bool verbose)
{
  int i, j;
  std::vector<UnitTest *> tests_to_run;

  for (j=0; j < tests.size(); j++) {

    bool found = false;
    for (i = 0; !found && i < _test_list.size(); i++)
      if (tests[j] == _test_list[i]->name) {

        tests_to_run.push_back(_test_list[i]);
        found = true;
      }

    if (!found) {
      printf("[WARNING] UnitTestDriver::run_tests - test %s not found\n", tests[j].c_str());
    }
  }

  return run_tests(tests_to_run, verbose);
}

UnitTestDriver &
UnitTestDriver::s_driver()
{
  static UnitTestDriver s_instance;
  return s_instance;
}

<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "logic/blinkcore.h"
#include <QtAlgorithms>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QMessageBox>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    core(new BlinkCore(this)),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    auto availableTypes = core->availableSelectorTypes();
    ui->anime_selectorFormat->addItems(availableTypes);
    ui->manga_selectorFormat->addItems(availableTypes);
    core->setAnimeOutFormat(availableTypes.at(0));
    core->setMangaOutFormat(availableTypes.at(0));
    connect(core, &BlinkCore::animelistTotal,
            [this](int i) { ui->anime_progress->setMaximum(qMax(i, 1)); });
    connect(core, &BlinkCore::animelistProcessed,
            ui->anime_progress, &QProgressBar::setValue);
    connect(core, &BlinkCore::mangalistTotal,
            [this](int i) { ui->manga_progress->setMaximum(qMax(i, 1)); });
    connect(core, &BlinkCore::mangalistProcessed,
            ui->manga_progress, &QProgressBar::setValue);

    connect(core, &BlinkCore::finished,
            this, &MainWindow::onBlinkFinished);
    connect(core, &BlinkCore::error,
            this, &MainWindow::onBlinkError);

    connect(ui->usernameEdit, &QLineEdit::textChanged,
            core, &BlinkCore::setUsername);
    connect(ui->usernameEdit, &QLineEdit::textChanged, this, &MainWindow::purgeProgress);
    connect(ui->anime_group, &QGroupBox::toggled, core, &BlinkCore::processAnimelist);
    connect(ui->manga_group, &QGroupBox::toggled, core, &BlinkCore::processMangalist);
    connect(ui->anime_path, &QLineEdit::textChanged, core, &BlinkCore::setAnimeOutPath);
    connect(ui->manga_path, &QLineEdit::textChanged, core, &BlinkCore::setMangaOutPath);
    connect(ui->anime_selectorFormat, &QComboBox::currentTextChanged, core, &BlinkCore::setAnimeOutFormat);
    connect(ui->manga_selectorFormat, &QComboBox::currentTextChanged, core, &BlinkCore::setMangaOutFormat);
    connect(ui->anime_browseButton, &QPushButton::clicked, [this]{writePath(ui->anime_path);});
    connect(ui->manga_browseButton, &QPushButton::clicked, [this]{writePath(ui->manga_path);});
    connect(ui->blinkButton, &QPushButton::clicked, this, &MainWindow::onBlinkButtonPressed);
}

MainWindow::~MainWindow()
{
    delete core;
    delete ui;
}

void MainWindow::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void MainWindow::writePath(QLineEdit *le)
{
    QString path
            = QFileDialog::getSaveFileName(this, tr("Save covers file as"),
                                           QString(),
                                           tr("Cascading style sheet files (*.css)"
                                              ";;All files (*.*)"));
    if (!path.isEmpty()) le->setText(path);
}

void MainWindow::onBlinkButtonPressed()
{
    if (ui->anime_path->text() == ui->manga_path->text()) {
        QMessageBox::warning(this, tr("Warning"),
                             tr("You cannot save both cover files into one"));
        return;
    }
    if (ui->anime_group->isChecked() && ui->anime_path->text().isEmpty()) {
        QMessageBox::warning(this, tr("Warning"),
                             tr("You must specify a path to save anime list covers"));
        return;
    }
    if (ui->manga_group->isChecked() && ui->manga_path->text().isEmpty()) {
        QMessageBox::warning(this, tr("Warning"),
                             tr("You must specify a path to save manga list covers"));
        return;
    }
    enableControls(false);
    core->startProcessing();
}

void MainWindow::enableControls(bool enable)
{
    ui->anime_group->setEnabled(enable);
    ui->manga_group->setEnabled(enable);
    ui->usernameEdit->setEnabled(enable);
    ui->blinkButton->setEnabled(enable);
}

void MainWindow::purgeProgress()
{
    ui->anime_progress->setValue(0);
    ui->anime_progress->setMaximum(1);
    ui->manga_progress->setValue(0);
    ui->manga_progress->setMaximum(1);
}

void MainWindow::onBlinkFinished()
{
    // this is a workaround because MAL can report incorrect quantities
    // so I just quickly change the values to indicate correct ones
    ui->anime_progress->setMaximum(qMax(1,ui->anime_progress->value()));
    ui->manga_progress->setMaximum(qMax(1,ui->manga_progress->value()));
    QMessageBox::information(this, tr("Info"),
                             tr("Saving completed"));
    enableControls(true);
}

void MainWindow::onBlinkError(QString msg)
{
    enableControls(true);
    QMessageBox::critical(this, tr("Error"),
                          tr("An error occured during operation:/n%1").arg(msg));
}
<commit_msg>Fixed issue when path is the same but one of groups is disabled<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "logic/blinkcore.h"
#include <QtAlgorithms>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QMessageBox>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    core(new BlinkCore(this)),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    auto availableTypes = core->availableSelectorTypes();
    ui->anime_selectorFormat->addItems(availableTypes);
    ui->manga_selectorFormat->addItems(availableTypes);
    core->setAnimeOutFormat(availableTypes.at(0));
    core->setMangaOutFormat(availableTypes.at(0));
    connect(core, &BlinkCore::animelistTotal,
            [this](int i) { ui->anime_progress->setMaximum(qMax(i, 1)); });
    connect(core, &BlinkCore::animelistProcessed,
            ui->anime_progress, &QProgressBar::setValue);
    connect(core, &BlinkCore::mangalistTotal,
            [this](int i) { ui->manga_progress->setMaximum(qMax(i, 1)); });
    connect(core, &BlinkCore::mangalistProcessed,
            ui->manga_progress, &QProgressBar::setValue);

    connect(core, &BlinkCore::finished,
            this, &MainWindow::onBlinkFinished);
    connect(core, &BlinkCore::error,
            this, &MainWindow::onBlinkError);

    connect(ui->usernameEdit, &QLineEdit::textChanged,
            core, &BlinkCore::setUsername);
    connect(ui->usernameEdit, &QLineEdit::textChanged, this, &MainWindow::purgeProgress);
    connect(ui->anime_group, &QGroupBox::toggled, core, &BlinkCore::processAnimelist);
    connect(ui->manga_group, &QGroupBox::toggled, core, &BlinkCore::processMangalist);
    connect(ui->anime_path, &QLineEdit::textChanged, core, &BlinkCore::setAnimeOutPath);
    connect(ui->manga_path, &QLineEdit::textChanged, core, &BlinkCore::setMangaOutPath);
    connect(ui->anime_selectorFormat, &QComboBox::currentTextChanged, core, &BlinkCore::setAnimeOutFormat);
    connect(ui->manga_selectorFormat, &QComboBox::currentTextChanged, core, &BlinkCore::setMangaOutFormat);
    connect(ui->anime_browseButton, &QPushButton::clicked, [this]{writePath(ui->anime_path);});
    connect(ui->manga_browseButton, &QPushButton::clicked, [this]{writePath(ui->manga_path);});
    connect(ui->blinkButton, &QPushButton::clicked, this, &MainWindow::onBlinkButtonPressed);
}

MainWindow::~MainWindow()
{
    delete core;
    delete ui;
}

void MainWindow::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void MainWindow::writePath(QLineEdit *le)
{
    QString path
            = QFileDialog::getSaveFileName(this, tr("Save covers file as"),
                                           QString(),
                                           tr("Cascading style sheet files (*.css)"
                                              ";;All files (*.*)"));
    if (!path.isEmpty()) le->setText(path);
}

void MainWindow::onBlinkButtonPressed()
{
    if (ui->anime_group->isChecked() && ui->manga_group->isChecked()
            && ui->anime_path->text() == ui->manga_path->text()) {
        QMessageBox::warning(this, tr("Warning"),
                             tr("You cannot save both cover files into one"));
        return;
    }
    if (ui->anime_group->isChecked() && ui->anime_path->text().isEmpty()) {
        QMessageBox::warning(this, tr("Warning"),
                             tr("You must specify a path to save anime list covers"));
        return;
    }
    if (ui->manga_group->isChecked() && ui->manga_path->text().isEmpty()) {
        QMessageBox::warning(this, tr("Warning"),
                             tr("You must specify a path to save manga list covers"));
        return;
    }
    enableControls(false);
    core->startProcessing();
}

void MainWindow::enableControls(bool enable)
{
    ui->anime_group->setEnabled(enable);
    ui->manga_group->setEnabled(enable);
    ui->usernameEdit->setEnabled(enable);
    ui->blinkButton->setEnabled(enable);
}

void MainWindow::purgeProgress()
{
    ui->anime_progress->setValue(0);
    ui->anime_progress->setMaximum(1);
    ui->manga_progress->setValue(0);
    ui->manga_progress->setMaximum(1);
}

void MainWindow::onBlinkFinished()
{
    // this is a workaround because MAL can report incorrect quantities
    // so I just quickly change the values to indicate correct ones
    ui->anime_progress->setMaximum(qMax(1,ui->anime_progress->value()));
    ui->manga_progress->setMaximum(qMax(1,ui->manga_progress->value()));
    QMessageBox::information(this, tr("Info"),
                             tr("Saving completed"));
    enableControls(true);
}

void MainWindow::onBlinkError(QString msg)
{
    enableControls(true);
    QMessageBox::critical(this, tr("Error"),
                          tr("An error occured during operation:/n%1").arg(msg));
}
<|endoftext|>


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

#include <QtCore/QDate>
#include <QtCore/QTime>
#include <QtCore/QString>
#include <QtCore/QDir>
#include <QtCore/QSettings>
#include <QtGui/QRgb>
#include <QtWidgets/QMessageBox>

#include <QtCore/QDebug>

#define URL_KANCOLLE "http://www.dmm.com/netgame/social/-/gadgets/=/app_id=854854/"
#define FLASH_POS_SEARCH_START_Y    40      //Flashの位置を探すときにY方向の開始座標（つまりDMMのヘッダを飛ばす）
#define KANCOLLE_WIDTH   800
#define KANCOLLE_HEIGHT  480

#define SETTING_FILE_NAME       "settings.ini"
#define SETTING_FILE_FORMAT     QSettings::IniFormat

#define STATUS_BAR_MSG_TIME     5000

class MainWindow::Private
{
public:
    Private(MainWindow *parent);
    ~Private();

private:
    MainWindow *q;

public:
    Ui::MainWindow ui;
    QSettings settings;       //設定管理
    //設定保存データ
    QString savePath;         //保存パス
    QPoint flashPos;          //座標

    //一時データ
    QPoint scroolPos;         //スクロール量
    bool finishCalibrated;    //キャリブレーション済み
};

MainWindow::Private::Private(MainWindow *parent)
    : q(parent)
    , settings(SETTING_FILE_NAME, SETTING_FILE_FORMAT)
    , savePath(settings.value("path", "").toString())
    , flashPos(settings.value("flashPos", QPoint(0, 0)).toPoint())
    , finishCalibrated(settings.value("finishCalibrated", false).toBool())
{
    ui.setupUi(q);
    ui.webView->page()->networkAccessManager()->setCookieJar(new CookieJar(q));
    connect(ui.capture, &QAction::triggered, q, &MainWindow::captureGame);
    connect(ui.reload, &QAction::triggered, ui.webView, &QWebView::reload);
    connect(ui.exit, &QAction::triggered, q, &MainWindow::close);
    connect(ui.adjust, &QAction::triggered, q, &MainWindow::calibration);

    //設定ダイアログ表示
    connect(ui.preferences, &QAction::triggered, [this]() {
        SettingsDialog dlg(q);
        dlg.setSavePath(savePath);
        if (dlg.exec()) {
            //設定更新
            savePath = dlg.savePath();
        }
    });

    //アバウト
    connect(ui.about, &QAction::triggered, [this]() {
        QMessageBox::about(q, MainWindow::tr("Kan Memo"), MainWindow::tr("Kan Memo -KanMusu Memory-\n\nCopyright 2013 IoriAYANE"));
    });

    connect(ui.webView, &QWebView::loadStarted, [this](){
       ui.progressBar->show();
    });
    //WebViewの読込み完了
    connect(ui.webView, &QWebView::loadFinished, [this](bool ok) {
        if (ok) {
            ui.statusBar->showMessage(MainWindow::tr("complete"), STATUS_BAR_MSG_TIME);
        } else {
            ui.statusBar->showMessage(MainWindow::tr("error"), STATUS_BAR_MSG_TIME);
        }
        ui.progressBar->hide();
    });

    //WebViewの読込み状態
    connect(ui.webView, &QWebView::loadProgress, ui.progressBar, &QProgressBar::setValue);

    connect(ui.webView->page(), SIGNAL(scrollRequested(int,int,QRect)), q, SLOT(scrollRequested(int,int,QRect)));
}

MainWindow::Private::~Private()
{
    //設定保存
    settings.setValue("path", savePath);
    settings.setValue("flashPos", flashPos);
    settings.setValue("finishCalibrated", finishCalibrated);
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , d(new Private(this))
{
    if(d->savePath.length() == 0){
        //設定を促す
        QMessageBox::information(this
                                 , tr("Kan Memo")
                                 , tr("Please select a folder to save the image of KanMusu."));
        d->savePath = SettingsDialog::selectSavePath(this, QDir::homePath());
    }

    //設定
    QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true);
    QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptEnabled, true);
    //艦これ読込み
    d->ui.webView->load(QUrl(URL_KANCOLLE));

    connect(this, &MainWindow::destroyed, [this]() {delete d;});
}

//思い出を残す
void MainWindow::captureGame()
{
    qDebug() << "captureGame";

    if(!d->finishCalibrated){
        //キャリブレーションされてないので確認して実行
        calibration();
    }

    //キャリブレーションしてなければ実行しない
    if(!d->finishCalibrated){
        d->ui.statusBar->showMessage(QString(tr("cancel")), STATUS_BAR_MSG_TIME);
        return;
    }

    int x = d->flashPos.x() + d->scroolPos.x();// int((d->ui.webView->size().width()-16) / 2) - int(800 / 2);
    int y = d->flashPos.y() + d->scroolPos.y();
    QImage img(d->ui.webView->size(), QImage::Format_ARGB32);
    QImage img2(KANCOLLE_WIDTH, KANCOLLE_HEIGHT, QImage::Format_ARGB32);
    QPainter painter(&img);
    QPainter painter2(&img2);
    //全体を描画
    d->ui.webView->page()->view()->render(&painter);
    //2つ目へ必要な分だけコピー
    painter2.drawImage(QPoint(0,0), img, QRect(x, y, KANCOLLE_WIDTH, KANCOLLE_HEIGHT));
    QDate date(QDate::currentDate());
    QTime time(QTime::currentTime());
    QString path = d->savePath + "/kanmusu";
    path.append(QString("_%1-%2-%3_%4-%5-%6-%7")
                .arg(date.year()).arg(date.month()).arg(date.day())
                .arg(time.hour()).arg(time.minute()).arg(time.second()).arg(time.msec())
                );
    path.append(".png");
    //    path.append(".jpg");
    qDebug() << "path:" << path;

    //保存する
    if(img2.save(path)){
        d->ui.statusBar->showMessage(tr("saving to %1...").arg(path), STATUS_BAR_MSG_TIME);

        //つぶやくダイアログ
        TweetDialog dlg(this);
        dlg.setImagePath(path);
        dlg.setToken(d->settings.value("token", "").toString());
        dlg.setTokenSecret(d->settings.value("tokenSecret", "").toString());
        dlg.user_id(d->settings.value("user_id", "").toString());
        dlg.screen_name(d->settings.value("screen_name", "").toString());
        dlg.exec();
        d->settings.setValue("token", dlg.token());
        d->settings.setValue("tokenSecret", dlg.tokenSecret());
        d->settings.setValue("user_id", dlg.user_id());
        d->settings.setValue("screen_name", dlg.screen_name());
        //        d->settings.sync();

    }else{
        d->ui.statusBar->showMessage(tr("failed"), STATUS_BAR_MSG_TIME);
    }

}
//キャリブレーション
void MainWindow::calibration()
{
    qDebug() << "calibration";

//    QMessageBox::information(this
//                             , tr("Information")
//                             , tr("Calibrate Kankore position."));
    QMessageBox msgdlg(this);
    msgdlg.setWindowTitle(tr("Information"));
    msgdlg.setText(tr("Search Kankore position.\nPlease be performed in the state that it seems the whole of KanColle."));
    msgdlg.setStandardButtons(QMessageBox::Yes);
    msgdlg.addButton("No", QMessageBox::RejectRole);
    msgdlg.exec();
    qDebug() << "calib:" << msgdlg.result();
    if(msgdlg.result() != QMessageBox::Yes)
        return;

    int set_count = 0;
    QImage img(d->ui.webView->size(), QImage::Format_ARGB32);
    QPainter painter(&img);
    int w = d->ui.webView->size().width();
    int h = d->ui.webView->size().height();
    QRgb rgb;

    //全体を描画
    d->ui.webView->page()->view()->render(&painter);

    //横方向にはじっこを調べる
    for(int i=0; i<(w/2); i++){
        rgb = img.pixel(i, h/2);
        if(qGray(rgb) < 250){
            qDebug() << "found X:" << i << "," << (h/2) << "/" << qGray(rgb)
                     << "/" << qRed(rgb) << "," << qGreen(rgb) << "," << qBlue(rgb);
            d->flashPos.setX(i);
            set_count++;
            break;
        }
    }
    //縦方向に端っこを調べる
    //    for(int i=h-1; i>=(h/2); i--){
    //        rgb = img.pixel(w/2, i);
    //        if(qGray(rgb) < 250){
    //            qDebug() << "found Y:" << (w/2) << "," << (i-KANKORE_HEIGHT) << "/" << qGray(rgb)
    //                     << "/" << qRed(rgb) << "," << qGreen(rgb) << "," << qBlue(rgb);
    //            d->flashPos.setY(i - KANKORE_HEIGHT + 1 - d->scroolPos.y());
    //            set_count++;
    //            break;
    //        }
    //    }
    for(int i=FLASH_POS_SEARCH_START_Y; i<h; i++){
        rgb = img.pixel(w/2, i);
        if(qGray(rgb) < 250){
            qDebug() << "found Y:" << (w/2) << "," << i << "/" << qGray(rgb)
                     << "/" << qRed(rgb) << "," << qGreen(rgb) << "," << qBlue(rgb);
            d->flashPos.setY(i - d->scroolPos.y());
            set_count++;
            break;
        }
    }
    //キャリブレーション済み
    if(set_count == 2){
        d->ui.statusBar->showMessage(tr("calibration succeded"), STATUS_BAR_MSG_TIME);
        d->finishCalibrated = true;
    }else{
        d->ui.statusBar->showMessage(tr("calibration failed"), STATUS_BAR_MSG_TIME);
        d->finishCalibrated = false;
    }
}

//スクロール状態
void MainWindow::scrollRequested(int dx, int dy, const QRect &rectToScroll)
{
    Q_UNUSED(dx)
    Q_UNUSED(rectToScroll)
    //    qDebug() << "scroll:" << dx << "," << dy << "/" << rectToScroll;
    d->scroolPos.setY(d->scroolPos.y() + dy);
    //    if(d->scroolPos.y() < 0)
    //        d->scroolPos.setY(0);

    qDebug() << "scroll:" << d->scroolPos.y();
}
<commit_msg>cleanup taking screen shot code<commit_after>/*
 * Copyright 2013 IoriAYANE
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "tweetdialog.h"
#include "settingsdialog.h"
#include "cookiejar.h"

#include <QtCore/QDate>
#include <QtCore/QTime>
#include <QtCore/QString>
#include <QtCore/QDir>
#include <QtCore/QSettings>
#include <QtGui/QRgb>
#include <QtWidgets/QMessageBox>

#include <QtCore/QDebug>

#define URL_KANCOLLE "http://www.dmm.com/netgame/social/-/gadgets/=/app_id=854854/"
#define FLASH_POS_SEARCH_START_Y    40      //Flashの位置を探すときにY方向の開始座標（つまりDMMのヘッダを飛ばす）
#define KANCOLLE_WIDTH   800
#define KANCOLLE_HEIGHT  480

#define SETTING_FILE_NAME       "settings.ini"
#define SETTING_FILE_FORMAT     QSettings::IniFormat

#define STATUS_BAR_MSG_TIME     5000

class MainWindow::Private
{
public:
    Private(MainWindow *parent);
    ~Private();

private:
    MainWindow *q;

public:
    Ui::MainWindow ui;
    QSettings settings;       //設定管理
    //設定保存データ
    QString savePath;         //保存パス
    QPoint flashPos;          //座標

    //一時データ
    QPoint scroolPos;         //スクロール量
    bool finishCalibrated;    //キャリブレーション済み
};

MainWindow::Private::Private(MainWindow *parent)
    : q(parent)
    , settings(SETTING_FILE_NAME, SETTING_FILE_FORMAT)
    , savePath(settings.value("path", "").toString())
    , flashPos(settings.value("flashPos", QPoint(0, 0)).toPoint())
    , finishCalibrated(settings.value("finishCalibrated", false).toBool())
{
    ui.setupUi(q);
    ui.webView->page()->networkAccessManager()->setCookieJar(new CookieJar(q));
    connect(ui.capture, &QAction::triggered, q, &MainWindow::captureGame);
    connect(ui.reload, &QAction::triggered, ui.webView, &QWebView::reload);
    connect(ui.exit, &QAction::triggered, q, &MainWindow::close);
    connect(ui.adjust, &QAction::triggered, q, &MainWindow::calibration);

    //設定ダイアログ表示
    connect(ui.preferences, &QAction::triggered, [this]() {
        SettingsDialog dlg(q);
        dlg.setSavePath(savePath);
        if (dlg.exec()) {
            //設定更新
            savePath = dlg.savePath();
        }
    });

    //アバウト
    connect(ui.about, &QAction::triggered, [this]() {
        QMessageBox::about(q, MainWindow::tr("Kan Memo"), MainWindow::tr("Kan Memo -KanMusu Memory-\n\nCopyright 2013 IoriAYANE"));
    });

    connect(ui.webView, &QWebView::loadStarted, [this](){
       ui.progressBar->show();
    });
    //WebViewの読込み完了
    connect(ui.webView, &QWebView::loadFinished, [this](bool ok) {
        if (ok) {
            ui.statusBar->showMessage(MainWindow::tr("complete"), STATUS_BAR_MSG_TIME);
        } else {
            ui.statusBar->showMessage(MainWindow::tr("error"), STATUS_BAR_MSG_TIME);
        }
        ui.progressBar->hide();
    });

    //WebViewの読込み状態
    connect(ui.webView, &QWebView::loadProgress, ui.progressBar, &QProgressBar::setValue);

    connect(ui.webView->page(), SIGNAL(scrollRequested(int,int,QRect)), q, SLOT(scrollRequested(int,int,QRect)));
}

MainWindow::Private::~Private()
{
    //設定保存
    settings.setValue("path", savePath);
    settings.setValue("flashPos", flashPos);
    settings.setValue("finishCalibrated", finishCalibrated);
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , d(new Private(this))
{
    if(d->savePath.length() == 0){
        //設定を促す
        QMessageBox::information(this
                                 , tr("Kan Memo")
                                 , tr("Please select a folder to save the image of KanMusu."));
        d->savePath = SettingsDialog::selectSavePath(this, QDir::homePath());
    }

    //設定
    QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true);
    QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptEnabled, true);
    //艦これ読込み
    d->ui.webView->load(QUrl(URL_KANCOLLE));

    connect(this, &MainWindow::destroyed, [this]() {delete d;});
}

//思い出を残す
void MainWindow::captureGame()
{
    qDebug() << "captureGame";

    if(!d->finishCalibrated){
        //キャリブレーションされてないので確認して実行
        calibration();
    }

    //キャリブレーションしてなければ実行しない
    if(!d->finishCalibrated){
        d->ui.statusBar->showMessage(QString(tr("cancel")), STATUS_BAR_MSG_TIME);
        return;
    }

    int x = d->flashPos.x() + d->scroolPos.x();// int((d->ui.webView->size().width()-16) / 2) - int(800 / 2);
    int y = d->flashPos.y() + d->scroolPos.y();

    QImage img(KANCOLLE_WIDTH, KANCOLLE_HEIGHT, QImage::Format_ARGB32);
    QPainter painter(&img);
    //全体を描画
    d->ui.webView->page()->view()->render(&painter, QPoint(0,0), QRegion(x, y, KANCOLLE_WIDTH, KANCOLLE_HEIGHT));

    QString path = QStringLiteral("%1/kanmusu_%2.png").arg(d->savePath).arg(QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz")));
    qDebug() << "path:" << path;

    //保存する
    d->ui.statusBar->showMessage(tr("saving to %1...").arg(path), STATUS_BAR_MSG_TIME);
    if(img.save(path)){
        //つぶやくダイアログ
        TweetDialog dlg(this);
        dlg.setImagePath(path);
        dlg.setToken(d->settings.value("token", "").toString());
        dlg.setTokenSecret(d->settings.value("tokenSecret", "").toString());
        dlg.user_id(d->settings.value("user_id", "").toString());
        dlg.screen_name(d->settings.value("screen_name", "").toString());
        dlg.exec();
        d->settings.setValue("token", dlg.token());
        d->settings.setValue("tokenSecret", dlg.tokenSecret());
        d->settings.setValue("user_id", dlg.user_id());
        d->settings.setValue("screen_name", dlg.screen_name());
        //        d->settings.sync();

    }else{
        d->ui.statusBar->showMessage(tr("failed"), STATUS_BAR_MSG_TIME);
    }

}
//キャリブレーション
void MainWindow::calibration()
{
    qDebug() << "calibration";

//    QMessageBox::information(this
//                             , tr("Information")
//                             , tr("Calibrate Kankore position."));
    QMessageBox msgdlg(this);
    msgdlg.setWindowTitle(tr("Information"));
    msgdlg.setText(tr("Search Kankore position.\nPlease be performed in the state that it seems the whole of KanColle."));
    msgdlg.setStandardButtons(QMessageBox::Yes);
    msgdlg.addButton("No", QMessageBox::RejectRole);
    msgdlg.exec();
    qDebug() << "calib:" << msgdlg.result();
    if(msgdlg.result() != QMessageBox::Yes)
        return;

    int set_count = 0;
    QImage img(d->ui.webView->size(), QImage::Format_ARGB32);
    QPainter painter(&img);
    int w = d->ui.webView->size().width();
    int h = d->ui.webView->size().height();
    QRgb rgb;

    //全体を描画
    d->ui.webView->page()->view()->render(&painter);

    //横方向にはじっこを調べる
    for(int i=0; i<(w/2); i++){
        rgb = img.pixel(i, h/2);
        if(qGray(rgb) < 250){
            qDebug() << "found X:" << i << "," << (h/2) << "/" << qGray(rgb)
                     << "/" << qRed(rgb) << "," << qGreen(rgb) << "," << qBlue(rgb);
            d->flashPos.setX(i);
            set_count++;
            break;
        }
    }
    //縦方向に端っこを調べる
    //    for(int i=h-1; i>=(h/2); i--){
    //        rgb = img.pixel(w/2, i);
    //        if(qGray(rgb) < 250){
    //            qDebug() << "found Y:" << (w/2) << "," << (i-KANKORE_HEIGHT) << "/" << qGray(rgb)
    //                     << "/" << qRed(rgb) << "," << qGreen(rgb) << "," << qBlue(rgb);
    //            d->flashPos.setY(i - KANKORE_HEIGHT + 1 - d->scroolPos.y());
    //            set_count++;
    //            break;
    //        }
    //    }
    for(int i=FLASH_POS_SEARCH_START_Y; i<h; i++){
        rgb = img.pixel(w/2, i);
        if(qGray(rgb) < 250){
            qDebug() << "found Y:" << (w/2) << "," << i << "/" << qGray(rgb)
                     << "/" << qRed(rgb) << "," << qGreen(rgb) << "," << qBlue(rgb);
            d->flashPos.setY(i - d->scroolPos.y());
            set_count++;
            break;
        }
    }
    //キャリブレーション済み
    if(set_count == 2){
        d->ui.statusBar->showMessage(tr("calibration succeded"), STATUS_BAR_MSG_TIME);
        d->finishCalibrated = true;
    }else{
        d->ui.statusBar->showMessage(tr("calibration failed"), STATUS_BAR_MSG_TIME);
        d->finishCalibrated = false;
    }
}

//スクロール状態
void MainWindow::scrollRequested(int dx, int dy, const QRect &rectToScroll)
{
    Q_UNUSED(dx)
    Q_UNUSED(rectToScroll)
    //    qDebug() << "scroll:" << dx << "," << dy << "/" << rectToScroll;
    d->scroolPos.setY(d->scroolPos.y() + dy);
    //    if(d->scroolPos.y() < 0)
    //        d->scroolPos.setY(0);

    qDebug() << "scroll:" << d->scroolPos.y();
}
<|endoftext|>


<commit_before>#define FTS_FUZZY_MATCH_IMPLEMENTATION
#include <QtWidgets>
#include "mainwindow.h"
#include <fts_fuzzy_match.h>
#include <glob.h>
#include <string>
#include <map>
#include <database.h>

Window::Window(QWidget *parent) : QMainWindow(parent)
{
    // Initalise database
    db.setDatabaseName("recipes.db");

    // Initialise widgets
    searchBox = new SearchBox();
    searchBox->setPlaceholderText("Search for recipes");
    recipeBox = new QComboBox();
    QStringList recipeCategories;
    recipeCategories << "All Recipes" << "Beef" << "Chicken" << "Dessert" << "Lamb" << "Pork" << "Seafood" << "Turkey" << "Veggie";
    recipeBox->addItems(recipeCategories);
    createRecipeList();
    numResults = new QLabel();
    recipeView = new QWebEngineView();

    // Set layout
    centralWidget = new QWidget();
    QGridLayout *sidebar = new QGridLayout();
    sidebar->addWidget(searchBox, 0, 0, 1, 2);
    sidebar->addWidget(numResults, 1, 0, 1, 1);
    sidebar->addWidget(recipeBox, 1, 1, 1, 1);
    sidebar->addWidget(recipeList, 2, 0, 1, 2);

    QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget);
    mainLayout->addLayout(sidebar, 1);
    mainLayout->addWidget(recipeView, 3);

    centralWidget->setLayout(mainLayout);
    centralWidget->show();
    setCentralWidget(centralWidget);

    // Create menus
    optionsMenu = new QMenu("Options");
    updateDb = new QAction("Update database", this);
    connect(updateDb, &QAction::triggered, this, &Window::updateDatabase);
    cleanDb = new QAction("Clean database", this);
    connect(cleanDb, &QAction::triggered, this, &Window::cleanDatabase);
    optionsMenu->addAction(updateDb);
    optionsMenu->addAction(cleanDb);
    menuBar()->addMenu(optionsMenu);

    // Set window paramters
    setWindowTitle(tr("Find Recipes"));
    setMinimumSize(940, 400);

    // Set signals
    connect(recipeList, &QListWidget::itemClicked, this, &Window::openFile);
    connect(searchBox, SIGNAL(inputText(QString)), this, SLOT(updateRecipesDiplay(QString)));
    connect(searchBox, SIGNAL(returnPressed()), recipeList, SLOT(setFocus()));
    connect(recipeBox, SIGNAL(currentTextChanged(QString)), searchBox, SLOT(recipeFiterChanged(QString)));

    // Update on startup
    updateDatabase();
}

// Get list of files according to glob patternn
QStringList globVector(const std::string& pattern){
    glob_t glob_result;
    glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result);
    QStringList files;
    for(unsigned int i=0; i<glob_result.gl_pathc; ++i){
        files << QString(glob_result.gl_pathv[i]);
    }
    globfree(&glob_result);
    return files;
}

void SearchBox::recipeFiterChanged(QString newFilter){
    // When the recipe filter changes, emit this signal to call updateRecipeDisplay
    emit inputText(text());
}

void SearchBox::keyPressEvent(QKeyEvent *evt){
    QLineEdit::keyPressEvent(evt);
    setPlaceholderText("Search for recipes");
    // When the search changes, emit this signal to call updateRecipeDisplay
    emit inputText(text());
}

void Window::updateRecipesDiplay(QString searchText){
    recipeList->clear();
    QList<QListWidgetItem*> recipes;

    if (searchText.isEmpty()) {
        recipes = getAllRecipes();
        for (int i=0; i<recipes.size(); ++i){
            recipeList->addItem(recipes[i]);
        }
        recipeList->sortItems();

    }else{
        recipes = getMatchingRecipes(searchText);
        for (int i=0; i<recipes.size(); ++i){
            recipeList->addItem(recipes[i]);
        }
    }

    recipeList->setDragEnabled(false);

    QString text = QString("%1 recipes").arg(recipes.size());
    if (recipes.size() == 1){
        text = "1 recipe";
    }
    numResults->setText(text);
}

QList<QListWidgetItem*> Window::getAllRecipes(){
    QList<QListWidgetItem*> recipes;

    // Open database and execute query
    db.open();
    // Prepare query based on filter
    QSqlQuery query = QSqlQuery();
    if(recipeBox->currentText() != "All Recipes"){
        QString category = recipeBox->currentText();
        query.prepare("select TITLE, IMG_PATH, HTML_PATH from RECIPES where CATEGORY = :category");
        query.bindValue(":category", category);
        query.setForwardOnly(true);
    }else{
        query.prepare("select TITLE, IMG_PATH, HTML_PATH from RECIPES");
        query.setForwardOnly(true);
    }
    // Execute query
    query.exec();

    while(query.next()){
        // Extract info from query results
        QString title = query.value(0).toString();
        QString img_path = query.value(1).toString();
        QString html_path = query.value(2).toString();

        // Create QListWidgetItems
        QListWidgetItem *recipe = new QListWidgetItem;
        recipe->setText(title);
        recipe->setData(Qt::UserRole, html_path);

        QImage *img = new QImage();
        bool loaded = img->load(img_path);
        if (loaded){
            recipe->setIcon(QPixmap::fromImage(*img));
        }else{
            // If image doesn't exist, use placeholder image
            bool loaded = img->load("./images/Placeholder.jpg");
            if (loaded){
                recipe->setIcon(QPixmap::fromImage(*img));
            }
        }
        recipes.append(recipe);
    }
    db.close();
    return recipes;
}

QList<QListWidgetItem*> Window::getMatchingRecipes(QString searchText){
    QList<QListWidgetItem*> recipes;

    // Get matching recipes and their scores. The QStringList contains title, img_path, file_path in order.
    std::map<double, QStringList> matchingRecipes = findMatches(searchText);
    // Build QListWidgetItems and add to QList
    // By default the map should be in ascending order, so use reverse iterator to get highest matches first
    for (auto iter = matchingRecipes.rbegin(); iter != matchingRecipes.rend(); ++iter){

        QString title = iter->second[0];
        QString img_path = iter->second[1];
        QString html_path = iter->second[2];

        QListWidgetItem *recipe = new QListWidgetItem;
        recipe->setText(title);
        recipe->setData(Qt::UserRole, html_path);

        QImage *img = new QImage();
        bool loaded = img->load(img_path);
        if (loaded){
            recipe->setIcon(QPixmap::fromImage(*img));
        }else{
            // If image doesn't exist, use placeholder image
            bool loaded = img->load("./images/Placeholder.jpg");
            if (loaded){
                recipe->setIcon(QPixmap::fromImage(*img));
            }
        }
        recipes.append(recipe);
    }

    return recipes;
}

std::map<double, QStringList> Window::findMatches(QString text)
{
    // Open database
    db.open();
    // Prepare query based on filter
    QSqlQuery query = QSqlQuery();
    if(recipeBox->currentText() != "All Recipes"){
        QString category = recipeBox->currentText();
        query.prepare("select TITLE, IMG_PATH, HTML_PATH from RECIPES where CATEGORY = :category");
        query.bindValue(":category", category);
        query.setForwardOnly(true);
    }else{
        query.prepare("select TITLE, IMG_PATH, HTML_PATH from RECIPES");
        query.setForwardOnly(true);
    }
    // Execute query
    query.exec();

    // Get matching files and their scores
    std::map<double, QStringList> matchingFiles;
    std::string txtstr = text.toStdString();
    while(query.next()){
        int score;
        QString title = query.value(0).toString();
        QString img_path = query.value(1).toString().replace("\'", "").replace(",", "");
        QString file_path = query.value(2).toString();

        std::string titlestr = title.toStdString();
        if (fts::fuzzy_match(txtstr.c_str(), titlestr.c_str(), score)){
            // If a map entry already has the current score, increase score by 0.01.
            double dbscore = (double)score;
            if (matchingFiles.count(dbscore) > 0){
                dbscore += 0.01;
            }
            matchingFiles[dbscore] = QStringList() << title << img_path << file_path;
        }
    }
    db.close();
    return matchingFiles;
}

void Window::createRecipeList()
{
    recipeList = new QListWidget();
    recipeList->setViewMode(QListView::IconMode);
    recipeList->setGridSize(QSize(212, int(212/1.51)) );
    recipeList->setIconSize(QSize(199, int(199/1.78)) );
    recipeList->setWordWrap(true);
    recipeList->setTextElideMode(Qt::ElideNone);
    recipeList->horizontalScrollBar()->setEnabled(false);
    recipeList->horizontalScrollBar()->setVisible(false);
}

void Window::openFile(QListWidgetItem *recipe)
{
    // Read hidden data to find full file path
    QString path = recipe->data(Qt::UserRole).toString();
    QString url = "file://" + currentDir.absoluteFilePath(path);
    recipeView->load(url);
}

void Window::updateDatabase(){
    int num_updates = db_ops::update_database(&db);
    QString updated_text;
    if (num_updates == 0){
        updated_text = QString("Search for recipes");
    }else{
        updated_text = QString("Search for recipes - Updated!").arg(num_updates);
    }
    searchBox->setPlaceholderText(updated_text);
    // Repopulate list
    updateRecipesDiplay("");
}

void Window::cleanDatabase(){
    int num_removals = db_ops::clean_database(&db);
    QString cleaned_text;
    if (num_removals == 1){
        cleaned_text = QString("Search for recipes - Removed 1 recipe!");
    }else{
        cleaned_text = QString("Search for recipes - Removed %1 recipes!").arg(num_removals);
    }
    searchBox->setPlaceholderText(cleaned_text);
    // Repopulate list
    updateRecipesDiplay("");
}

void Window::resizeEvent(QResizeEvent *event){
    int iconWidth, iconHeight, gridWidth, gridHeight;
    double gridRatio = 1.51;
    double iconRatio = 1.78;
    QSize recipeListSize = recipeList->size();

    // Set gridwith based on recipeList size and ensure it doesn't go below minimum value
    gridWidth = qMax(int(recipeListSize.width()) - 17.0, 212.0);
    // Calculate other parameters based on ratios of default values.
    gridHeight = int(gridWidth/gridRatio);
    iconWidth = gridWidth - 13;
    iconHeight = int(iconWidth/iconRatio);

    recipeList->setIconSize(QSize(iconWidth, iconHeight));
    recipeList->setGridSize(QSize(gridWidth, gridHeight));
    recipeList->setUniformItemSizes(true);
}

<commit_msg>Disable javascript in webview, if going to source website. Should block adverts.<commit_after>#define FTS_FUZZY_MATCH_IMPLEMENTATION
#include <QtWidgets>
#include "mainwindow.h"
#include <fts_fuzzy_match.h>
#include <glob.h>
#include <string>
#include <map>
#include <database.h>

Window::Window(QWidget *parent) : QMainWindow(parent)
{
    // Initalise database
    db.setDatabaseName("recipes.db");

    // Initialise widgets
    searchBox = new SearchBox();
    searchBox->setPlaceholderText("Search for recipes");
    recipeBox = new QComboBox();
    QStringList recipeCategories;
    recipeCategories << "All Recipes" << "Beef" << "Chicken" << "Dessert" << "Lamb" << "Pork" << "Seafood" << "Turkey" << "Veggie";
    recipeBox->addItems(recipeCategories);
    createRecipeList();
    numResults = new QLabel();
    recipeView = new QWebEngineView();
    recipeView->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false);

    // Set layout
    centralWidget = new QWidget();
    QGridLayout *sidebar = new QGridLayout();
    sidebar->addWidget(searchBox, 0, 0, 1, 2);
    sidebar->addWidget(numResults, 1, 0, 1, 1);
    sidebar->addWidget(recipeBox, 1, 1, 1, 1);
    sidebar->addWidget(recipeList, 2, 0, 1, 2);

    QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget);
    mainLayout->addLayout(sidebar, 1);
    mainLayout->addWidget(recipeView, 3);

    centralWidget->setLayout(mainLayout);
    centralWidget->show();
    setCentralWidget(centralWidget);

    // Create menus
    optionsMenu = new QMenu("Options");
    updateDb = new QAction("Update database", this);
    connect(updateDb, &QAction::triggered, this, &Window::updateDatabase);
    cleanDb = new QAction("Clean database", this);
    connect(cleanDb, &QAction::triggered, this, &Window::cleanDatabase);
    optionsMenu->addAction(updateDb);
    optionsMenu->addAction(cleanDb);
    menuBar()->addMenu(optionsMenu);

    // Set window paramters
    setWindowTitle(tr("Find Recipes"));
    setMinimumSize(940, 400);

    // Set signals
    connect(recipeList, &QListWidget::itemClicked, this, &Window::openFile);
    connect(searchBox, SIGNAL(inputText(QString)), this, SLOT(updateRecipesDiplay(QString)));
    connect(searchBox, SIGNAL(returnPressed()), recipeList, SLOT(setFocus()));
    connect(recipeBox, SIGNAL(currentTextChanged(QString)), searchBox, SLOT(recipeFiterChanged(QString)));

    // Update on startup
    updateDatabase();
}

// Get list of files according to glob patternn
QStringList globVector(const std::string& pattern){
    glob_t glob_result;
    glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result);
    QStringList files;
    for(unsigned int i=0; i<glob_result.gl_pathc; ++i){
        files << QString(glob_result.gl_pathv[i]);
    }
    globfree(&glob_result);
    return files;
}

void SearchBox::recipeFiterChanged(QString newFilter){
    // When the recipe filter changes, emit this signal to call updateRecipeDisplay
    emit inputText(text());
}

void SearchBox::keyPressEvent(QKeyEvent *evt){
    QLineEdit::keyPressEvent(evt);
    setPlaceholderText("Search for recipes");
    // When the search changes, emit this signal to call updateRecipeDisplay
    emit inputText(text());
}

void Window::updateRecipesDiplay(QString searchText){
    recipeList->clear();
    QList<QListWidgetItem*> recipes;

    if (searchText.isEmpty()) {
        recipes = getAllRecipes();
        for (int i=0; i<recipes.size(); ++i){
            recipeList->addItem(recipes[i]);
        }
        recipeList->sortItems();

    }else{
        recipes = getMatchingRecipes(searchText);
        for (int i=0; i<recipes.size(); ++i){
            recipeList->addItem(recipes[i]);
        }
    }

    recipeList->setDragEnabled(false);

    QString text = QString("%1 recipes").arg(recipes.size());
    if (recipes.size() == 1){
        text = "1 recipe";
    }
    numResults->setText(text);
}

QList<QListWidgetItem*> Window::getAllRecipes(){
    QList<QListWidgetItem*> recipes;

    // Open database and execute query
    db.open();
    // Prepare query based on filter
    QSqlQuery query = QSqlQuery();
    if(recipeBox->currentText() != "All Recipes"){
        QString category = recipeBox->currentText();
        query.prepare("select TITLE, IMG_PATH, HTML_PATH from RECIPES where CATEGORY = :category");
        query.bindValue(":category", category);
        query.setForwardOnly(true);
    }else{
        query.prepare("select TITLE, IMG_PATH, HTML_PATH from RECIPES");
        query.setForwardOnly(true);
    }
    // Execute query
    query.exec();

    while(query.next()){
        // Extract info from query results
        QString title = query.value(0).toString();
        QString img_path = query.value(1).toString();
        QString html_path = query.value(2).toString();

        // Create QListWidgetItems
        QListWidgetItem *recipe = new QListWidgetItem;
        recipe->setText(title);
        recipe->setData(Qt::UserRole, html_path);

        QImage *img = new QImage();
        bool loaded = img->load(img_path);
        if (loaded){
            recipe->setIcon(QPixmap::fromImage(*img));
        }else{
            // If image doesn't exist, use placeholder image
            bool loaded = img->load("./images/Placeholder.jpg");
            if (loaded){
                recipe->setIcon(QPixmap::fromImage(*img));
            }
        }
        recipes.append(recipe);
    }
    db.close();
    return recipes;
}

QList<QListWidgetItem*> Window::getMatchingRecipes(QString searchText){
    QList<QListWidgetItem*> recipes;

    // Get matching recipes and their scores. The QStringList contains title, img_path, file_path in order.
    std::map<double, QStringList> matchingRecipes = findMatches(searchText);
    // Build QListWidgetItems and add to QList
    // By default the map should be in ascending order, so use reverse iterator to get highest matches first
    for (auto iter = matchingRecipes.rbegin(); iter != matchingRecipes.rend(); ++iter){

        QString title = iter->second[0];
        QString img_path = iter->second[1];
        QString html_path = iter->second[2];

        QListWidgetItem *recipe = new QListWidgetItem;
        recipe->setText(title);
        recipe->setData(Qt::UserRole, html_path);

        QImage *img = new QImage();
        bool loaded = img->load(img_path);
        if (loaded){
            recipe->setIcon(QPixmap::fromImage(*img));
        }else{
            // If image doesn't exist, use placeholder image
            bool loaded = img->load("./images/Placeholder.jpg");
            if (loaded){
                recipe->setIcon(QPixmap::fromImage(*img));
            }
        }
        recipes.append(recipe);
    }

    return recipes;
}

std::map<double, QStringList> Window::findMatches(QString text)
{
    // Open database
    db.open();
    // Prepare query based on filter
    QSqlQuery query = QSqlQuery();
    if(recipeBox->currentText() != "All Recipes"){
        QString category = recipeBox->currentText();
        query.prepare("select TITLE, IMG_PATH, HTML_PATH from RECIPES where CATEGORY = :category");
        query.bindValue(":category", category);
        query.setForwardOnly(true);
    }else{
        query.prepare("select TITLE, IMG_PATH, HTML_PATH from RECIPES");
        query.setForwardOnly(true);
    }
    // Execute query
    query.exec();

    // Get matching files and their scores
    std::map<double, QStringList> matchingFiles;
    std::string txtstr = text.toStdString();
    while(query.next()){
        int score;
        QString title = query.value(0).toString();
        QString img_path = query.value(1).toString().replace("\'", "").replace(",", "");
        QString file_path = query.value(2).toString();

        std::string titlestr = title.toStdString();
        if (fts::fuzzy_match(txtstr.c_str(), titlestr.c_str(), score)){
            // If a map entry already has the current score, increase score by 0.01.
            double dbscore = (double)score;
            if (matchingFiles.count(dbscore) > 0){
                dbscore += 0.01;
            }
            matchingFiles[dbscore] = QStringList() << title << img_path << file_path;
        }
    }
    db.close();
    return matchingFiles;
}

void Window::createRecipeList()
{
    recipeList = new QListWidget();
    recipeList->setViewMode(QListView::IconMode);
    recipeList->setGridSize(QSize(212, int(212/1.51)) );
    recipeList->setIconSize(QSize(199, int(199/1.78)) );
    recipeList->setWordWrap(true);
    recipeList->setTextElideMode(Qt::ElideNone);
    recipeList->horizontalScrollBar()->setEnabled(false);
    recipeList->horizontalScrollBar()->setVisible(false);
}

void Window::openFile(QListWidgetItem *recipe)
{
    // Read hidden data to find full file path
    QString path = recipe->data(Qt::UserRole).toString();
    QString url = "file://" + currentDir.absoluteFilePath(path);
    recipeView->load(url);
}

void Window::updateDatabase(){
    int num_updates = db_ops::update_database(&db);
    QString updated_text;
    if (num_updates == 0){
        updated_text = QString("Search for recipes");
    }else{
        updated_text = QString("Search for recipes - Updated!").arg(num_updates);
    }
    searchBox->setPlaceholderText(updated_text);
    // Repopulate list
    updateRecipesDiplay("");
}

void Window::cleanDatabase(){
    int num_removals = db_ops::clean_database(&db);
    QString cleaned_text;
    if (num_removals == 1){
        cleaned_text = QString("Search for recipes - Removed 1 recipe!");
    }else{
        cleaned_text = QString("Search for recipes - Removed %1 recipes!").arg(num_removals);
    }
    searchBox->setPlaceholderText(cleaned_text);
    // Repopulate list
    updateRecipesDiplay("");
}

void Window::resizeEvent(QResizeEvent *event){
    int iconWidth, iconHeight, gridWidth, gridHeight;
    double gridRatio = 1.51;
    double iconRatio = 1.78;
    QSize recipeListSize = recipeList->size();

    // Set gridwith based on recipeList size and ensure it doesn't go below minimum value
    gridWidth = qMax(int(recipeListSize.width()) - 17.0, 212.0);
    // Calculate other parameters based on ratios of default values.
    gridHeight = int(gridWidth/gridRatio);
    iconWidth = gridWidth - 13;
    iconHeight = int(iconWidth/iconRatio);

    recipeList->setIconSize(QSize(iconWidth, iconHeight));
    recipeList->setGridSize(QSize(gridWidth, gridHeight));
    recipeList->setUniformItemSizes(true);
}

<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/message_loop.h"
#include "base/memory/scoped_ptr.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/controls/tabbed_pane/tabbed_pane.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

namespace views {

// A view for testing that takes a fixed preferred size upon construction.
class FixedSizeView : public View {
 public:
  explicit FixedSizeView(const gfx::Size& size)
    : size_(size) {}

  // Overridden from View:
  virtual gfx::Size GetPreferredSize() OVERRIDE {
    return size_;
  }

 private:
  const gfx::Size size_;

  DISALLOW_COPY_AND_ASSIGN(FixedSizeView);
};

class TabbedPaneTest : public ViewsTestBase {
 public:
  TabbedPaneTest() {}

  void TestSizeAndLayout(TabbedPane* tabbed_pane);

  void TestAddRemove(TabbedPane* tabbed_pane);

  TabbedPane* tabbed_pane_;  // Owned by the |widget_|'s root View.

#if defined(OS_WIN) && !defined(USE_AURA)
  TabbedPane* tabbed_pane_win_;  // Owned by the |widget_|'s root View.
#endif

 private:
  virtual void SetUp() OVERRIDE;

  scoped_ptr<Widget> widget_;

  DISALLOW_COPY_AND_ASSIGN(TabbedPaneTest);
};

void TabbedPaneTest::SetUp() {
  ViewsTestBase::SetUp();
  widget_.reset(new Widget());
  Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  params.bounds = gfx::Rect(0, 0, 100, 100);
  widget_->Init(params);
  tabbed_pane_ = new TabbedPane();
  // In order to properly initialize the |TabbedPane| it must be added to a
  // parent view (see the ViewHierarchyChanged method of the |TabbedPane|).
  widget_->GetRootView()->AddChildView(tabbed_pane_);

#if defined(OS_WIN) && !defined(USE_AURA)
  tabbed_pane_win_ = new TabbedPane();
  tabbed_pane_win_->set_use_native_win_control(true);
  widget_->GetRootView()->AddChildView(tabbed_pane_win_);
#endif
}

void TabbedPaneTest::TestSizeAndLayout(TabbedPane* tabbed_pane) {
  View* child1 = new FixedSizeView(gfx::Size(20, 10));
  tabbed_pane->AddTab(ASCIIToUTF16("tab1"), child1);
  View* child2 = new FixedSizeView(gfx::Size(5, 5));
  tabbed_pane->AddTab(ASCIIToUTF16("tab2"), child2);
  tabbed_pane->SelectTabAt(0);

  // The |tabbed_pane_| implementation of Views has no border by default.
  // Therefore it should be as wide as the widest tab. The native Windows
  // tabbed pane has a border that used up extra space. Therefore the preferred
  // width is larger than the largest child.
  gfx::Size pref(tabbed_pane->GetPreferredSize());
  EXPECT_GE(pref.width(), 20);
  EXPECT_GT(pref.height(), 10);

  // The bounds of our children should be smaller than the tabbed pane's bounds.
  tabbed_pane->SetBounds(0, 0, 100, 200);
  RunPendingMessages();
  gfx::Rect bounds(child1->bounds());
  EXPECT_GT(bounds.width(), 0);
  // The |tabbed_pane_| has no border. Therefore the children should be as wide
  // as the |tabbed_pane_|.
  EXPECT_LE(bounds.width(), 100);
  EXPECT_GT(bounds.height(), 0);
  EXPECT_LT(bounds.height(), 200);

  // If we switch to the other tab, it should get assigned the same bounds.
  tabbed_pane->SelectTabAt(1);
  EXPECT_EQ(bounds, child2->bounds());
}

void TabbedPaneTest::TestAddRemove(TabbedPane* tabbed_pane) {
  View* tab0 = new View;
  tabbed_pane->AddTab(ASCIIToUTF16("tab0"), tab0);
  EXPECT_EQ(tab0, tabbed_pane->GetSelectedTab());
  EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex());

  // Add more 3 tabs.
  tabbed_pane->AddTab(ASCIIToUTF16("tab1"), new View);
  tabbed_pane->AddTab(ASCIIToUTF16("tab2"), new View);
  tabbed_pane->AddTab(ASCIIToUTF16("tab3"), new View);
  EXPECT_EQ(4, tabbed_pane->GetTabCount());

  // Note: AddTab() doesn't select a tab if the tabbed pane isn't empty.

  // Select the last one.
  tabbed_pane->SelectTabAt(tabbed_pane->GetTabCount() - 1);
  EXPECT_EQ(3, tabbed_pane->GetSelectedTabIndex());

  // Remove the last one.
  delete tabbed_pane->RemoveTabAtIndex(3);
  EXPECT_EQ(3, tabbed_pane->GetTabCount());

  // After removing the last tab, check if the tabbed pane selected the previous
  // tab.
  EXPECT_EQ(2, tabbed_pane->GetSelectedTabIndex());

  tabbed_pane->AddTabAtIndex(0, ASCIIToUTF16("tab4"), new View, true);

  // Assert that even adding a new tab, the tabbed pane doesn't change the
  // selection, i.e., it doesn't select the new one.
  // The last tab should remains selected, instead of the tab added at index 0.
  EXPECT_EQ(3, tabbed_pane->GetSelectedTabIndex());

  // Now change the selected tab.
  tabbed_pane->SelectTabAt(1);
  EXPECT_EQ(1, tabbed_pane->GetSelectedTabIndex());

  // Remove the first one.
  delete tabbed_pane->RemoveTabAtIndex(0);
  EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex());
}

// Tests TabbedPane::GetPreferredSize() and TabbedPane::Layout().
TEST_F(TabbedPaneTest, SizeAndLayout) {
  TestSizeAndLayout(tabbed_pane_);
  // TODO(markusheintz): Once replacing NativeTabbedPaneWin with
  // NativeTabbedPaneView is completed (http://crbug.com/138059), then the
  // TestSizeAndLayout method should be inlined here again and the "ifdef" part
  // should be deleted.
#if defined(OS_WIN) && !defined(USE_AURA)
  TestSizeAndLayout(tabbed_pane_win_);
#endif
}

TEST_F(TabbedPaneTest, AddRemove) {
  TestAddRemove(tabbed_pane_);
  // TODO(markusheintz): Once replacing NativeTabbedPaneWin with
  // NativeTabbedPaneView is completed (http://crbug.com/138059), then the
  // TestAddRemove method should be inlined here again and the "ifdef" part
  // should be deleted.
#if defined(OS_WIN) && !defined(USE_AURA)
  TestAddRemove(tabbed_pane_win_);
#endif
}

} // namespace views
<commit_msg>Valgrind/Heapchecker: Fix leaks in TabbedPaneTest.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/message_loop.h"
#include "base/memory/scoped_ptr.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/controls/tabbed_pane/tabbed_pane.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

namespace views {

// A view for testing that takes a fixed preferred size upon construction.
class FixedSizeView : public View {
 public:
  explicit FixedSizeView(const gfx::Size& size)
    : size_(size) {}

  // Overridden from View:
  virtual gfx::Size GetPreferredSize() OVERRIDE {
    return size_;
  }

 private:
  const gfx::Size size_;

  DISALLOW_COPY_AND_ASSIGN(FixedSizeView);
};

class TabbedPaneTest : public ViewsTestBase {
 public:
  TabbedPaneTest() {}

  void TestSizeAndLayout(TabbedPane* tabbed_pane);

  void TestAddRemove(TabbedPane* tabbed_pane);

  TabbedPane* tabbed_pane_;  // Owned by the |widget_|'s root View.

#if defined(OS_WIN) && !defined(USE_AURA)
  TabbedPane* tabbed_pane_win_;  // Owned by the |widget_|'s root View.
#endif

 private:
  virtual void SetUp() OVERRIDE;

  scoped_ptr<Widget> widget_;

  DISALLOW_COPY_AND_ASSIGN(TabbedPaneTest);
};

void TabbedPaneTest::SetUp() {
  ViewsTestBase::SetUp();
  widget_.reset(new Widget());
  Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  params.bounds = gfx::Rect(0, 0, 100, 100);
  widget_->Init(params);
  tabbed_pane_ = new TabbedPane();
  // In order to properly initialize the |TabbedPane| it must be added to a
  // parent view (see the ViewHierarchyChanged method of the |TabbedPane|).
  widget_->GetRootView()->AddChildView(tabbed_pane_);

#if defined(OS_WIN) && !defined(USE_AURA)
  tabbed_pane_win_ = new TabbedPane();
  tabbed_pane_win_->set_use_native_win_control(true);
  widget_->GetRootView()->AddChildView(tabbed_pane_win_);
#endif
}

void TabbedPaneTest::TestSizeAndLayout(TabbedPane* tabbed_pane) {
  View* child1 = new FixedSizeView(gfx::Size(20, 10));
  tabbed_pane->AddTab(ASCIIToUTF16("tab1"), child1);
  View* child2 = new FixedSizeView(gfx::Size(5, 5));
  tabbed_pane->AddTab(ASCIIToUTF16("tab2"), child2);
  tabbed_pane->SelectTabAt(0);

  // The |tabbed_pane_| implementation of Views has no border by default.
  // Therefore it should be as wide as the widest tab. The native Windows
  // tabbed pane has a border that used up extra space. Therefore the preferred
  // width is larger than the largest child.
  gfx::Size pref(tabbed_pane->GetPreferredSize());
  EXPECT_GE(pref.width(), 20);
  EXPECT_GT(pref.height(), 10);

  // The bounds of our children should be smaller than the tabbed pane's bounds.
  tabbed_pane->SetBounds(0, 0, 100, 200);
  RunPendingMessages();
  gfx::Rect bounds(child1->bounds());
  EXPECT_GT(bounds.width(), 0);
  // The |tabbed_pane_| has no border. Therefore the children should be as wide
  // as the |tabbed_pane_|.
  EXPECT_LE(bounds.width(), 100);
  EXPECT_GT(bounds.height(), 0);
  EXPECT_LT(bounds.height(), 200);

  // If we switch to the other tab, it should get assigned the same bounds.
  tabbed_pane->SelectTabAt(1);
  EXPECT_EQ(bounds, child2->bounds());

  // Clean up.
  delete tabbed_pane->RemoveTabAtIndex(0);
  EXPECT_EQ(1, tabbed_pane->GetTabCount());
  delete tabbed_pane->RemoveTabAtIndex(0);
  EXPECT_EQ(0, tabbed_pane->GetTabCount());
}

void TabbedPaneTest::TestAddRemove(TabbedPane* tabbed_pane) {
  View* tab0 = new View;
  tabbed_pane->AddTab(ASCIIToUTF16("tab0"), tab0);
  EXPECT_EQ(tab0, tabbed_pane->GetSelectedTab());
  EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex());

  // Add more 3 tabs.
  tabbed_pane->AddTab(ASCIIToUTF16("tab1"), new View);
  tabbed_pane->AddTab(ASCIIToUTF16("tab2"), new View);
  tabbed_pane->AddTab(ASCIIToUTF16("tab3"), new View);
  EXPECT_EQ(4, tabbed_pane->GetTabCount());

  // Note: AddTab() doesn't select a tab if the tabbed pane isn't empty.

  // Select the last one.
  tabbed_pane->SelectTabAt(tabbed_pane->GetTabCount() - 1);
  EXPECT_EQ(3, tabbed_pane->GetSelectedTabIndex());

  // Remove the last one.
  delete tabbed_pane->RemoveTabAtIndex(3);
  EXPECT_EQ(3, tabbed_pane->GetTabCount());

  // After removing the last tab, check if the tabbed pane selected the previous
  // tab.
  EXPECT_EQ(2, tabbed_pane->GetSelectedTabIndex());

  tabbed_pane->AddTabAtIndex(0, ASCIIToUTF16("tab4"), new View, true);

  // Assert that even adding a new tab, the tabbed pane doesn't change the
  // selection, i.e., it doesn't select the new one.
  // The last tab should remains selected, instead of the tab added at index 0.
  EXPECT_EQ(3, tabbed_pane->GetSelectedTabIndex());

  // Now change the selected tab.
  tabbed_pane->SelectTabAt(1);
  EXPECT_EQ(1, tabbed_pane->GetSelectedTabIndex());

  // Remove the first one.
  delete tabbed_pane->RemoveTabAtIndex(0);
  EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex());

  // Clean up the other panes.
  EXPECT_EQ(3, tabbed_pane->GetTabCount());
  delete tabbed_pane->RemoveTabAtIndex(0);
  delete tabbed_pane->RemoveTabAtIndex(0);
  delete tabbed_pane->RemoveTabAtIndex(0);
  EXPECT_EQ(0, tabbed_pane->GetTabCount());
}

// Tests TabbedPane::GetPreferredSize() and TabbedPane::Layout().
TEST_F(TabbedPaneTest, SizeAndLayout) {
  TestSizeAndLayout(tabbed_pane_);
  // TODO(markusheintz): Once replacing NativeTabbedPaneWin with
  // NativeTabbedPaneView is completed (http://crbug.com/138059), then the
  // TestSizeAndLayout method should be inlined here again and the "ifdef" part
  // should be deleted.
#if defined(OS_WIN) && !defined(USE_AURA)
  TestSizeAndLayout(tabbed_pane_win_);
#endif
}

TEST_F(TabbedPaneTest, AddRemove) {
  TestAddRemove(tabbed_pane_);
  // TODO(markusheintz): Once replacing NativeTabbedPaneWin with
  // NativeTabbedPaneView is completed (http://crbug.com/138059), then the
  // TestAddRemove method should be inlined here again and the "ifdef" part
  // should be deleted.
#if defined(OS_WIN) && !defined(USE_AURA)
  TestAddRemove(tabbed_pane_win_);
#endif
}

}  // namespace views
<|endoftext|>