<commit_before>/*
 * Copyright 2002-2005 The Apache Software Foundation.
 *
 * 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.
 */

/*
 * XSEC
 *
 * DSIGSignedInfo := Class for checking and setting up signed Info nodes in a DSIG signature
 *
 * $Id$
 *
 */

// XSEC Includes
#include <xsec/dsig/DSIGSignedInfo.hpp>
#include <xsec/dsig/DSIGReference.hpp>
#include <xsec/framework/XSECError.hpp>
#include <xsec/framework/XSECEnv.hpp>
#include <xsec/utils/XSECDOMUtils.hpp>
#include <xsec/dsig/DSIGSignature.hpp>

#include <xercesc/util/Janitor.hpp>

XERCES_CPP_NAMESPACE_USE

// Constructors and Destructors

DSIGSignedInfo::DSIGSignedInfo(DOMDocument *doc,
		XSECSafeBufferFormatter * pFormatter,
		DOMNode *signedInfoNode, const XSECEnv * env) {

	mp_doc = doc;
	m_HMACOutputLength = 0;
	mp_formatter = pFormatter;
	mp_signedInfoNode = signedInfoNode;
	m_signatureMethod = SIGNATURE_NONE;
	mp_env = env;
	mp_referenceList = NULL;
	m_loaded = false;

}

DSIGSignedInfo::DSIGSignedInfo(DOMDocument *doc,
		XSECSafeBufferFormatter * pFormatter,
		const XSECEnv * env) {

	mp_doc = doc;
	m_HMACOutputLength = 0;
	mp_formatter = pFormatter;
	mp_signedInfoNode = NULL;
	m_signatureMethod = SIGNATURE_NONE;
	mp_env = env;
	mp_referenceList = NULL;
	m_loaded = false;

}

DSIGSignedInfo::~DSIGSignedInfo() {

	mp_formatter = NULL;
	mp_env = NULL;
	if (mp_referenceList != NULL) {

		delete mp_referenceList;
		mp_referenceList = NULL;

	}

}

signatureMethod DSIGSignedInfo::getSignatureMethod(void) {

	return m_signatureMethod;

}

DOMNode * DSIGSignedInfo::getDOMNode() {

	return mp_signedInfoNode;

}

canonicalizationMethod DSIGSignedInfo::getCanonicalizationMethod(void) {

	return m_canonicalizationMethod;

}

hashMethod DSIGSignedInfo::getHashMethod() {

	return m_hashMethod;

}

int DSIGSignedInfo::getHMACOutputLength() {

	return m_HMACOutputLength;

}

// --------------------------------------------------------------------------------
//           Verify each reference element
// --------------------------------------------------------------------------------

bool DSIGSignedInfo::verify(safeBuffer &errStr) {

	return DSIGReference::verifyReferenceList(mp_referenceList, errStr);

}

// --------------------------------------------------------------------------------
//           Calculate and set hash values for each reference element
// --------------------------------------------------------------------------------

void DSIGSignedInfo::hash(bool interlockingReferences) {

	DSIGReference::hashReferenceList(mp_referenceList, interlockingReferences);

}

// --------------------------------------------------------------------------------
//           Create an empty reference in the signed info
// --------------------------------------------------------------------------------

DSIGReference * DSIGSignedInfo::createReference(const XMLCh * URI,
								hashMethod hm,
								char * type) {

	safeBuffer hURI;

	if (hashMethod2URI(hURI, hm) == false) {
		throw XSECException(XSECException::UnknownSignatureAlgorithm,
			"DSIGSignedInfo::createReference - Hash method unknown");
	}

	return createReference(URI, hURI.sbStrToXMLCh(), MAKE_UNICODE_STRING(type));;
}

DSIGReference * DSIGSignedInfo::createReference(
		const XMLCh * URI,
		const XMLCh * hashAlgorithmURI,
		const XMLCh * type) {

	DSIGReference * ref;
	XSECnew(ref, DSIGReference(mp_env));
	Janitor<DSIGReference> j_ref(ref);

	DOMNode *refNode = ref->createBlankReference(URI, hashAlgorithmURI, type);

	// Add the node to the end of the childeren
	mp_signedInfoNode->appendChild(refNode);
	mp_env->doPrettyPrint(mp_signedInfoNode);

	// Add to the reference List
	j_ref.release();
	mp_referenceList->addReference(ref);

	return ref;
}

// --------------------------------------------------------------------------------
//           Create an empty SignedInfo
// --------------------------------------------------------------------------------

// deprecated

DOMElement *DSIGSignedInfo::createBlankSignedInfo(canonicalizationMethod cm,
			signatureMethod	sm,
			hashMethod hm) {

	// This is now deprecated.  Because this is so, we go the long way - translate
	// to URI and then call the "standard" method, which will translate back to
	// internal enums if possible

	const XMLCh * cURI;
	safeBuffer sURI;

	if ((cURI = canonicalizationMethod2UNICODEURI(cm)) == NULL) {
		throw XSECException(XSECException::UnknownCanonicalization,
			"DSIGSignature::createBlankSignature - Canonicalisation method unknown");
	}

	if (signatureHashMethod2URI(sURI, sm, hm) == false) {
		throw XSECException(XSECException::UnknownSignatureAlgorithm,
			"DSIGSignature::createBlankSignature - Signature/Hash method unknown");
	}

	return createBlankSignedInfo(cURI, sURI.sbStrToXMLCh());

}

DOMElement * DSIGSignedInfo::createBlankSignedInfo(
			const XMLCh * canonicalizationAlgorithmURI,
			const XMLCh * signatureAlgorithmURI) {

	safeBuffer str;
	const XMLCh * prefixNS = mp_env->getDSIGNSPrefix();

	makeQName(str, prefixNS, "SignedInfo");

	DOMElement *ret = mp_doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG,
								str.rawXMLChBuffer());

	mp_signedInfoNode = ret;

	// Now create the algorithm parts
	XSECmapURIToCanonicalizationMethod(canonicalizationAlgorithmURI, m_canonicalizationMethod);
	XSECmapURIToSignatureMethods(signatureAlgorithmURI, m_signatureMethod, m_hashMethod);

	// Canonicalisation

	DOMElement *canMeth = mp_doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG,
			makeQName(str, prefixNS, "CanonicalizationMethod").rawXMLChBuffer());

	mp_env->doPrettyPrint(mp_signedInfoNode);
	mp_signedInfoNode->appendChild(canMeth);
	mp_env->doPrettyPrint(mp_signedInfoNode);

	canMeth->setAttributeNS(NULL,DSIGConstants::s_unicodeStrAlgorithm,
		canonicalizationAlgorithmURI);

	// Now the SignatureMethod

	DOMElement *sigMeth = mp_doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG,
			makeQName(str, prefixNS, "SignatureMethod").rawXMLChBuffer());

	mp_signedInfoNode->appendChild(sigMeth);
	mp_env->doPrettyPrint(mp_signedInfoNode);

	sigMeth->setAttributeNS(NULL, DSIGConstants::s_unicodeStrAlgorithm,
		signatureAlgorithmURI);

	// Store the algorithm URI internally
	mp_algorithmURI = sigMeth->getAttributeNS(NULL, DSIGConstants::s_unicodeStrAlgorithm);

	// Create an empty reference list

	XSECnew(mp_referenceList, DSIGReferenceList());

	return ret;

}

// --------------------------------------------------------------------------------
//           Load the SignedInfo
// --------------------------------------------------------------------------------

void DSIGSignedInfo::load(void) {

	if (mp_signedInfoNode == 0) {

		// Attempt to load an empty signature element
		throw XSECException(XSECException::LoadEmptySignedInfo);

	}

	if (!strEquals(getDSIGLocalName(mp_signedInfoNode), "SignedInfo")) {

		throw XSECException(XSECException::LoadNonSignedInfo);

	}

	DOMNode * tmpSI = mp_signedInfoNode->getFirstChild();

	// Check for CanonicalizationMethod

	while (tmpSI != 0 && (tmpSI->getNodeType() != DOMNode::ELEMENT_NODE))
		// Skip text and comments
		tmpSI = tmpSI->getNextSibling();

	if (tmpSI == 0 || !strEquals(getDSIGLocalName(tmpSI), "CanonicalizationMethod")) {

		throw XSECException(XSECException::ExpectedDSIGChildNotFound,
				"Expected <CanonicalizationMethod> as first child of <SignedInfo>");

	}

	// Determine what the canonicalization method is
	DOMNamedNodeMap *tmpAtts = tmpSI->getAttributes();

	DOMNode *algorithm = tmpAtts->getNamedItem(DSIGConstants::s_unicodeStrAlgorithm);

	if (algorithm == 0) {

		throw XSECException(XSECException::ExpectedDSIGChildNotFound,
					"Expected Algorithm attribute in <CanonicalizationMethod>");

	}

	safeBuffer tmpSB;

	tmpSB << (*mp_formatter << algorithm->getNodeValue());

	if (tmpSB.sbStrcmp(URI_ID_C14N_NOC) == 0) {

		m_canonicalizationMethod = CANON_C14N_NOC;

	}

	else if (tmpSB.sbStrcmp(URI_ID_C14N_COM) == 0) {

		m_canonicalizationMethod = CANON_C14N_COM;

	}

	else if (tmpSB.sbStrcmp(URI_ID_EXC_C14N_COM) == 0) {

		m_canonicalizationMethod = CANON_C14NE_COM;

	}

	else if (tmpSB.sbStrcmp(URI_ID_EXC_C14N_NOC) == 0) {

		m_canonicalizationMethod = CANON_C14NE_NOC;

	}

	else

		throw XSECException(XSECException::UnknownCanonicalization);

	// Now load the SignatureMethod

	tmpSI = tmpSI->getNextSibling();

	while (tmpSI != 0 && (tmpSI->getNodeType() != DOMNode::ELEMENT_NODE))
		// Skip text and comments
		tmpSI = tmpSI->getNextSibling();

	if (tmpSI == 0 || !strEquals(getDSIGLocalName(tmpSI), "SignatureMethod")) {

		throw XSECException(XSECException::ExpectedDSIGChildNotFound,
				"Expected <SignatureMethod> as child of <SignedInfo>");
	}

	// Determine the algorithms used to sign this document

	tmpAtts = tmpSI->getAttributes();

	algorithm = tmpAtts->getNamedItem(DSIGConstants::s_unicodeStrAlgorithm);

	if (algorithm == 0) {

		throw XSECException(XSECException::ExpectedDSIGChildNotFound,
					"Expected Algorithm attribute in <SignatureMethod>");

	}

	mp_algorithmURI = algorithm->getNodeValue();

	/* NOTE - as of version 1.3.1 all code relating to parsing the algorithm
	 * has been removed.  This should all be handled inside the algorithm mappers.
	 * Having code here restricts available algorithms, as this code is not extended for
	 * new algorthms.
	 */

	/* Look for maximum output value. Really only applies to HMACs, but as we no
	 * longer know at this point if this is an HMAC, we need to check. */

	DOMNode *tmpSOV = tmpSI->getFirstChild();
	while (tmpSOV != NULL &&
		tmpSOV->getNodeType() != DOMNode::ELEMENT_NODE &&
		!strEquals(getDSIGLocalName(tmpSOV), "HMACOutputLength"))
		tmpSOV = tmpSOV->getNextSibling();

	if (tmpSOV != NULL) {

		// Have a max output value!
		tmpSOV = tmpSOV->getFirstChild();
		while (tmpSOV != NULL && tmpSOV->getNodeType() != DOMNode::TEXT_NODE)
			tmpSOV = tmpSOV->getNextSibling();

		if (tmpSOV != NULL) {

			safeBuffer val;
			val << (*mp_formatter << tmpSOV->getNodeValue());
			m_HMACOutputLength = atoi((char *) val.rawBuffer());

		}
	}

	// Now look at references....

	tmpSI = tmpSI->getNextSibling();

	// Run through the rest of the elements until done

	while (tmpSI != 0 && (tmpSI->getNodeType() != DOMNode::ELEMENT_NODE))
		// Skip text and comments
		tmpSI = tmpSI->getNextSibling();

	if (tmpSI != NULL) {

		// Have an element node - should be a reference, so let's load the list

		mp_referenceList = DSIGReference::loadReferenceListFromXML(mp_env, tmpSI);

	}

}

<commit_msg>Missed a c14n reference to handle 1.1<commit_after>/*
 * Copyright 2002-2005 The Apache Software Foundation.
 *
 * 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.
 */

/*
 * XSEC
 *
 * DSIGSignedInfo := Class for checking and setting up signed Info nodes in a DSIG signature
 *
 * $Id$
 *
 */

// XSEC Includes
#include <xsec/dsig/DSIGSignedInfo.hpp>
#include <xsec/dsig/DSIGReference.hpp>
#include <xsec/framework/XSECError.hpp>
#include <xsec/framework/XSECEnv.hpp>
#include <xsec/utils/XSECDOMUtils.hpp>
#include <xsec/dsig/DSIGSignature.hpp>

#include <xercesc/util/Janitor.hpp>

XERCES_CPP_NAMESPACE_USE

// Constructors and Destructors

DSIGSignedInfo::DSIGSignedInfo(DOMDocument *doc,
		XSECSafeBufferFormatter * pFormatter,
		DOMNode *signedInfoNode, const XSECEnv * env) {

	mp_doc = doc;
	m_HMACOutputLength = 0;
	mp_formatter = pFormatter;
	mp_signedInfoNode = signedInfoNode;
	m_signatureMethod = SIGNATURE_NONE;
	mp_env = env;
	mp_referenceList = NULL;
	m_loaded = false;

}

DSIGSignedInfo::DSIGSignedInfo(DOMDocument *doc,
		XSECSafeBufferFormatter * pFormatter,
		const XSECEnv * env) {

	mp_doc = doc;
	m_HMACOutputLength = 0;
	mp_formatter = pFormatter;
	mp_signedInfoNode = NULL;
	m_signatureMethod = SIGNATURE_NONE;
	mp_env = env;
	mp_referenceList = NULL;
	m_loaded = false;

}

DSIGSignedInfo::~DSIGSignedInfo() {

	mp_formatter = NULL;
	mp_env = NULL;
	if (mp_referenceList != NULL) {

		delete mp_referenceList;
		mp_referenceList = NULL;

	}

}

signatureMethod DSIGSignedInfo::getSignatureMethod(void) {

	return m_signatureMethod;

}

DOMNode * DSIGSignedInfo::getDOMNode() {

	return mp_signedInfoNode;

}

canonicalizationMethod DSIGSignedInfo::getCanonicalizationMethod(void) {

	return m_canonicalizationMethod;

}

hashMethod DSIGSignedInfo::getHashMethod() {

	return m_hashMethod;

}

int DSIGSignedInfo::getHMACOutputLength() {

	return m_HMACOutputLength;

}

// --------------------------------------------------------------------------------
//           Verify each reference element
// --------------------------------------------------------------------------------

bool DSIGSignedInfo::verify(safeBuffer &errStr) {

	return DSIGReference::verifyReferenceList(mp_referenceList, errStr);

}

// --------------------------------------------------------------------------------
//           Calculate and set hash values for each reference element
// --------------------------------------------------------------------------------

void DSIGSignedInfo::hash(bool interlockingReferences) {

	DSIGReference::hashReferenceList(mp_referenceList, interlockingReferences);

}

// --------------------------------------------------------------------------------
//           Create an empty reference in the signed info
// --------------------------------------------------------------------------------

DSIGReference * DSIGSignedInfo::createReference(const XMLCh * URI,
								hashMethod hm,
								char * type) {

	safeBuffer hURI;

	if (hashMethod2URI(hURI, hm) == false) {
		throw XSECException(XSECException::UnknownSignatureAlgorithm,
			"DSIGSignedInfo::createReference - Hash method unknown");
	}

	return createReference(URI, hURI.sbStrToXMLCh(), MAKE_UNICODE_STRING(type));;
}

DSIGReference * DSIGSignedInfo::createReference(
		const XMLCh * URI,
		const XMLCh * hashAlgorithmURI,
		const XMLCh * type) {

	DSIGReference * ref;
	XSECnew(ref, DSIGReference(mp_env));
	Janitor<DSIGReference> j_ref(ref);

	DOMNode *refNode = ref->createBlankReference(URI, hashAlgorithmURI, type);

	// Add the node to the end of the childeren
	mp_signedInfoNode->appendChild(refNode);
	mp_env->doPrettyPrint(mp_signedInfoNode);

	// Add to the reference List
	j_ref.release();
	mp_referenceList->addReference(ref);

	return ref;
}

// --------------------------------------------------------------------------------
//           Create an empty SignedInfo
// --------------------------------------------------------------------------------

// deprecated

DOMElement *DSIGSignedInfo::createBlankSignedInfo(canonicalizationMethod cm,
			signatureMethod	sm,
			hashMethod hm) {

	// This is now deprecated.  Because this is so, we go the long way - translate
	// to URI and then call the "standard" method, which will translate back to
	// internal enums if possible

	const XMLCh * cURI;
	safeBuffer sURI;

	if ((cURI = canonicalizationMethod2UNICODEURI(cm)) == NULL) {
		throw XSECException(XSECException::UnknownCanonicalization,
			"DSIGSignature::createBlankSignature - Canonicalisation method unknown");
	}

	if (signatureHashMethod2URI(sURI, sm, hm) == false) {
		throw XSECException(XSECException::UnknownSignatureAlgorithm,
			"DSIGSignature::createBlankSignature - Signature/Hash method unknown");
	}

	return createBlankSignedInfo(cURI, sURI.sbStrToXMLCh());

}

DOMElement * DSIGSignedInfo::createBlankSignedInfo(
			const XMLCh * canonicalizationAlgorithmURI,
			const XMLCh * signatureAlgorithmURI) {

	safeBuffer str;
	const XMLCh * prefixNS = mp_env->getDSIGNSPrefix();

	makeQName(str, prefixNS, "SignedInfo");

	DOMElement *ret = mp_doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG,
								str.rawXMLChBuffer());

	mp_signedInfoNode = ret;

	// Now create the algorithm parts
	XSECmapURIToCanonicalizationMethod(canonicalizationAlgorithmURI, m_canonicalizationMethod);
	XSECmapURIToSignatureMethods(signatureAlgorithmURI, m_signatureMethod, m_hashMethod);

	// Canonicalisation

	DOMElement *canMeth = mp_doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG,
			makeQName(str, prefixNS, "CanonicalizationMethod").rawXMLChBuffer());

	mp_env->doPrettyPrint(mp_signedInfoNode);
	mp_signedInfoNode->appendChild(canMeth);
	mp_env->doPrettyPrint(mp_signedInfoNode);

	canMeth->setAttributeNS(NULL,DSIGConstants::s_unicodeStrAlgorithm,
		canonicalizationAlgorithmURI);

	// Now the SignatureMethod

	DOMElement *sigMeth = mp_doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG,
			makeQName(str, prefixNS, "SignatureMethod").rawXMLChBuffer());

	mp_signedInfoNode->appendChild(sigMeth);
	mp_env->doPrettyPrint(mp_signedInfoNode);

	sigMeth->setAttributeNS(NULL, DSIGConstants::s_unicodeStrAlgorithm,
		signatureAlgorithmURI);

	// Store the algorithm URI internally
	mp_algorithmURI = sigMeth->getAttributeNS(NULL, DSIGConstants::s_unicodeStrAlgorithm);

	// Create an empty reference list

	XSECnew(mp_referenceList, DSIGReferenceList());

	return ret;

}

// --------------------------------------------------------------------------------
//           Load the SignedInfo
// --------------------------------------------------------------------------------

void DSIGSignedInfo::load(void) {

	if (mp_signedInfoNode == 0) {

		// Attempt to load an empty signature element
		throw XSECException(XSECException::LoadEmptySignedInfo);

	}

	if (!strEquals(getDSIGLocalName(mp_signedInfoNode), "SignedInfo")) {

		throw XSECException(XSECException::LoadNonSignedInfo);

	}

	DOMNode * tmpSI = mp_signedInfoNode->getFirstChild();

	// Check for CanonicalizationMethod

	while (tmpSI != 0 && (tmpSI->getNodeType() != DOMNode::ELEMENT_NODE))
		// Skip text and comments
		tmpSI = tmpSI->getNextSibling();

	if (tmpSI == 0 || !strEquals(getDSIGLocalName(tmpSI), "CanonicalizationMethod")) {

		throw XSECException(XSECException::ExpectedDSIGChildNotFound,
				"Expected <CanonicalizationMethod> as first child of <SignedInfo>");

	}

	// Determine what the canonicalization method is
	DOMNamedNodeMap *tmpAtts = tmpSI->getAttributes();

	DOMNode *algorithm = tmpAtts->getNamedItem(DSIGConstants::s_unicodeStrAlgorithm);

	if (algorithm == 0) {

		throw XSECException(XSECException::ExpectedDSIGChildNotFound,
					"Expected Algorithm attribute in <CanonicalizationMethod>");

	}

	safeBuffer tmpSB;

	tmpSB << (*mp_formatter << algorithm->getNodeValue());

	if (tmpSB.sbStrcmp(URI_ID_C14N_NOC) == 0) {

		m_canonicalizationMethod = CANON_C14N_NOC;

	}

	else if (tmpSB.sbStrcmp(URI_ID_C14N_COM) == 0) {

		m_canonicalizationMethod = CANON_C14N_COM;

	}

	else if (tmpSB.sbStrcmp(URI_ID_C14N11_NOC) == 0) {

        m_canonicalizationMethod = CANON_C14N11_NOC;

    }

    else if (tmpSB.sbStrcmp(URI_ID_C14N11_COM) == 0) {

        m_canonicalizationMethod = CANON_C14N11_COM;

    }

    else if (tmpSB.sbStrcmp(URI_ID_EXC_C14N_COM) == 0) {

		m_canonicalizationMethod = CANON_C14NE_COM;

	}

	else if (tmpSB.sbStrcmp(URI_ID_EXC_C14N_NOC) == 0) {

		m_canonicalizationMethod = CANON_C14NE_NOC;

	}

	else

		throw XSECException(XSECException::UnknownCanonicalization);

	// Now load the SignatureMethod

	tmpSI = tmpSI->getNextSibling();

	while (tmpSI != 0 && (tmpSI->getNodeType() != DOMNode::ELEMENT_NODE))
		// Skip text and comments
		tmpSI = tmpSI->getNextSibling();

	if (tmpSI == 0 || !strEquals(getDSIGLocalName(tmpSI), "SignatureMethod")) {

		throw XSECException(XSECException::ExpectedDSIGChildNotFound,
				"Expected <SignatureMethod> as child of <SignedInfo>");
	}

	// Determine the algorithms used to sign this document

	tmpAtts = tmpSI->getAttributes();

	algorithm = tmpAtts->getNamedItem(DSIGConstants::s_unicodeStrAlgorithm);

	if (algorithm == 0) {

		throw XSECException(XSECException::ExpectedDSIGChildNotFound,
					"Expected Algorithm attribute in <SignatureMethod>");

	}

	mp_algorithmURI = algorithm->getNodeValue();

	/* NOTE - as of version 1.3.1 all code relating to parsing the algorithm
	 * has been removed.  This should all be handled inside the algorithm mappers.
	 * Having code here restricts available algorithms, as this code is not extended for
	 * new algorthms.
	 */

	/* Look for maximum output value. Really only applies to HMACs, but as we no
	 * longer know at this point if this is an HMAC, we need to check. */

	DOMNode *tmpSOV = tmpSI->getFirstChild();
	while (tmpSOV != NULL &&
		tmpSOV->getNodeType() != DOMNode::ELEMENT_NODE &&
		!strEquals(getDSIGLocalName(tmpSOV), "HMACOutputLength"))
		tmpSOV = tmpSOV->getNextSibling();

	if (tmpSOV != NULL) {

		// Have a max output value!
		tmpSOV = tmpSOV->getFirstChild();
		while (tmpSOV != NULL && tmpSOV->getNodeType() != DOMNode::TEXT_NODE)
			tmpSOV = tmpSOV->getNextSibling();

		if (tmpSOV != NULL) {

			safeBuffer val;
			val << (*mp_formatter << tmpSOV->getNodeValue());
			m_HMACOutputLength = atoi((char *) val.rawBuffer());

		}
	}

	// Now look at references....

	tmpSI = tmpSI->getNextSibling();

	// Run through the rest of the elements until done

	while (tmpSI != 0 && (tmpSI->getNodeType() != DOMNode::ELEMENT_NODE))
		// Skip text and comments
		tmpSI = tmpSI->getNextSibling();

	if (tmpSI != NULL) {

		// Have an element node - should be a reference, so let's load the list

		mp_referenceList = DSIGReference::loadReferenceListFromXML(mp_env, tmpSI);

	}

}

<|endoftext|>


<commit_before>#pragma once
using namespace std;

#include <vector>
#include <sstream>

namespace OCScript
{
	// 便利な機能をまとめたクラスです。
	class Utility
	{
	public:

		// 区切り文字により文字列をスプリットします。
		// 引数: ソース文字列, 区切り文字
		static vector<wstring> StrSplit(const wstring &src, wchar_t delimiter)
		{
			wstringstream ss(src);
			vector<wstring> items;
			wstring item;

			while (getline(ss, item, delimiter))
				items.push_back(item);

			return items;
		}

		// 区切り文字により文字列を連結します。
		// 引数: ソース文字列, 区切り文字
		static wstring StrJoin(const vector<wstring> &src, wstring delimiter)
		{
			wstring dest;

			for (auto it = src.begin(); it != src.end(); it++)
			{
				dest += *it;

				if (it != src.end() - 1)
					dest += delimiter;
			}

			return dest;
		}

		// 文字列を置換して新しい文字列として返します。
		// 引数: ソース文字列, 対象の文字列, 置換後の文字列
		static wstring StrReplace(const wstring src, const wstring oldVal, const wstring newVal)
		{
			auto temp = src;
			wstring::size_type pos(temp.find(oldVal));

			while (pos != wstring::npos)
			{
				temp.replace(pos, oldVal.length(), newVal);
				pos = temp.find(oldVal, pos + newVal.length());
			}

			return temp;
		}
	};
}
<commit_msg>fix indent<commit_after>#pragma once
using namespace std;

#include <vector>
#include <sstream>

namespace OCScript
{
	// 便利な機能をまとめたクラスです。
	class Utility
	{
	public:

		// 区切り文字により文字列をスプリットします。
		// 引数: ソース文字列, 区切り文字
		static vector<wstring> StrSplit(const wstring &src, wchar_t delimiter)
		{
			wstringstream ss(src);
			vector<wstring> items;
			wstring item;

			while (getline(ss, item, delimiter))
				items.push_back(item);

			return items;
		}

		// 区切り文字により文字列を連結します。
		// 引数: ソース文字列, 区切り文字
		static wstring StrJoin(const vector<wstring> &src, wstring delimiter)
		{
			wstring dest;

			for (auto it = src.begin(); it != src.end(); it++)
			{
				dest += *it;

				if (it != src.end() - 1)
					dest += delimiter;
			}

			return dest;
		}

		// 文字列を置換して新しい文字列として返します。
		// 引数: ソース文字列, 対象の文字列, 置換後の文字列
		static wstring StrReplace(const wstring src, const wstring oldVal, const wstring newVal)
		{
			auto temp = src;
			wstring::size_type pos(temp.find(oldVal));

			while (pos != wstring::npos)
			{
				temp.replace(pos, oldVal.length(), newVal);
				pos = temp.find(oldVal, pos + newVal.length());
			}

			return temp;
		}
	};
}
<|endoftext|>


<commit_before>/*
 * opencog/execution/EvaluationLink.cc
 *
 * Copyright (C) 2009, 2013, 2014 Linas Vepstas
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program 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 Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atomspace/atom_types.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/cython/PythonEval.h>
#include <opencog/guile/SchemeEval.h>
#include "EvaluationLink.h"

using namespace opencog;

EvaluationLink::EvaluationLink(const HandleSeq& oset,
                             TruthValuePtr tv,
                             AttentionValuePtr av)
    : Link(EVALUATION_LINK, oset, tv, av)
{
    if ((2 != oset.size()) or
       (LIST_LINK != oset[1]->getType()))
    {
        throw RuntimeException(TRACE_INFO,
            "EvaluationLink must have predicate and args!");
    }
}

EvaluationLink::EvaluationLink(Handle schema, Handle args,
                             TruthValuePtr tv,
                             AttentionValuePtr av)
    : Link(EVALUATION_LINK, schema, args, tv, av)
{
    if (LIST_LINK != args->getType()) {
        throw RuntimeException(TRACE_INFO,
            "EvaluationLink must have args in a ListLink!");
    }
}

/// do_evaluate -- evaluate the GroundedPredicateNode of the EvaluationLink
///
/// Expects the argument to be an EvaluationLink, which should have the
/// following structure:
///
///     EvaluationLink
///         GroundedPredicateNode "lang: func_name"
///         ListLink
///             SomeAtom
///             OtherAtom
///
/// The "lang:" should be either "scm:" for scheme, or "py:" for python.
/// This method will then invoke "func_name" on the provided ListLink
/// of arguments to the function.
///
TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, Handle execlnk)
{
    if (EVALUATION_LINK != execlnk->getType()) {
        throw RuntimeException(TRACE_INFO, "Expecting to get an EvaluationLink!");
    }
    LinkPtr l(LinkCast(execlnk));
    return do_evaluate(as, l->getOutgoingSet());
}

/// do_evaluate -- evaluate the GroundedPredicateNode of the EvaluationLink
///
/// Expects the sequence to be exactly two atoms long.
/// Expects the first handle of the sequence to be a GroundedPredicateNode
/// Expects the second handle of the sequence to be a ListLink
/// Executes the GroundedPredicateNode, supplying the second handle as argument
///
TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, const HandleSeq& sna)
{
    if (2 != sna.size())
    {
        throw RuntimeException(TRACE_INFO, "Incorrect arity for an EvaluationLink!");
    }
    return do_evaluate(as, sna[0], sna[1]);
}

/// do_evaluate -- evaluate the GroundedPredicateNode of the EvaluationLink
///
/// Expects "gsn" to be a GroundedPredicateNode
/// Expects "args" to be a ListLink
/// Executes the GroundedPredicateNode, supplying the args as argument
///
TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, Handle gsn, Handle args)
{
    if (GROUNDED_PREDICATE_NODE != gsn->getType())
    {
        throw RuntimeException(TRACE_INFO, "Expecting GroundedPredicateNode!");
    }
    if (LIST_LINK != args->getType())
    {
        throw RuntimeException(TRACE_INFO, "Expecting arguments to EvaluationLink!");
    }

    // Get the schema name.
    const std::string& schema = NodeCast(gsn)->getName();
    // printf ("Grounded schema name: %s\n", schema.c_str());

    // A very special-case C++ comparison.
    // This compares two NumberNodes, by their numeric value.
    // Hard-coded in C++ for speed. (well, and for convenience ...)
    if (0 == schema.compare("c++:greater"))
    {
        LinkPtr ll(LinkCast(args));
        Handle h1(ll->getOutgoingAtom(0));
        Handle h2(ll->getOutgoingAtom(1));
        if (NUMBER_NODE != h1->getType() or NUMBER_NODE != h2->getType())
            throw RuntimeException(TRACE_INFO,
                "Expecting c++:greater arguments to be NumberNode's!");
        const std::string& s1 = NodeCast(h1)->getName();
        const std::string& s2 = NodeCast(h2)->getName();
        double v1 = atof(s1.c_str());
        double v2 = atof(s2.c_str());
        if (v1 > v2)
            return TruthValue::TRUE_TV();
        else
            return TruthValue::FALSE_TV();
    }

    // A very special-case C++ comparison.
    // This compares a set of atoms, verifying that they are all different.
    // Hard-coded in C++ for speed. (well, and for convenience ...)
    if (0 == schema.compare("c++:exclusive"))
    {
        LinkPtr ll(LinkCast(args));
        Arity sz = ll->getArity();
        for (Arity i=0; i<sz-1; i++) {
            Handle h1(ll->getOutgoingAtom(i));
            for (Arity j=i+1; j<sz; j++) {
                Handle h2(ll->getOutgoingAtom(j));
                if (h1 == h2) return TruthValue::FALSE_TV();
            }
        }
        return TruthValue::TRUE_TV();
    }

    // At this point, we only run scheme and python schemas.
    if (0 == schema.compare(0,4,"scm:", 4))
    {
#ifdef HAVE_GUILE
        // Be friendly, and strip leading white-space, if any.
        size_t pos = 4;
        while (' ' == schema[pos]) pos++;

        SchemeEval* applier = get_evaluator(as);
        return applier->apply_tv(schema.substr(pos), args);
#else
        throw RuntimeException(TRACE_INFO,
             "Cannot evaluate scheme GroundedPredicateNode!");
#endif /* HAVE_GUILE */
    }

    if (0 == schema.compare(0, 3,"py:", 3))
    {
#ifdef HAVE_CYTHON
        // Be friendly, and strip leading white-space, if any.
        size_t pos = 3;
        while (' ' == schema[pos]) pos++;

        PythonEval &applier = PythonEval::instance();
        // std::string rc = applier.apply(schema.substr(pos), args);
        // if (rc.compare("None") or rc.compare("False")) return false;
        return applier.apply_tv(schema.substr(pos), args);
#else
        throw RuntimeException(TRACE_INFO,
             "Cannot evaluate python GroundedPredicateNode!");
#endif /* HAVE_CYTHON */
    }

    // Unkown proceedure type.
    throw RuntimeException(TRACE_INFO,
        "Cannot evaluate unknown GroundedPredicateNode!");
}
<commit_msg>Nothing but a white-space change<commit_after>/*
 * opencog/execution/EvaluationLink.cc
 *
 * Copyright (C) 2009, 2013, 2014 Linas Vepstas
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program 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 Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atomspace/atom_types.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/cython/PythonEval.h>
#include <opencog/guile/SchemeEval.h>
#include "EvaluationLink.h"

using namespace opencog;

EvaluationLink::EvaluationLink(const HandleSeq& oset,
                               TruthValuePtr tv,
                               AttentionValuePtr av)
    : Link(EVALUATION_LINK, oset, tv, av)
{
	if ((2 != oset.size()) or
	   (LIST_LINK != oset[1]->getType()))
	{
		throw RuntimeException(TRACE_INFO,
		    "EvaluationLink must have predicate and args!");
	}
}

EvaluationLink::EvaluationLink(Handle schema, Handle args,
                               TruthValuePtr tv,
                               AttentionValuePtr av)
    : Link(EVALUATION_LINK, schema, args, tv, av)
{
	if (LIST_LINK != args->getType()) {
		throw RuntimeException(TRACE_INFO,
		    "EvaluationLink must have args in a ListLink!");
	}
}

/// do_evaluate -- evaluate the GroundedPredicateNode of the EvaluationLink
///
/// Expects the argument to be an EvaluationLink, which should have the
/// following structure:
///
///     EvaluationLink
///         GroundedPredicateNode "lang: func_name"
///         ListLink
///             SomeAtom
///             OtherAtom
///
/// The "lang:" should be either "scm:" for scheme, or "py:" for python.
/// This method will then invoke "func_name" on the provided ListLink
/// of arguments to the function.
///
TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, Handle execlnk)
{
	if (EVALUATION_LINK != execlnk->getType()) {
		throw RuntimeException(TRACE_INFO, "Expecting to get an EvaluationLink!");
	}
	LinkPtr l(LinkCast(execlnk));
	return do_evaluate(as, l->getOutgoingSet());
}

/// do_evaluate -- evaluate the GroundedPredicateNode of the EvaluationLink
///
/// Expects the sequence to be exactly two atoms long.
/// Expects the first handle of the sequence to be a GroundedPredicateNode
/// Expects the second handle of the sequence to be a ListLink
/// Executes the GroundedPredicateNode, supplying the second handle as argument
///
TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, const HandleSeq& sna)
{
	if (2 != sna.size())
	{
		throw RuntimeException(TRACE_INFO, "Incorrect arity for an EvaluationLink!");
	}
	return do_evaluate(as, sna[0], sna[1]);
}

/// do_evaluate -- evaluate the GroundedPredicateNode of the EvaluationLink
///
/// Expects "gsn" to be a GroundedPredicateNode
/// Expects "args" to be a ListLink
/// Executes the GroundedPredicateNode, supplying the args as argument
///
TruthValuePtr EvaluationLink::do_evaluate(AtomSpace* as, Handle gsn, Handle args)
{
	if (GROUNDED_PREDICATE_NODE != gsn->getType())
	{
		throw RuntimeException(TRACE_INFO, "Expecting GroundedPredicateNode!");
	}
	if (LIST_LINK != args->getType())
	{
		throw RuntimeException(TRACE_INFO, "Expecting arguments to EvaluationLink!");
	}

	// Get the schema name.
	const std::string& schema = NodeCast(gsn)->getName();
	// printf ("Grounded schema name: %s\n", schema.c_str());

	// A very special-case C++ comparison.
	// This compares two NumberNodes, by their numeric value.
	// Hard-coded in C++ for speed. (well, and for convenience ...)
	if (0 == schema.compare("c++:greater"))
	{
		LinkPtr ll(LinkCast(args));
		Handle h1(ll->getOutgoingAtom(0));
		Handle h2(ll->getOutgoingAtom(1));
		if (NUMBER_NODE != h1->getType() or NUMBER_NODE != h2->getType())
			throw RuntimeException(TRACE_INFO,
			     "Expecting c++:greater arguments to be NumberNode's!");
		const std::string& s1 = NodeCast(h1)->getName();
		const std::string& s2 = NodeCast(h2)->getName();
		double v1 = atof(s1.c_str());
		double v2 = atof(s2.c_str());
		if (v1 > v2)
			return TruthValue::TRUE_TV();
		else
			return TruthValue::FALSE_TV();
	}

	// A very special-case C++ comparison.
	// This compares a set of atoms, verifying that they are all different.
	// Hard-coded in C++ for speed. (well, and for convenience ...)
	if (0 == schema.compare("c++:exclusive"))
	{
		LinkPtr ll(LinkCast(args));
		Arity sz = ll->getArity();
		for (Arity i=0; i<sz-1; i++) {
			Handle h1(ll->getOutgoingAtom(i));
			for (Arity j=i+1; j<sz; j++) {
				Handle h2(ll->getOutgoingAtom(j));
				if (h1 == h2) return TruthValue::FALSE_TV();
			}
		}
		return TruthValue::TRUE_TV();
	}

	// At this point, we only run scheme and python schemas.
	if (0 == schema.compare(0,4,"scm:", 4))
	{
#ifdef HAVE_GUILE
		// Be friendly, and strip leading white-space, if any.
		size_t pos = 4;
		while (' ' == schema[pos]) pos++;

		SchemeEval* applier = get_evaluator(as);
		return applier->apply_tv(schema.substr(pos), args);
#else
		throw RuntimeException(TRACE_INFO,
			 "Cannot evaluate scheme GroundedPredicateNode!");
#endif /* HAVE_GUILE */
	}

	if (0 == schema.compare(0, 3,"py:", 3))
	{
#ifdef HAVE_CYTHON
		// Be friendly, and strip leading white-space, if any.
		size_t pos = 3;
		while (' ' == schema[pos]) pos++;

		PythonEval &applier = PythonEval::instance();
		// std::string rc = applier.apply(schema.substr(pos), args);
		// if (rc.compare("None") or rc.compare("False")) return false;
		return applier.apply_tv(schema.substr(pos), args);
#else
		throw RuntimeException(TRACE_INFO,
			 "Cannot evaluate python GroundedPredicateNode!");
#endif /* HAVE_CYTHON */
	}

	// Unkown proceedure type.
	throw RuntimeException(TRACE_INFO,
	     "Cannot evaluate unknown GroundedPredicateNode!");
}
<|endoftext|>


<commit_before>#include <bytecode/smtl/SmtlCpu.h>
#include <stdio.h>

SmtlCpu::SmtlCpu()
{
	isExecuting=true;
	pc=0;
}
int SmtlCpu::doOp(char opcode)
{
    processOpcode(opcode);
}
int SmtlCpu::execute(char opcode[],int num)
{
	int i=0;
	while(i!=num)
	{
		processOpcode(opcode[i]);
	}
}

void SmtlCpu::step()
{
    processOpcode(memory[pc]);
}

void SmtlCpu::processOpcode(char opcode)
{
    switch(opcode)
    {
    	case 0x0: //HALT
    		printf("hlt\n");
    		isExecuting=false;
    		break;
    	case 0x1: //SET MEMORY
    		printf("set 0x%X -> 0x%X\n",memory[pc+1],memory[pc+2]);
    		memory[memory[pc+1]]=memory[pc+2];
    		pc=pc+3;
    		break;
    	case 0x2: //JMP
			printf("jmp 0x%X\n",memory[pc+1]);
			pc=memory[pc+1];
			break;
    	default:
    		printf("???\n");
    		pc++;
	}
}
<commit_msg>Update SmtlCpu.cpp<commit_after>#include <bytecode/smtl/SmtlCpu.h>
#include <stdio.h>

SmtlCpu::SmtlCpu()
{
	isExecuting=true;
	pc=0;
}
int SmtlCpu::doOp(char opcode)
{
    processOpcode(opcode);
}
int SmtlCpu::execute(char opcode[],int num)
{
	int i=0;
	while(i!=num)
	{
		processOpcode(opcode[i]);
		i++;
	}
}

void SmtlCpu::step()
{
    processOpcode(memory[pc]);
}

void SmtlCpu::processOpcode(char opcode)
{
    switch(opcode)
    {
    	case 0x0: //HALT
    		printf("hlt\n");
    		isExecuting=false;
    		break;
    	case 0x1: //SET MEMORY
    		printf("set 0x%X -> 0x%X\n",memory[pc+1],memory[pc+2]);
    		memory[memory[pc+1]]=memory[pc+2];
    		pc=pc+3;
    		break;
    	case 0x2: //JMP
			printf("jmp 0x%X\n",memory[pc+1]);
			pc=memory[pc+1];
			break;
    	default:
    		printf("???\n");
    		pc++;
	}
}
<|endoftext|>


<commit_before>#include "acmacs-base/html.hh"
#include "acmacs-map-draw/mapi-settings.hh"

// ----------------------------------------------------------------------

// {"N": "compare-sequences", "groups": [{"select": <Select Antigens>, "name": "group-1"} ...], "html": "filename.html (filename.data.js is generated as well)", "json": "filename.json", "report": true, "open": true},

struct group_t
{
    std::string name;
    acmacs::chart::PointIndexList antigens;

    group_t(std::string_view a_name, acmacs::chart::PointIndexList&& a_antigens) : name{a_name}, antigens{std::move(a_antigens)} {}
};

bool acmacs::mapi::v1::Settings::apply_compare_sequences()
{
    using namespace std::string_view_literals;

    std::vector<group_t> groups;
    if (const auto& groups_v = getenv("groups"sv); groups_v.is_array()) {
        for (const auto& grp : groups_v.array()) {
            try {
                groups.emplace_back(grp["name"sv].to<std::string_view>(), select_antigens(grp["select"sv]));
            }
            catch (std::exception& err) {
                throw acmacs::mapi::unrecognized{AD_FORMAT("unrecognized \"groups\" entry: {}: {}", grp, err)};
            }
        }
    }
    else
        throw acmacs::mapi::unrecognized{AD_FORMAT("unrecognized or absent \"groups\" clause: {} (array of objects expected)", groups_v)};

    acmacs::html::Generator html;
    html.title("Compare sequences"sv);
    fmt::print("{}\n", html.generate());

    return true;

}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>mapi: compare-sequences<commit_after>#include "acmacs-base/html.hh"
#include "seqdb-3/compare.hh"
#include "acmacs-map-draw/mapi-settings.hh"
#include "acmacs-map-draw/draw.hh"

// ----------------------------------------------------------------------

// {"N": "compare-sequences", "groups": [{"select": <Select Antigens>, "name": "group-1"} ...], "html": "filename.html (filename.data.js is generated as well)", "json": "filename.json", "report": true, "open": true},

struct group_t
{
    std::string name;
    acmacs::chart::PointIndexList antigens;

    group_t(std::string_view a_name, acmacs::chart::PointIndexList&& a_antigens) : name{a_name}, antigens{std::move(a_antigens)} {}
};

bool acmacs::mapi::v1::Settings::apply_compare_sequences()
{
    using namespace std::string_view_literals;

    std::vector<group_t> groups;
    if (const auto& groups_v = getenv("groups"sv); groups_v.is_array()) {
        for (const auto& grp : groups_v.array()) {
            try {
                groups.emplace_back(grp["name"sv].to<std::string_view>(), select_antigens(grp["select"sv]));
            }
            catch (std::exception& err) {
                throw acmacs::mapi::unrecognized{AD_FORMAT("unrecognized \"groups\" entry: {}: {}", grp, err)};
            }
        }
    }
    else
        throw acmacs::mapi::unrecognized{AD_FORMAT("unrecognized or absent \"groups\" clause: {} (array of objects expected)", groups_v)};

    const auto& matched_seqdb = chart_draw().chart(0).match_seqdb();
    const auto nuc_aa{acmacs::seqdb::compare::aa};
    acmacs::seqdb::subsets_to_compare_t subsets_to_compare{nuc_aa};
    for (const auto& group_desc : groups) {
        auto& subset = subsets_to_compare.subsets.emplace_back(group_desc.name).subset;
        for (const auto ind : group_desc.antigens) {
            if (matched_seqdb[ind])
                subset.append(matched_seqdb[ind]);
        }
    }
    subsets_to_compare.make_counters();
    fmt::print("{}\n", subsets_to_compare.format_json(2));

    acmacs::html::Generator html;
    html.title("Compare sequences"sv);
    fmt::print("{}\n", html.generate());

    return true;
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>/*  Copyright (C) 2009  Adenilson Cavalcanti <savagobr@yahoo.com>
 *
 *  This program 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; by version 2 of the License or (at your
 *  choice) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/* TODO:
 * - test add new event
 * - implement and test edit event
 * - support for recurrent events (it will require changes on libgcal)
 */

#include "gcalresource.h"
#include "settings.h"
#include "settingsadaptor.h"

#include <QtDBus/QDBusConnection>
#include <kabc/addressee.h>
#include <kabc/phonenumber.h>
#include <kabc/key.h>
#include <kabc/errorhandler.h>
#include <kcal/event.h>
#include <kdatetime.h>
#include <qstring.h>
#include <KWindowSystem>
#include <akonadi/changerecorder.h>
#include <akonadi/itemfetchscope.h>
#include <KUrl>
#include <kdatetime.h>
#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<KCal::Incidence> IncidencePtr;

extern "C" {
#include <gcalendar.h>
#include <gcal_status.h>
}

using namespace Akonadi;

GCalResource::GCalResource( const QString &id )
	: ResourceBase(id)
{
	new SettingsAdaptor( Settings::self() );
	QDBusConnection::sessionBus().registerObject(
		QLatin1String( "/Settings" ), Settings::self(),
		QDBusConnection::ExportAdaptors );

	changeRecorder()->itemFetchScope().fetchFullPayload();

	if (!(gcal = gcal_new(GCALENDAR)))
		exit(1);
	gcal_set_store_xml(gcal, 1);
	all_events.length = 0;
	all_events.entries = NULL;
}

GCalResource::~GCalResource()
{
	gcal_cleanup_events(&all_events);
	pending.clear();
	deleted.clear();
}

void GCalResource::retrieveTimestamp(QString &timestamp)
{
	timestamp = Settings::self()->timestamp();
}

void GCalResource::saveTimestamp(QString &timestamp)
{
	Settings::self()->setTimestamp(timestamp);
	Settings::self()->writeConfig();
}

void GCalResource::retrieveCollections()
{
    if (!authenticated) {
	    kError() << "No authentication for Google Calendar available";
	    const QString message = i18nc("@info: status",
					  "Not yet authenticated for "
					  "use of Google Calendar");

	    emit error(message);

	    emit status(Broken, message);
	    return;
    }

    Collection c;
    c.setParent(Collection::root());
    c.setRemoteId("google-calendar");
    c.setName(name());

    QStringList mimeTypes;
    mimeTypes << "text/calendar";
    c.setContentMimeTypes(mimeTypes);

    Collection::List list;
    list << c;
    collectionsRetrieved(list);
}

void GCalResource::retrieveItems( const Akonadi::Collection &collection )
{
	Q_UNUSED(collection);
	Item::List items;
	int result;
	gcal_event_t event;
	QString timestamp;
	QByteArray t_byte;

	kError() << "\n............. retrieveItems ...........\n";
	if (!authenticated) {
		kError() << "No authentication for Google calendar available";
		const QString message = i18nc("@info:status",
					      "Not yet authenticated for"
					      " use of Google calendar");
		emit error(message);
		emit status(Broken, message);
		return;
	}

	/* Query by updated */
// 	retrieveTimestamp(timestamp);
// 	t_byte = timestamp.toLocal8Bit();
// 	if (t_byte.length() > TIMESTAMP_SIZE) {
// 		//TODO: implement getUpdated
// 		//result = getUpdated(t_byte.data());
// 		return;
// 	}
	kError() << "First retrieve";

	if ((result = gcal_get_events(gcal, &all_events)))
		exit(1);

	/* GCalendar returns last updated entry as first element */
	event = gcal_event_element(&all_events, 0);
	if (!event) {
		kError() << "Failed to retrieve last updated event.";
		const QString message = i18nc("@info:status",
					      "Failed getting last updated"
					      " event");
		emit error(message);
		emit status(Broken, message);
		return;

	}

	timestamp = gcal_event_get_updated(event);
	saveTimestamp(timestamp);

	/* Each google entry has a unique ID and edit_url */
	for (size_t i = 0; i < all_events.length; ++i) {
		Item item(QLatin1String("text/calendar"));
		KCal::Event *kevent;
		kevent = new KCal::Event;
		QString temp;
		event = gcal_event_element(&all_events, i);

		temp = gcal_event_get_title(event);
		kevent->setSummary(temp);

		temp = gcal_event_get_where(event);
		kevent->setLocation(temp);

		KCal::Incidence::Status status;
		if (gcal_event_is_deleted(event))
			status = KCal::Incidence::StatusCanceled;
		else
			status = KCal::Incidence::StatusConfirmed;
		kevent->setStatus(status);

		temp = gcal_event_get_content(event);
		kevent->setDescription(temp);

		KDateTime start, end;
		temp = gcal_event_get_start(event);
		start = start.fromString(temp, KDateTime::ISODate);
		temp = gcal_event_get_end(event);
		end = end.fromString(temp, KDateTime::ISODate);
		kevent->setDtStart(start);
		kevent->setDtEnd(end);

		qDebug() << "start: " << start.dateTime()
			 << "\tend: " << end.dateTime();

		/* remoteID: edit_url */
		KUrl urlEtag(gcal_event_get_url(event));
		item.setRemoteId(urlEtag.url());
		item.setPayload(IncidencePtr(kevent));

		items << item;
	}

	itemsRetrieved(items);
	kError() << "\n............. done retrieveItems! ...........\n";
}

bool GCalResource::retrieveItem( const Akonadi::Item &item, const QSet<QByteArray> &parts )
{
	Q_UNUSED(parts);
	Q_UNUSED(item);
	return true;
}

void GCalResource::aboutToQuit()
{
	// TODO: any cleanup you need to do while there is still an active
	// event loop. The resource will terminate after this method returns
}

void GCalResource::doSetOnline(bool online)
{
	Q_UNUSED(online);
}

int GCalResource::getUpdated(char *timestamp)
{
	Q_UNUSED(timestamp);

	return -1;
}

void GCalResource::configure( WId windowId )
{
	int result = 0;
	if (windowId && dlgConf)
		KWindowSystem::setMainWindow(dlgConf, windowId);

	dlgConf->exec();
	if (!(result = authenticate(dlgConf->eAccount->text(),
				    dlgConf->ePass->text()))) {
		result = saveToWallet(dlgConf->eAccount->text(),
				      dlgConf->ePass->text(),
				      windowId);

		synchronize();
	}

	if (result) {
		kError() << "Failed configuring resource.";
		const QString message = i18nc("@info:status",
					      "Invalid password.");
		emit error(message);
		emit status(Broken, message);
		return;
	}
}

void GCalResource::itemAdded( const Akonadi::Item &item, const Akonadi::Collection &collection )
{
	Q_UNUSED(collection);

    gcal_event_t event;
    KCal::Event kevent;
    QByteArray t_byte;
    QString temp;

    if (!authenticated) {
        kError() << "No authentication for Google calendar available";
        const QString message = i18nc("@info:status",
                          "Not yet authenticated for"
                          " use of Google calendar");
        emit error(message);
        emit status(Broken, message);
        return;
    }

    if(item.hasPayload<KCal::Event>())
        kevent = item.payload<KCal::Event>();

    if (!(event = gcal_event_new(NULL))) {
        kError() << "Memory allocation error!";
        const QString message = i18nc("@info:status",
                      "Failed to create gcal_event");
        emit error(message);
        emit status(Broken, message);
        return;
    }

    temp = kevent.summary();
    if(!temp.length()) {
        t_byte = temp.toLocal8Bit();
        gcal_event_set_title(event, t_byte);
    }

    temp = kevent.description();
    if(!temp.length()) {
        t_byte = temp.toLocal8Bit();
        gcal_event_set_content(event, t_byte);
    }
    //TODO: event start, event end, status

    temp = kevent.location();
    if(!temp.length()) {
        t_byte = temp.toLocal8Bit();
        gcal_event_set_where(event, t_byte);
    }

    if ((gcal_add_event(gcal, event))) {
        kError() << "Failed adding new calendar"
                << "title:" << kevent.summary();
        const QString message = i18nc("@info:status",
                            "failed adding new calendar");
        emit error(message);
        emit status(Broken, message);
    }

    KUrl url(gcal_event_get_url(event));
    Item newItem(item);
    newItem.setPayload<KCal::Event>(kevent);
    newItem.setRemoteId(url.url());
    changeCommitted(newItem);

    gcal_event_delete(event);
}

void GCalResource::itemChanged( const Akonadi::Item &item, const QSet<QByteArray> &parts )
{
	Q_UNUSED(parts);
	Q_UNUSED(item);
}

void GCalResource::itemRemoved( const Akonadi::Item &item )
{
	Q_UNUSED(item);

    gcal_event_t event;
    QString temp;
    QByteArray t_byte;

    kDebug() << "Deleting one item ... ";

    if(!authenticated) {
        kError() << "No authentication for Google calendar";
        const QString message = i18nc("@info:status",
                                "Not yet authenticated for "
                                "use of Google calendar");
        emit error(message);
        emit status(Broken, message);
    }

    if (!(event = gcal_event_new(NULL))) {
        kError() << "Memory allocation error!";
        const QString message = i18nc("@info:status",
                                "Failed to create gcal_event");
        emit error(message);
        emit status(Broken, message);
    }

    KUrl url(item.remoteId());
    temp = url.url();
    t_byte = temp.toAscii();
    gcal_event_set_url(event, t_byte.data());

    if (gcal_erase_event(gcal, event)) {
        kError() << "Failed deleting calendar";
        const QString message = i18nc("@info:status",
                                "Failed deleting new calendar");
        emit error(message);
        emit status(Broken, message);
    }

    gcal_event_delete(event);

    changeProcessed();
    kDebug() << "done deleting!!";
}

AKONADI_RESOURCE_MAIN( GCalResource )

#include "gcalresource.moc"
<commit_msg>Using the same technique with auto_ptr to retrieve event payload for adding event.<commit_after>/*  Copyright (C) 2009  Adenilson Cavalcanti <savagobr@yahoo.com>
 *
 *  This program 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; by version 2 of the License or (at your
 *  choice) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/* TODO:
 * - fix missing fields on 'add' event
 * - implement and test edit event
 * - support for recurrent events (it will require changes on libgcal)
 */

#include "gcalresource.h"
#include "settings.h"
#include "settingsadaptor.h"

#include <QtDBus/QDBusConnection>
#include <kabc/addressee.h>
#include <kabc/phonenumber.h>
#include <kabc/key.h>
#include <kabc/errorhandler.h>
#include <kcal/event.h>
#include <kdatetime.h>
#include <qstring.h>
#include <KWindowSystem>
#include <akonadi/changerecorder.h>
#include <akonadi/itemfetchscope.h>
#include <KUrl>
#include <kdatetime.h>
#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<KCal::Incidence> IncidencePtr;

extern "C" {
#include <gcalendar.h>
#include <gcal_status.h>
}

using namespace Akonadi;

GCalResource::GCalResource( const QString &id )
	: ResourceBase(id)
{
	new SettingsAdaptor( Settings::self() );
	QDBusConnection::sessionBus().registerObject(
		QLatin1String( "/Settings" ), Settings::self(),
		QDBusConnection::ExportAdaptors );

	changeRecorder()->itemFetchScope().fetchFullPayload();

	if (!(gcal = gcal_new(GCALENDAR)))
		exit(1);
	gcal_set_store_xml(gcal, 1);
	all_events.length = 0;
	all_events.entries = NULL;
}

GCalResource::~GCalResource()
{
	gcal_cleanup_events(&all_events);
	pending.clear();
	deleted.clear();
}

void GCalResource::retrieveTimestamp(QString &timestamp)
{
	timestamp = Settings::self()->timestamp();
}

void GCalResource::saveTimestamp(QString &timestamp)
{
	Settings::self()->setTimestamp(timestamp);
	Settings::self()->writeConfig();
}

void GCalResource::retrieveCollections()
{
    if (!authenticated) {
	    kError() << "No authentication for Google Calendar available";
	    const QString message = i18nc("@info: status",
					  "Not yet authenticated for "
					  "use of Google Calendar");

	    emit error(message);

	    emit status(Broken, message);
	    return;
    }

    Collection c;
    c.setParent(Collection::root());
    c.setRemoteId("google-calendar");
    c.setName(name());

    QStringList mimeTypes;
    mimeTypes << "text/calendar";
    c.setContentMimeTypes(mimeTypes);

    Collection::List list;
    list << c;
    collectionsRetrieved(list);
}

void GCalResource::retrieveItems( const Akonadi::Collection &collection )
{
	Q_UNUSED(collection);
	Item::List items;
	int result;
	gcal_event_t event;
	QString timestamp;
	QByteArray t_byte;

	kError() << "\n............. retrieveItems ...........\n";
	if (!authenticated) {
		kError() << "No authentication for Google calendar available";
		const QString message = i18nc("@info:status",
					      "Not yet authenticated for"
					      " use of Google calendar");
		emit error(message);
		emit status(Broken, message);
		return;
	}

	/* Query by updated */
// 	retrieveTimestamp(timestamp);
// 	t_byte = timestamp.toLocal8Bit();
// 	if (t_byte.length() > TIMESTAMP_SIZE) {
// 		//TODO: implement getUpdated
// 		//result = getUpdated(t_byte.data());
// 		return;
// 	}
	kError() << "First retrieve";

	if ((result = gcal_get_events(gcal, &all_events)))
		exit(1);

	/* GCalendar returns last updated entry as first element */
	event = gcal_event_element(&all_events, 0);
	if (!event) {
		kError() << "Failed to retrieve last updated event.";
		const QString message = i18nc("@info:status",
					      "Failed getting last updated"
					      " event");
		emit error(message);
		emit status(Broken, message);
		return;

	}

	timestamp = gcal_event_get_updated(event);
	saveTimestamp(timestamp);

	/* Each google entry has a unique ID and edit_url */
	for (size_t i = 0; i < all_events.length; ++i) {
		Item item(QLatin1String("text/calendar"));
		KCal::Event *kevent;
		kevent = new KCal::Event;
		QString temp;
		event = gcal_event_element(&all_events, i);

		temp = gcal_event_get_title(event);
		kevent->setSummary(temp);

		temp = gcal_event_get_where(event);
		kevent->setLocation(temp);

		KCal::Incidence::Status status;
		if (gcal_event_is_deleted(event))
			status = KCal::Incidence::StatusCanceled;
		else
			status = KCal::Incidence::StatusConfirmed;
		kevent->setStatus(status);

		temp = gcal_event_get_content(event);
		kevent->setDescription(temp);

		KDateTime start, end;
		temp = gcal_event_get_start(event);
		start = start.fromString(temp, KDateTime::ISODate);
		temp = gcal_event_get_end(event);
		end = end.fromString(temp, KDateTime::ISODate);
		kevent->setDtStart(start);
		kevent->setDtEnd(end);

		qDebug() << "start: " << start.dateTime()
			 << "\tend: " << end.dateTime();

		/* remoteID: edit_url */
		KUrl urlEtag(gcal_event_get_url(event));
		item.setRemoteId(urlEtag.url());
		item.setPayload(IncidencePtr(kevent));

		items << item;
	}

	itemsRetrieved(items);
	kError() << "\n............. done retrieveItems! ...........\n";
}

bool GCalResource::retrieveItem( const Akonadi::Item &item, const QSet<QByteArray> &parts )
{
	Q_UNUSED(parts);
	Q_UNUSED(item);
	return true;
}

void GCalResource::aboutToQuit()
{
	// TODO: any cleanup you need to do while there is still an active
	// event loop. The resource will terminate after this method returns
}

void GCalResource::doSetOnline(bool online)
{
	Q_UNUSED(online);
}

int GCalResource::getUpdated(char *timestamp)
{
	Q_UNUSED(timestamp);

	return -1;
}

void GCalResource::configure( WId windowId )
{
	int result = 0;
	if (windowId && dlgConf)
		KWindowSystem::setMainWindow(dlgConf, windowId);

	dlgConf->exec();
	if (!(result = authenticate(dlgConf->eAccount->text(),
				    dlgConf->ePass->text()))) {
		result = saveToWallet(dlgConf->eAccount->text(),
				      dlgConf->ePass->text(),
				      windowId);

		synchronize();
	}

	if (result) {
		kError() << "Failed configuring resource.";
		const QString message = i18nc("@info:status",
					      "Invalid password.");
		emit error(message);
		emit status(Broken, message);
		return;
	}
}

void GCalResource::itemAdded( const Akonadi::Item &item, const Akonadi::Collection &collection )
{
	Q_UNUSED(collection);

    gcal_event_t event;
    KCal::Event *kevent;
    IncidencePtr ptrEvent;
    QByteArray t_byte;
    QString temp;
    KDateTime time;
    //TODO: test for recurrence (and ignore creation for while)

    if (!authenticated) {
        kError() << "No authentication for Google calendar available";
        const QString message = i18nc("@info:status",
                          "Not yet authenticated for"
                          " use of Google calendar");
        emit error(message);
        emit status(Broken, message);
        return;
    }

    if (item.hasPayload<IncidencePtr>()) {
	    ptrEvent = item.payload<IncidencePtr>();
	    kevent = dynamic_cast<KCal::Event *>(ptrEvent.get());
    } else {
        kError() << "Add without payload!";
        const QString message = i18nc("@info:status",
                          "No payload to add event");
        emit error(message);
        emit status(Broken, message);
        return;
    }

    if (!(event = gcal_event_new(NULL))) {
        kError() << "Memory allocation error!";
        const QString message = i18nc("@info:status",
                      "Failed to create gcal_event");
        emit error(message);
        emit status(Broken, message);
        return;
    }

    /* FIXME: for some reason the event is missing these fields:
     * summary, description, location.
     */
    temp = kevent->summary();
    if(!temp.length()) {
        t_byte = temp.toLocal8Bit();
        gcal_event_set_title(event, t_byte);
    }

    temp = kevent->description();
    if(!temp.length()) {
        t_byte = temp.toLocal8Bit();
        gcal_event_set_content(event, t_byte);
    }

    temp = kevent->location();
    if(!temp.length()) {
        t_byte = temp.toLocal8Bit();
        gcal_event_set_where(event, t_byte);
    }

    /* Only this fields are being successfuly extracted */
    time = kevent->dtStart();
    temp = time.toString(KDateTime::ISODate);
    t_byte = temp.toLocal8Bit();
    gcal_event_set_start(event, t_byte.data());

    time = kevent->dtEnd();
    temp = time.toString(KDateTime::ISODate);
    t_byte = temp.toLocal8Bit();
    gcal_event_set_end(event, t_byte.data());

    if ((gcal_add_event(gcal, event))) {
        kError() << "Failed adding new calendar"
                << "title:" << kevent->summary();
        const QString message = i18nc("@info:status",
                            "failed adding new calendar");
        emit error(message);
        emit status(Broken, message);
    }

    KUrl url(gcal_event_get_url(event));
    Item newItem(item);
    /* Either 'new' or crash... */
    newItem.setPayload(IncidencePtr(new KCal::Event(*kevent)));
    newItem.setRemoteId(url.url());
    changeCommitted(newItem);

    gcal_event_delete(event);
}

void GCalResource::itemChanged( const Akonadi::Item &item, const QSet<QByteArray> &parts )
{
	Q_UNUSED(parts);
	Q_UNUSED(item);
}

void GCalResource::itemRemoved( const Akonadi::Item &item )
{
	Q_UNUSED(item);

    gcal_event_t event;
    QString temp;
    QByteArray t_byte;

    kDebug() << "Deleting one item ... ";

    if(!authenticated) {
        kError() << "No authentication for Google calendar";
        const QString message = i18nc("@info:status",
                                "Not yet authenticated for "
                                "use of Google calendar");
        emit error(message);
        emit status(Broken, message);
    }

    if (!(event = gcal_event_new(NULL))) {
        kError() << "Memory allocation error!";
        const QString message = i18nc("@info:status",
                                "Failed to create gcal_event");
        emit error(message);
        emit status(Broken, message);
    }

    KUrl url(item.remoteId());
    temp = url.url();
    t_byte = temp.toAscii();
    gcal_event_set_url(event, t_byte.data());

    if (gcal_erase_event(gcal, event)) {
        kError() << "Failed deleting calendar";
        const QString message = i18nc("@info:status",
                                "Failed deleting new calendar");
        emit error(message);
        emit status(Broken, message);
    }

    gcal_event_delete(event);

    changeProcessed();
    kDebug() << "done deleting!!";
}

AKONADI_RESOURCE_MAIN( GCalResource )

#include "gcalresource.moc"
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */
#include "ToolWindowManager.h"
#include "ToolWindowManagerTabBar.h"
#include "ToolWindowManagerArea.h"
#include "ToolWindowManagerWrapper.h"
#include <QMouseEvent>
#include <QStyle>
#include <QStylePainter>
#include <QStyleOption>

ToolWindowManagerTabBar::ToolWindowManagerTabBar(QWidget *parent) :
  QTabBar(parent)
{
  m_tabsClosable = false;

  setMouseTracking(true);

  m_area = qobject_cast<ToolWindowManagerArea *>(parent);

  // Workaround for extremely dodgy KDE behaviour - by default the KDE theme will install event
  // filters on various widgets such as QTabBar and any descendents, and if a click is detected on
  // them that isn't on a tab it will immediately start moving the window, interfering with our own
  // click-to-drag behaviour.
  setProperty("_kde_no_window_grab", true);

  QStyleOptionToolButton buttonOpt;

  int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);

  buttonOpt.initFrom(parentWidget());
  buttonOpt.iconSize = QSize(size, size);
  buttonOpt.subControls = 0;
  buttonOpt.activeSubControls = 0;
  buttonOpt.features = QStyleOptionToolButton::None;
  buttonOpt.arrowType = Qt::NoArrow;
  buttonOpt.state |= QStyle::State_AutoRaise;

  // TODO make our own pin icon, that is pinned/unpinned
  m_pin.icon = style()->standardIcon(QStyle::SP_TitleBarNormalButton, &buttonOpt, this);
  m_close.icon = style()->standardIcon(QStyle::SP_TitleBarCloseButton, &buttonOpt, this);

  m_pin.hover = m_pin.clicked = false;
  m_close.hover = m_close.clicked = false;
}

ToolWindowManagerTabBar::~ToolWindowManagerTabBar() {
}

QSize ToolWindowManagerTabBar::sizeHint() const {
  if(useMinimalBar()) {
    if (floatingWindowChild())
      return QSize(0, 0);

    QFontMetrics fm = fontMetrics();

    int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
    int mw = style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, this);

    int h = qMax(fm.height(), iconSize) + 2*mw;

    return QSize(h, h);
  }

  return QTabBar::sizeHint();
}

bool ToolWindowManagerTabBar::useMinimalBar() const
{
  if (count() > 1)
    return false;

  if (m_area) {
    return m_area->useMinimalTabBar();
  }
  return true;
}

QSize ToolWindowManagerTabBar::minimumSizeHint() const {
  if (useMinimalBar()) {
    return sizeHint();
  }

  return QTabBar::minimumSizeHint();
}

bool ToolWindowManagerTabBar::inButton(QPoint pos) {
  return m_pin.rect.contains(pos) || m_close.rect.contains(pos);
}

void ToolWindowManagerTabBar::paintEvent(QPaintEvent *event) {
  if (useMinimalBar()) {
    if (floatingWindowChild())
      return;

    QStylePainter p(this);

    QStyleOptionDockWidget option;

    option.initFrom(parentWidget());
    option.rect = m_titleRect;
    option.title = tabText(0);
    option.closable = m_tabsClosable;
    option.movable = false;
    // we only set floatable true so we can hijack the float button for our own pin/auto-hide button
    option.floatable = true;

    Shape s = shape();
    option.verticalTitleBar = s == RoundedEast || s == TriangularEast ||
                              s == RoundedWest || s == TriangularWest;

    p.drawControl(QStyle::CE_DockWidgetTitle, option);

    int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);

    QStyleOptionToolButton buttonOpt;

    buttonOpt.initFrom(parentWidget());
    buttonOpt.iconSize = QSize(size, size);
    buttonOpt.subControls = 0;
    buttonOpt.activeSubControls = 0;
    buttonOpt.features = QStyleOptionToolButton::None;
    buttonOpt.arrowType = Qt::NoArrow;
    buttonOpt.state = QStyle::State_Active|QStyle::State_Enabled|QStyle::State_AutoRaise;

    buttonOpt.rect = m_pin.rect;
    buttonOpt.icon = m_pin.icon;

    ToolWindowManager::ToolWindowProperty props =
      m_area->m_manager->toolWindowProperties(m_area->widget(0));

    bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

    if (!tabClosable)
      buttonOpt.rect = m_close.rect;

    QStyle::State prevState = buttonOpt.state;

    if(m_pin.clicked)
      buttonOpt.state |= QStyle::State_Sunken;
    else if(m_pin.hover)
      buttonOpt.state |= QStyle::State_Raised | QStyle::State_MouseOver;

    if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this)) {
      style()->drawPrimitive(QStyle::PE_PanelButtonTool, &buttonOpt, &p, this);
    }

    style()->drawComplexControl(QStyle::CC_ToolButton, &buttonOpt, &p, this);

    if (m_tabsClosable && tabClosable) {
      buttonOpt.rect = m_close.rect;
      buttonOpt.icon = m_close.icon;

      buttonOpt.state = prevState;

      if(m_close.clicked)
        buttonOpt.state |= QStyle::State_Sunken;
      else if(m_close.hover)
        buttonOpt.state |= QStyle::State_Raised | QStyle::State_MouseOver;

      if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this)) {
        style()->drawPrimitive(QStyle::PE_PanelButtonTool, &buttonOpt, &p, this);
      }

      style()->drawComplexControl(QStyle::CC_ToolButton, &buttonOpt, &p, this);
    }
    return;
  }

  QTabBar::paintEvent(event);
}

void ToolWindowManagerTabBar::resizeEvent(QResizeEvent *event) {
  QTabBar::resizeEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  m_titleRect = QRect(0, 0, size().width(), sizeHint().height());

  QStyleOptionDockWidget option;

  option.initFrom(parentWidget());
  option.rect = m_titleRect;
  option.closable = m_tabsClosable;
  option.movable = false;
  // we only set floatable true so we can hijack the float button for our own pin/auto-hide button
  option.floatable = true;

  m_pin.rect = style()->subElementRect(QStyle::SE_DockWidgetFloatButton, &option, this);
  m_close.rect = style()->subElementRect(QStyle::SE_DockWidgetCloseButton, &option, this);

  // TODO - temporarily until this is implemented, hide the pin button.
  m_pin.rect = QRect();
}

void ToolWindowManagerTabBar::mousePressEvent(QMouseEvent *event) {
  QTabBar::mousePressEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ButtonData prevPin = m_pin;
  ButtonData prevClose = m_close;

  ToolWindowManager::ToolWindowProperty props =
    m_area->m_manager->toolWindowProperties(m_area->widget(0));

  bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

  QRect pinRect = m_pin.rect;
  QRect closeRect = m_close.rect;

  if (!tabClosable) {
    pinRect = closeRect;
    closeRect = QRect();
  }

  if (pinRect.contains(mapFromGlobal(QCursor::pos())) &&
     event->buttons() & Qt::LeftButton) {
    m_pin.clicked = true;
  } else {
    m_pin.clicked = false;
  }

  if (closeRect.contains(mapFromGlobal(QCursor::pos())) &&
     event->buttons() & Qt::LeftButton) {
    m_close.clicked = true;
  } else {
    m_close.clicked = false;
  }

  if (prevPin != m_pin || prevClose != m_close)
    update();

  event->accept();
}

void ToolWindowManagerTabBar::mouseMoveEvent(QMouseEvent *event) {
  QTabBar::mouseMoveEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ButtonData prevPin = m_pin;
  ButtonData prevClose = m_close;

  ToolWindowManager::ToolWindowProperty props =
    m_area->m_manager->toolWindowProperties(m_area->widget(0));

  bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

  QRect pinRect = m_pin.rect;
  QRect closeRect = m_close.rect;

  if (!tabClosable) {
    pinRect = closeRect;
    closeRect = QRect();
  }

  if (pinRect.contains(mapFromGlobal(QCursor::pos()))) {
    m_pin.hover = true;
    if (event->buttons() & Qt::LeftButton)
      m_pin.clicked = true;
  } else {
    m_pin.hover = false;
    m_pin.clicked = false;
  }

  if (closeRect.contains(mapFromGlobal(QCursor::pos()))) {
    m_close.hover = true;
    if (event->buttons() & Qt::LeftButton)
      m_close.clicked = true;
  } else {
    m_close.hover = false;
    m_close.clicked = false;
  }

  if (prevPin != m_pin || prevClose != m_close)
    update();
}

void ToolWindowManagerTabBar::mouseReleaseEvent(QMouseEvent *event) {
  QTabBar::mouseReleaseEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ToolWindowManager::ToolWindowProperty props =
    m_area->m_manager->toolWindowProperties(m_area->widget(0));

  bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

  QRect pinRect = m_pin.rect;
  QRect closeRect = m_close.rect;

  if (!tabClosable) {
    pinRect = closeRect;
    closeRect = QRect();
  }

  if (pinRect.contains(mapFromGlobal(QCursor::pos()))) {
    // process a pin of these tabs

    m_pin.clicked = false;

    update();

    event->accept();
  }

  if (closeRect.contains(mapFromGlobal(QCursor::pos()))) {
    if (m_area)
      m_area->tabCloseRequested(0);

    m_close.clicked = false;

    update();

    event->accept();
  }
}

void ToolWindowManagerTabBar::tabInserted(int) {
  updateClosable();
}

void ToolWindowManagerTabBar::tabRemoved(int) {
  updateClosable();
}

void ToolWindowManagerTabBar::updateClosable() {
  QTabBar::setTabsClosable(m_tabsClosable && count() > 1);
}

bool ToolWindowManagerTabBar::floatingWindowChild() const {
  ToolWindowManagerArea *area = qobject_cast<ToolWindowManagerArea *>(parentWidget());

  if (area) {
    ToolWindowManagerWrapper *wrapper = qobject_cast<ToolWindowManagerWrapper *>(area->parentWidget());

    if (wrapper && wrapper->floating())
      return true;
  }

  return false;
}
<commit_msg>Only use the close rect for drawing the pin button if pinning is enabled<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */
#include "ToolWindowManager.h"
#include "ToolWindowManagerTabBar.h"
#include "ToolWindowManagerArea.h"
#include "ToolWindowManagerWrapper.h"
#include <QMouseEvent>
#include <QStyle>
#include <QStylePainter>
#include <QStyleOption>

ToolWindowManagerTabBar::ToolWindowManagerTabBar(QWidget *parent) :
  QTabBar(parent)
{
  m_tabsClosable = false;

  setMouseTracking(true);

  m_area = qobject_cast<ToolWindowManagerArea *>(parent);

  // Workaround for extremely dodgy KDE behaviour - by default the KDE theme will install event
  // filters on various widgets such as QTabBar and any descendents, and if a click is detected on
  // them that isn't on a tab it will immediately start moving the window, interfering with our own
  // click-to-drag behaviour.
  setProperty("_kde_no_window_grab", true);

  QStyleOptionToolButton buttonOpt;

  int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);

  buttonOpt.initFrom(parentWidget());
  buttonOpt.iconSize = QSize(size, size);
  buttonOpt.subControls = 0;
  buttonOpt.activeSubControls = 0;
  buttonOpt.features = QStyleOptionToolButton::None;
  buttonOpt.arrowType = Qt::NoArrow;
  buttonOpt.state |= QStyle::State_AutoRaise;

  // TODO make our own pin icon, that is pinned/unpinned
  m_pin.icon = style()->standardIcon(QStyle::SP_TitleBarNormalButton, &buttonOpt, this);
  m_close.icon = style()->standardIcon(QStyle::SP_TitleBarCloseButton, &buttonOpt, this);

  m_pin.hover = m_pin.clicked = false;
  m_close.hover = m_close.clicked = false;
}

ToolWindowManagerTabBar::~ToolWindowManagerTabBar() {
}

QSize ToolWindowManagerTabBar::sizeHint() const {
  if(useMinimalBar()) {
    if (floatingWindowChild())
      return QSize(0, 0);

    QFontMetrics fm = fontMetrics();

    int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
    int mw = style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, this);

    int h = qMax(fm.height(), iconSize) + 2*mw;

    return QSize(h, h);
  }

  return QTabBar::sizeHint();
}

bool ToolWindowManagerTabBar::useMinimalBar() const
{
  if (count() > 1)
    return false;

  if (m_area) {
    return m_area->useMinimalTabBar();
  }
  return true;
}

QSize ToolWindowManagerTabBar::minimumSizeHint() const {
  if (useMinimalBar()) {
    return sizeHint();
  }

  return QTabBar::minimumSizeHint();
}

bool ToolWindowManagerTabBar::inButton(QPoint pos) {
  return m_pin.rect.contains(pos) || m_close.rect.contains(pos);
}

void ToolWindowManagerTabBar::paintEvent(QPaintEvent *event) {
  if (useMinimalBar()) {
    if (floatingWindowChild())
      return;

    QStylePainter p(this);

    QStyleOptionDockWidget option;

    option.initFrom(parentWidget());
    option.rect = m_titleRect;
    option.title = tabText(0);
    option.closable = m_tabsClosable;
    option.movable = false;
    // we only set floatable true so we can hijack the float button for our own pin/auto-hide button
    option.floatable = true;

    Shape s = shape();
    option.verticalTitleBar = s == RoundedEast || s == TriangularEast ||
                              s == RoundedWest || s == TriangularWest;

    p.drawControl(QStyle::CE_DockWidgetTitle, option);

    int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);

    QStyleOptionToolButton buttonOpt;

    buttonOpt.initFrom(parentWidget());
    buttonOpt.iconSize = QSize(size, size);
    buttonOpt.subControls = 0;
    buttonOpt.activeSubControls = 0;
    buttonOpt.features = QStyleOptionToolButton::None;
    buttonOpt.arrowType = Qt::NoArrow;
    buttonOpt.state = QStyle::State_Active|QStyle::State_Enabled|QStyle::State_AutoRaise;

    buttonOpt.rect = m_pin.rect;
    buttonOpt.icon = m_pin.icon;

    ToolWindowManager::ToolWindowProperty props =
      m_area->m_manager->toolWindowProperties(m_area->widget(0));

    bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

    if (!tabClosable && !m_pin.rect.isEmpty())
      buttonOpt.rect = m_close.rect;

    QStyle::State prevState = buttonOpt.state;

    if(m_pin.clicked)
      buttonOpt.state |= QStyle::State_Sunken;
    else if(m_pin.hover)
      buttonOpt.state |= QStyle::State_Raised | QStyle::State_MouseOver;

    if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this)) {
      style()->drawPrimitive(QStyle::PE_PanelButtonTool, &buttonOpt, &p, this);
    }

    style()->drawComplexControl(QStyle::CC_ToolButton, &buttonOpt, &p, this);

    if (m_tabsClosable && tabClosable) {
      buttonOpt.rect = m_close.rect;
      buttonOpt.icon = m_close.icon;

      buttonOpt.state = prevState;

      if(m_close.clicked)
        buttonOpt.state |= QStyle::State_Sunken;
      else if(m_close.hover)
        buttonOpt.state |= QStyle::State_Raised | QStyle::State_MouseOver;

      if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this)) {
        style()->drawPrimitive(QStyle::PE_PanelButtonTool, &buttonOpt, &p, this);
      }

      style()->drawComplexControl(QStyle::CC_ToolButton, &buttonOpt, &p, this);
    }
    return;
  }

  QTabBar::paintEvent(event);
}

void ToolWindowManagerTabBar::resizeEvent(QResizeEvent *event) {
  QTabBar::resizeEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  m_titleRect = QRect(0, 0, size().width(), sizeHint().height());

  QStyleOptionDockWidget option;

  option.initFrom(parentWidget());
  option.rect = m_titleRect;
  option.closable = m_tabsClosable;
  option.movable = false;
  // we only set floatable true so we can hijack the float button for our own pin/auto-hide button
  option.floatable = true;

  m_pin.rect = style()->subElementRect(QStyle::SE_DockWidgetFloatButton, &option, this);
  m_close.rect = style()->subElementRect(QStyle::SE_DockWidgetCloseButton, &option, this);

  // TODO - temporarily until this is implemented, hide the pin button.
  m_pin.rect = QRect();
}

void ToolWindowManagerTabBar::mousePressEvent(QMouseEvent *event) {
  QTabBar::mousePressEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ButtonData prevPin = m_pin;
  ButtonData prevClose = m_close;

  ToolWindowManager::ToolWindowProperty props =
    m_area->m_manager->toolWindowProperties(m_area->widget(0));

  bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

  QRect pinRect = m_pin.rect;
  QRect closeRect = m_close.rect;

  if (!tabClosable) {
    if (!pinRect.isEmpty())
      pinRect = closeRect;
    closeRect = QRect();
  }

  if (pinRect.contains(mapFromGlobal(QCursor::pos())) &&
     event->buttons() & Qt::LeftButton) {
    m_pin.clicked = true;
  } else {
    m_pin.clicked = false;
  }

  if (closeRect.contains(mapFromGlobal(QCursor::pos())) &&
     event->buttons() & Qt::LeftButton) {
    m_close.clicked = true;
  } else {
    m_close.clicked = false;
  }

  if (prevPin != m_pin || prevClose != m_close)
    update();

  event->accept();
}

void ToolWindowManagerTabBar::mouseMoveEvent(QMouseEvent *event) {
  QTabBar::mouseMoveEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ButtonData prevPin = m_pin;
  ButtonData prevClose = m_close;

  ToolWindowManager::ToolWindowProperty props =
    m_area->m_manager->toolWindowProperties(m_area->widget(0));

  bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

  QRect pinRect = m_pin.rect;
  QRect closeRect = m_close.rect;

  if (!tabClosable) {
    if (!pinRect.isEmpty())
      pinRect = closeRect;
    closeRect = QRect();
  }

  if (pinRect.contains(mapFromGlobal(QCursor::pos()))) {
    m_pin.hover = true;
    if (event->buttons() & Qt::LeftButton)
      m_pin.clicked = true;
  } else {
    m_pin.hover = false;
    m_pin.clicked = false;
  }

  if (closeRect.contains(mapFromGlobal(QCursor::pos()))) {
    m_close.hover = true;
    if (event->buttons() & Qt::LeftButton)
      m_close.clicked = true;
  } else {
    m_close.hover = false;
    m_close.clicked = false;
  }

  if (prevPin != m_pin || prevClose != m_close)
    update();
}

void ToolWindowManagerTabBar::mouseReleaseEvent(QMouseEvent *event) {
  QTabBar::mouseReleaseEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ToolWindowManager::ToolWindowProperty props =
    m_area->m_manager->toolWindowProperties(m_area->widget(0));

  bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

  QRect pinRect = m_pin.rect;
  QRect closeRect = m_close.rect;

  if (!tabClosable) {
    if (!pinRect.isEmpty())
      pinRect = closeRect;
    closeRect = QRect();
  }

  if (pinRect.contains(mapFromGlobal(QCursor::pos()))) {
    // process a pin of these tabs

    m_pin.clicked = false;

    update();

    event->accept();
  }

  if (closeRect.contains(mapFromGlobal(QCursor::pos()))) {
    if (m_area)
      m_area->tabCloseRequested(0);

    m_close.clicked = false;

    update();

    event->accept();
  }
}

void ToolWindowManagerTabBar::tabInserted(int) {
  updateClosable();
}

void ToolWindowManagerTabBar::tabRemoved(int) {
  updateClosable();
}

void ToolWindowManagerTabBar::updateClosable() {
  QTabBar::setTabsClosable(m_tabsClosable && count() > 1);
}

bool ToolWindowManagerTabBar::floatingWindowChild() const {
  ToolWindowManagerArea *area = qobject_cast<ToolWindowManagerArea *>(parentWidget());

  if (area) {
    ToolWindowManagerWrapper *wrapper = qobject_cast<ToolWindowManagerWrapper *>(area->parentWidget());

    if (wrapper && wrapper->floating())
      return true;
  }

  return false;
}
<|endoftext|>