<commit_before>/*
    python/pybind11_opencv.hpp: Transparent conversion for OpenCV cv::Mat matrices.
                                This header is based on pybind11/eigen.h.

    Copyright (c) 2016 Patrik Huber

    All rights reserved. Use of this source code is governed by a
    BSD-style license that can be found in pybind11's LICENSE file.
*/
#pragma once

#include "pybind11/numpy.h"

#include "opencv2/core/core.hpp"
#include "opencv2/core/types_c.h"

#include <iostream>

NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail)

/**
 * @file python/pybind11_opencv.hpp
 * @brief Transparent conversion to and from Python for OpenCV vector and matrix types.
 *
 * OpenCV and numpy both use row-major storage order by default, so the conversion works
 * pretty much out of the box, even for multi-channel matrices.
 * Handling of non-standard strides is not implemented.
 * Handling of column-major numpy arrays is unsupported.
 */

/**
 * @brief Transparent conversion for OpenCV's cv::Vec types to and from Python.
 */
template<typename T, int N>
struct type_caster<cv::Vec<T, N>>
{
	using vector_type = cv::Vec<T, N>;
	using Scalar = T;
	static constexpr std::size_t num_elements = N;

	bool load(handle src, bool)
	{
		auto buf = array_t<Scalar>::ensure(src);
		if (!buf)
			return false;

		if (buf.ndim() == 1) // a 1-dimensional vector
		{
			if (buf.shape(0) != num_elements) {
				return false; // not a N-elements vector (can this ever happen?)
			}
			if (buf.strides(0) != sizeof(Scalar))
			{
				std::cout << "An array with non-standard strides is given. Please pass a contiguous array." << std::endl;
				return false;
			}
			value = cv::Vec<T, N>(buf.mutable_data());
		}
		else { // buf.ndim() != 1
			return false;
		}
		return true;
	}

	static handle cast(const vector_type& src, return_value_policy /* policy */, handle /* parent */)
	{
		return array(
			num_elements,	// shape
			src.val			// data
		).release();
	}

	// Specifies the doc-string for the type in Python:
	PYBIND11_TYPE_CASTER(vector_type, _("numpy.ndarray[") + npy_format_descriptor<Scalar>::name() +
		_("[") + _<num_elements>() + _("]]"));
};

/**
 * @brief Helper function to convert a Python array to a cv::Mat.
 *
 * This is an internal helper function that converts a pybind11::array to a cv::Mat.
 * - The \p opencv_depth given must match the type of the data in \p buf.
 * - \p buf must be a 1, 2 or 3-dimensional array.
 * - The function expects a valid \p buf object.
 *
 * The buffer's mutable_data() is used directly, and I think no data is copied.
 *
 * @param buf Python buffer object.
 * @param opencv_depth OpenCV "depth" (the data type, e.g. CV_8U).
 * @return A cv::Mat pointing to the buffer's data or an empty Mat if an error occured.
 */
cv::Mat pyarray_to_mat(pybind11::array buf, int opencv_depth)
{
	cv::Mat value;

	if (buf.ndim() == 1)
	{ // A numpy array, with only one dimension. A row-vector.
		auto num_elements = buf.shape(0);
		auto opencv_type = CV_MAKETYPE(opencv_depth, 1);
		value = cv::Mat(1, num_elements, opencv_type, buf.mutable_data());
	}
	else if (buf.ndim() == 2)
	{ // We got a matrix (but it can also be 1 x n or n x 1)
		auto opencv_type = CV_MAKETYPE(opencv_depth, 1);
		value = cv::Mat(buf.shape(0), buf.shape(1), opencv_type, buf.mutable_data());
	}
	else if (buf.ndim() == 3)
	{ // We got something with 3 dimensions, i.e. an image with 2, 3 or 4 channels (or 'k' for that matter)
		auto num_chans = buf.shape(2); // Check whether 3 or 4 and abort otherwise??
		auto opencv_type = CV_MAKETYPE(opencv_depth, num_chans);
		value = cv::Mat(buf.shape(0), buf.shape(1), opencv_type, buf.mutable_data());
	}
	else { // buf.ndim() is not 1, 2 or 3.
		return cv::Mat();
	}
	return value;
};

/**
 * @brief Transparent conversion for OpenCV's cv::Mat type to and from Python.
 *
 * Converts cv::Mat's to and from Python. Can construct a cv::Mat from numpy arrays,
 * as well as potentially other Python array types.
 *
 * - Supports only contiguous matrices
 * - The numpy array has to be in default (row-major) storage order
 * - Non-default strides are not implemented.
 *
 * Note about strides: http://docs.opencv.org/2.4/modules/core/doc/basic_structures.html#mat-step1
 * And possibly use src.elemSize or src.elemSize1.
 * See also the old bindings: https://github.com/patrikhuber/eos/commit/1c3b0113a3efcbb1a92efca646be663ef8593793
 */
template<>
struct type_caster<cv::Mat>
{
    bool load(handle src, bool)
	{
		// Since cv::Mat has its time dynamically specified at run-time, we can't bind functions
		// that take a cv::Mat to any particular Scalar type.
		// Thus the data we get from python can be any type.
		auto buf = pybind11::array::ensure(src);
		if (!buf)
			return false;

		auto pyarray_dtype = buf.dtype();
		// Todo: We should probably check that buf.strides(i) is "default", by dividing it by the Scalar type or something.

		int opencv_depth;
		if (pyarray_dtype == pybind11::dtype::of<std::uint8_t>())
		{
			opencv_depth = CV_8U;
		}
		else if (pyarray_dtype == pybind11::dtype::of<float>())
		{
			opencv_depth = CV_32F;
		}
		else if (pyarray_dtype == pybind11::dtype::of<double>())
		{
			opencv_depth = CV_64F;
		}
		else
		{
			return false;
		}
		// Todo: Would be nice to add int32, as it's the default in python if you create a
		//       list with [1, 2, 3]. But it doesn't evaluate to true when comparing with
		//       all integer dtypes (int, int32, uint32, etc.).

		value = pyarray_to_mat(buf, opencv_depth);
		if (value.empty())
		{
			return false;
		}
		return true;
	};

    static handle cast(const cv::Mat& src, return_value_policy /* policy */, handle /* parent */)
	{
		if (!src.isContinuous())
		{
			throw std::runtime_error("Cannot cast non-contiguous cv::Mat objects to Python. Change the C++ code to return a contiguous cv::Mat.");
			// We could probably support that with implementing strides properly.
		}

		const auto opencv_depth = src.depth();
		const auto num_chans = src.channels();
		std::vector<std::size_t> shape;
		if (num_chans == 1)
		{
			shape = { (size_t)src.rows, (size_t)src.cols };
			// if either of them is == 1, we could specify only 1 value for shape - but be careful with strides,
			// if there's a col-vector, I don't think we can do it without using strides.
			// Also, check what happens in python when we pass a col & row vec respectively.
		}
		else if (num_chans == 2 || num_chans == 3 || num_chans == 4)
		{
			shape = { (size_t)src.rows, (size_t)src.cols, (size_t)num_chans };
		}
		else {
			throw std::runtime_error("Cannot return matrices with more than 4 channels back to Python.");
			// We could probably implement this quite easily but >4 channel images/matrices don't occur often.
		}

		// Now return the data, depending on its type:
		if (opencv_depth == CV_8U)
		{
			return array(pybind11::dtype::of<std::uint8_t>(), shape, src.data).release();
		}
		else if (opencv_depth == CV_32F)
		{
			return array(pybind11::dtype::of<float>(), shape, src.data).release();
		}
		else if (opencv_depth == CV_64F)
		{
			return array(pybind11::dtype::of<double>(), shape, src.data).release();
		}
		else {
			throw std::runtime_error("Can currently only return matrices of type 8U, 32F and 64F back to Python. Other types can be added if needed.");
		}
	};

    PYBIND11_TYPE_CASTER(cv::Mat, _("numpy.ndarray[uint8|float32|float64[m, n, d]]"));
};

NAMESPACE_END(detail)
NAMESPACE_END(pybind11)
<commit_msg>Replaced dtype comparison with isinstance()<commit_after>/*
    python/pybind11_opencv.hpp: Transparent conversion for OpenCV cv::Mat matrices.
                                This header is based on pybind11/eigen.h.

    Copyright (c) 2016 Patrik Huber

    All rights reserved. Use of this source code is governed by a
    BSD-style license that can be found in pybind11's LICENSE file.
*/
#pragma once

#include "pybind11/numpy.h"

#include "opencv2/core/core.hpp"
#include "opencv2/core/types_c.h"

#include <iostream>

NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail)

/**
 * @file python/pybind11_opencv.hpp
 * @brief Transparent conversion to and from Python for OpenCV vector and matrix types.
 *
 * OpenCV and numpy both use row-major storage order by default, so the conversion works
 * pretty much out of the box, even for multi-channel matrices.
 * Handling of non-standard strides is not implemented.
 * Handling of column-major numpy arrays is unsupported.
 */

/**
 * @brief Transparent conversion for OpenCV's cv::Vec types to and from Python.
 */
template<typename T, int N>
struct type_caster<cv::Vec<T, N>>
{
	using vector_type = cv::Vec<T, N>;
	using Scalar = T;
	static constexpr std::size_t num_elements = N;

	bool load(handle src, bool)
	{
		auto buf = array_t<Scalar>::ensure(src);
		if (!buf)
			return false;

		if (buf.ndim() == 1) // a 1-dimensional vector
		{
			if (buf.shape(0) != num_elements) {
				return false; // not a N-elements vector (can this ever happen?)
			}
			if (buf.strides(0) != sizeof(Scalar))
			{
				std::cout << "An array with non-standard strides is given. Please pass a contiguous array." << std::endl;
				return false;
			}
			value = cv::Vec<T, N>(buf.mutable_data());
		}
		else { // buf.ndim() != 1
			return false;
		}
		return true;
	}

	static handle cast(const vector_type& src, return_value_policy /* policy */, handle /* parent */)
	{
		return array(
			num_elements,	// shape
			src.val			// data
		).release();
	}

	// Specifies the doc-string for the type in Python:
	PYBIND11_TYPE_CASTER(vector_type, _("numpy.ndarray[") + npy_format_descriptor<Scalar>::name() +
		_("[") + _<num_elements>() + _("]]"));
};

/**
 * @brief Helper function to convert a Python array to a cv::Mat.
 *
 * This is an internal helper function that converts a pybind11::array to a cv::Mat.
 * - The \p opencv_depth given must match the type of the data in \p buf.
 * - \p buf must be a 1, 2 or 3-dimensional array.
 * - The function expects a valid \p buf object.
 *
 * The buffer's mutable_data() is used directly, and I think no data is copied.
 *
 * @param buf Python buffer object.
 * @param opencv_depth OpenCV "depth" (the data type, e.g. CV_8U).
 * @return A cv::Mat pointing to the buffer's data or an empty Mat if an error occured.
 */
cv::Mat pyarray_to_mat(pybind11::array buf, int opencv_depth)
{
	cv::Mat value;

	if (buf.ndim() == 1)
	{ // A numpy array, with only one dimension. A row-vector.
		auto num_elements = buf.shape(0);
		auto opencv_type = CV_MAKETYPE(opencv_depth, 1);
		value = cv::Mat(1, num_elements, opencv_type, buf.mutable_data());
	}
	else if (buf.ndim() == 2)
	{ // We got a matrix (but it can also be 1 x n or n x 1)
		auto opencv_type = CV_MAKETYPE(opencv_depth, 1);
		value = cv::Mat(buf.shape(0), buf.shape(1), opencv_type, buf.mutable_data());
	}
	else if (buf.ndim() == 3)
	{ // We got something with 3 dimensions, i.e. an image with 2, 3 or 4 channels (or 'k' for that matter)
		auto num_chans = buf.shape(2); // Check whether 3 or 4 and abort otherwise??
		auto opencv_type = CV_MAKETYPE(opencv_depth, num_chans);
		value = cv::Mat(buf.shape(0), buf.shape(1), opencv_type, buf.mutable_data());
	}
	else { // buf.ndim() is not 1, 2 or 3.
		return cv::Mat();
	}
	return value;
};

/**
 * @brief Transparent conversion for OpenCV's cv::Mat type to and from Python.
 *
 * Converts cv::Mat's to and from Python. Can construct a cv::Mat from numpy arrays,
 * as well as potentially other Python array types.
 *
 * - Supports only contiguous matrices
 * - The numpy array has to be in default (row-major) storage order
 * - Non-default strides are not implemented.
 *
 * Note about strides: http://docs.opencv.org/2.4/modules/core/doc/basic_structures.html#mat-step1
 * And possibly use src.elemSize or src.elemSize1.
 * See also the old bindings: https://github.com/patrikhuber/eos/commit/1c3b0113a3efcbb1a92efca646be663ef8593793
 */
template<>
struct type_caster<cv::Mat>
{
    bool load(handle src, bool)
	{
		// Since cv::Mat has its time dynamically specified at run-time, we can't bind functions
		// that take a cv::Mat to any particular Scalar type.
		// Thus the data we get from python can be any type.
		auto buf = pybind11::array::ensure(src);
		if (!buf)
			return false;

		// Todo: We should probably check that buf.strides(i) is "default", by dividing it by the Scalar type or something.

		int opencv_depth;
		if (pybind11::isinstance<pybind11::array_t<std::uint8_t>>(buf))
		{
			opencv_depth = CV_8U;
		}
		else if (pybind11::isinstance<pybind11::array_t<float>>(buf))
		{
			opencv_depth = CV_32F;
		}
		else if (pybind11::isinstance<pybind11::array_t<double>>(buf))
		{
			opencv_depth = CV_64F;
		}
		else
		{
			return false;
		}
		// Todo: Would be nice to add int32, as it's the default in python if you create a
		//       list with [1, 2, 3]. But it doesn't evaluate to true when comparing with
		//       all integer dtypes (int, int32, uint32, etc.).

		value = pyarray_to_mat(buf, opencv_depth);
		if (value.empty())
		{
			return false;
		}
		return true;
	};

    static handle cast(const cv::Mat& src, return_value_policy /* policy */, handle /* parent */)
	{
		if (!src.isContinuous())
		{
			throw std::runtime_error("Cannot cast non-contiguous cv::Mat objects to Python. Change the C++ code to return a contiguous cv::Mat.");
			// We could probably support that with implementing strides properly.
		}

		const auto opencv_depth = src.depth();
		const auto num_chans = src.channels();
		std::vector<std::size_t> shape;
		if (num_chans == 1)
		{
			shape = { (size_t)src.rows, (size_t)src.cols };
			// if either of them is == 1, we could specify only 1 value for shape - but be careful with strides,
			// if there's a col-vector, I don't think we can do it without using strides.
			// Also, check what happens in python when we pass a col & row vec respectively.
		}
		else if (num_chans == 2 || num_chans == 3 || num_chans == 4)
		{
			shape = { (size_t)src.rows, (size_t)src.cols, (size_t)num_chans };
		}
		else {
			throw std::runtime_error("Cannot return matrices with more than 4 channels back to Python.");
			// We could probably implement this quite easily but >4 channel images/matrices don't occur often.
		}

		// Now return the data, depending on its type:
		if (opencv_depth == CV_8U)
		{
			return array(pybind11::dtype::of<std::uint8_t>(), shape, src.data).release();
		}
		else if (opencv_depth == CV_32F)
		{
			return array(pybind11::dtype::of<float>(), shape, src.data).release();
		}
		else if (opencv_depth == CV_64F)
		{
			return array(pybind11::dtype::of<double>(), shape, src.data).release();
		}
		else {
			throw std::runtime_error("Can currently only return matrices of type 8U, 32F and 64F back to Python. Other types can be added if needed.");
		}
	};

    PYBIND11_TYPE_CASTER(cv::Mat, _("numpy.ndarray[uint8|float32|float64[m, n, d]]"));
};

NAMESPACE_END(detail)
NAMESPACE_END(pybind11)
<|endoftext|>


<commit_before>/** @file
    @brief Implementation

    @date 2015

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

// Copyright 2015 Sensics, Inc.
//
// 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.

#undef OSVR_DEV_VERBOSE_DISABLE

// Internal Includes
#include <osvr/Common/ProcessDeviceDescriptor.h>
#include <osvr/Common/PathTree.h>
#include <osvr/Common/PathNode.h>
#include <osvr/Common/PathElementTools.h>
#include <osvr/Common/RoutingConstants.h>
#include <osvr/Util/Flag.h>
#include <osvr/Util/Verbosity.h>
#include <osvr/Util/TreeTraversalVisitor.h>
#include <osvr/Common/AliasProcessor.h>
#include <osvr/Common/NormalizeDeviceDescriptor.h>

#include "PathParseAndRetrieve.h"

// Library/third-party includes
#include <json/value.h>
#include <json/reader.h>
#include <boost/noncopyable.hpp>
#include <boost/variant/get.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/erase.hpp>

// Standard includes
#include <utility>
#include <tuple>

namespace osvr {
namespace common {
    static const char INTERFACES_KEY[] = "interfaces";

    static inline util::Flag
    processInterfacesFromDescriptor(PathNode &devNode,
                                    Json::Value const &desc) {
        util::Flag changed;
        if (!desc.isMember(INTERFACES_KEY)) {
            // No interfaces member
            return changed;
        }

        Json::Value const &ifaces = desc[INTERFACES_KEY];
        if (!ifaces.isObject()) {
            // Interfaces member isn't an object
            return changed;
        }
        for (auto const &iface : ifaces.getMemberNames()) {

            auto &ifaceNode = treePathRetrieve(devNode, iface);
            if (elements::isNull(ifaceNode.value())) {
                ifaceNode.value() = elements::InterfaceElement();
                changed.set();
            }
        }
        return changed;
    }

    static const char TARGET_KEY[] = "$target";
    static const char SEMANTIC_KEY[] = "semantic";
    static const char AUTOMATIC_KEY[] = "automaticAliases";
    namespace {
        class SemanticRecursion : boost::noncopyable {
          public:
            SemanticRecursion(PathNode &devNode) : m_devNode(devNode) {}

            util::Flag operator()(Json::Value const &semanticObject) {
                m_recurse(semanticObject, SEMANTIC_KEY);
                return m_flag;
            }

          private:
            bool m_add(Json::Value const &currentLevel,
                       std::string const &relativeSemanticPath) {
                return addAliasFromSourceAndRelativeDest(
                    m_devNode, currentLevel.toStyledString(),
                    relativeSemanticPath, ALIASPRIORITY_SEMANTICROUTE);
            }
            void m_recurse(Json::Value const &currentLevel,
                           std::string const &relativeSemanticPath) {
                if (currentLevel.isString()) {
                    m_flag += m_add(currentLevel, relativeSemanticPath);
                    return;
                }
                if (currentLevel.isObject()) {
                    Json::Value target = currentLevel[TARGET_KEY];
                    if (!target.isNull()) {
                        m_flag += m_add(target, relativeSemanticPath);
                    }

                    for (auto const &memberName :
                         currentLevel.getMemberNames()) {
                        if (TARGET_KEY == memberName) {
                            continue;
                        }
                        m_recurse(currentLevel[memberName],
                                  relativeSemanticPath + getPathSeparator() +
                                      memberName);
                    }
                }
            }
            PathNode &m_devNode;
            util::Flag m_flag;
        };

    } // namespace

    static inline util::Flag
    processSemanticFromDescriptor(PathNode &devNode, Json::Value const &desc) {
        util::Flag changed;
        Json::Value const &sem = desc[SEMANTIC_KEY];
        if (!sem.isObject()) {
            // Semantic member isn't an object or isn't a member
            return changed;
        }
        SemanticRecursion f{devNode};
        changed += f(sem);
        return changed;
    }
    static inline bool processAutomaticFromDescriptor(PathNode &devNode,
                                                      Json::Value const &desc) {
        Json::Value const &automatic = desc[AUTOMATIC_KEY];
        return AliasProcessor{}
            .setDefaultPriority(ALIASPRIORITY_AUTOMATIC)
            .enableRelativePath()
            .enableRelativeSource()
            .enableWildcard()
            .process(devNode, automatic);
    }

    /// Given a device name, which may or may not include a host, as well as a
    /// fallback host and port, return a (dev, host) string pair.
    static inline std::pair<std::string, std::string>
    getDevHost(std::string const &deviceName, std::string const &host) {

        auto atLocation = deviceName.find('@');
        if (std::string::npos == atLocation) {
            // No at-symbol - we append our hostname.
            return std::make_pair(deviceName, host);
        }

        // Split host from device
        std::string devName(deviceName);
        devName.resize(atLocation);
        return std::make_pair(devName, devName.substr(atLocation + 1));
    }

    bool processDeviceDescriptorForPathTree(PathTree &tree,
                                            std::string const &deviceName,
                                            std::string const &jsonDescriptor,
                                            int listenPort,
                                            std::string const &host) {
        std::string devName{deviceName};
        if (getPathSeparatorCharacter() == devName.at(0)) {
            // Leading slash, which we'll need to drop from the device name
            devName.erase(begin(devName));
        }
        util::Flag changed;

        /// Set up device node
        auto &devNode = tree.getNodeByPath(getPathSeparator() + devName);
        auto devElt = boost::get<elements::DeviceElement>(&devNode.value());
        if (nullptr == devElt) {
            std::string dev;
            std::string devHost;

            // Split host from device, or use the default host and port.
            std::tie(dev, devHost) = getDevHost(devName, host);
            devNode.value() =
                elements::DeviceElement::createDeviceElement(dev, devHost, listenPort);
            devElt = boost::get<elements::DeviceElement>(&devNode.value());
            changed.set();
        }

        /// normalize device descriptor
        const std::string normalizedDescriptor =
            normalizeDeviceDescriptor(jsonDescriptor);

        /// Parse JSON to stuff into device node.
        Json::Value descriptor;
        {
            Json::Reader reader;
            if (!reader.parse(normalizedDescriptor, descriptor)) {
                /// @todo warn about failed descriptor parse?
                return changed.get();
            }
        }
        if (descriptor == devElt->getDescriptor() && !changed) {
            /// @todo no change in descriptor so no processing?
            return changed.get();
        }
        devElt->getDescriptor() = descriptor;

        changed += processDeviceDescriptorFromExistingDevice(devNode, *devElt);

        return changed.get();
    }

    bool processDeviceDescriptorFromExistingDevice(
        PathNode &devNode, elements::DeviceElement const &devElt) {
        util::Flag changed;

        changed +=
            processInterfacesFromDescriptor(devNode, devElt.getDescriptor());

        changed +=
            processSemanticFromDescriptor(devNode, devElt.getDescriptor());

        changed +=
            processAutomaticFromDescriptor(devNode, devElt.getDescriptor());

        return changed.get();
    }

} // namespace common
} // namespace osvr
<commit_msg>handle articulationSpec when processing device descriptor<commit_after>/** @file
    @brief Implementation

    @date 2015

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

// Copyright 2015 Sensics, Inc.
//
// 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.

#undef OSVR_DEV_VERBOSE_DISABLE

// Internal Includes
#include <osvr/Common/ProcessDeviceDescriptor.h>
#include <osvr/Common/PathTree.h>
#include <osvr/Common/PathNode.h>
#include <osvr/Common/PathElementTools.h>
#include <osvr/Common/RoutingConstants.h>
#include <osvr/Util/Flag.h>
#include <osvr/Util/Verbosity.h>
#include <osvr/Util/TreeTraversalVisitor.h>
#include <osvr/Common/AliasProcessor.h>
#include <osvr/Common/NormalizeDeviceDescriptor.h>

#include "PathParseAndRetrieve.h"

// Library/third-party includes
#include <json/value.h>
#include <json/reader.h>
#include <boost/noncopyable.hpp>
#include <boost/variant/get.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/erase.hpp>

// Standard includes
#include <utility>
#include <tuple>

namespace osvr {
namespace common {
    static const char INTERFACES_KEY[] = "interfaces";

    static inline util::Flag
    processInterfacesFromDescriptor(PathNode &devNode,
                                    Json::Value const &desc) {
        util::Flag changed;
        if (!desc.isMember(INTERFACES_KEY)) {
            // No interfaces member
            return changed;
        }

        Json::Value const &ifaces = desc[INTERFACES_KEY];
        if (!ifaces.isObject()) {
            // Interfaces member isn't an object
            return changed;
        }
        for (auto const &iface : ifaces.getMemberNames()) {

            auto &ifaceNode = treePathRetrieve(devNode, iface);
            if (elements::isNull(ifaceNode.value())) {
                ifaceNode.value() = elements::InterfaceElement();
                changed.set();
            }
        }
        return changed;
    }

    static const char TARGET_KEY[] = "$target";
    static const char SEMANTIC_KEY[] = "semantic";
    static const char AUTOMATIC_KEY[] = "automaticAliases";
    static const char ARTICULATION_KEY[] = "articulationSpec";
    static const char DATA_KEY[] = "$data";
    namespace {
        class SemanticRecursion : boost::noncopyable {
          public:
            SemanticRecursion(PathNode &devNode) : m_devNode(devNode) {}

            util::Flag operator()(Json::Value const &semanticObject) {
                m_recurse(semanticObject, SEMANTIC_KEY);
                return m_flag;
            }

          private:
            bool m_add(Json::Value const &currentLevel,
                       std::string const &relativeSemanticPath) {
                return addAliasFromSourceAndRelativeDest(
                    m_devNode, currentLevel.toStyledString(),
                    relativeSemanticPath, ALIASPRIORITY_SEMANTICROUTE);
            }
            void m_recurse(Json::Value const &currentLevel,
                           std::string const &relativeSemanticPath) {
                if (currentLevel.isString()) {
                    m_flag += m_add(currentLevel, relativeSemanticPath);
                    return;
                }
                if (currentLevel.isObject()) {
                    Json::Value target = currentLevel[TARGET_KEY];
                    if (!target.isNull()) {
                        m_flag += m_add(target, relativeSemanticPath);
                    }

                    for (auto const &memberName :
                         currentLevel.getMemberNames()) {
                        if (TARGET_KEY == memberName) {
                            continue;
                        }
                        m_recurse(currentLevel[memberName],
                                  relativeSemanticPath + getPathSeparator() +
                                      memberName);
                    }
                }
            }
            PathNode &m_devNode;
            util::Flag m_flag;
        };

        class ArticulationsRecursion : boost::noncopyable {
          public:
            ArticulationsRecursion(PathNode &devNode) : m_devNode(devNode) {}

            util::Flag operator()(Json::Value const &articulationsObject) {
                m_recurse(articulationsObject, ARTICULATION_KEY);
                return m_flag;
            }

          private:
            bool m_add(Json::Value const &currentLevel,
                       std::string const &relativeArticulationsPath) {
                return addArticulation(m_devNode, currentLevel.toStyledString(),
                                       relativeArticulationsPath);
            }
            void m_recurse(Json::Value const &currentLevel,
                           std::string const &relativeArticulationsPath) {
                // if (currentLevel.isString()) {
                //    m_flag += m_add(currentLevel, relativeArticulationsPath);
                //    return;
                //}
                if (currentLevel.isObject()) {
                    Json::Value data = currentLevel[DATA_KEY];
                    if (!data.isNull()) {
                        m_flag += m_add(data, relativeArticulationsPath);
                    }

                    for (auto const &memberName :
                         currentLevel.getMemberNames()) {
                        if (DATA_KEY == memberName) {
                            continue;
                        }
                        m_recurse(currentLevel[memberName],
                                  relativeArticulationsPath +
                                      getPathSeparator() + memberName);
                    }
                }
            }
            PathNode &m_devNode;
            util::Flag m_flag;
        };

    } // namespace

    static inline util::Flag
    processSemanticFromDescriptor(PathNode &devNode, Json::Value const &desc) {
        util::Flag changed;
        Json::Value const &sem = desc[SEMANTIC_KEY];
        if (!sem.isObject()) {
            // Semantic member isn't an object or isn't a member
            return changed;
        }
        SemanticRecursion f{devNode};
        changed += f(sem);
        return changed;
    }

    static inline util::Flag
    processArticulationSpecFromDescriptor(PathNode &devNode,
                                          Json::Value const &desc) {
        util::Flag changed;
        Json::Value const &articulations = desc[ARTICULATION_KEY];
        if (!articulations.isObject()) {
            // Articulation spec member isn't an object or isn't a member
            return changed;
        }
        ArticulationsRecursion f{devNode};
        changed += f(articulations);
        return changed;
    }

    static inline bool processAutomaticFromDescriptor(PathNode &devNode,
                                                      Json::Value const &desc) {
        Json::Value const &automatic = desc[AUTOMATIC_KEY];
        return AliasProcessor{}
            .setDefaultPriority(ALIASPRIORITY_AUTOMATIC)
            .enableRelativePath()
            .enableRelativeSource()
            .enableWildcard()
            .process(devNode, automatic);
    }

    /// Given a device name, which may or may not include a host, as well as a
    /// fallback host and port, return a (dev, host) string pair.
    static inline std::pair<std::string, std::string>
    getDevHost(std::string const &deviceName, std::string const &host) {

        auto atLocation = deviceName.find('@');
        if (std::string::npos == atLocation) {
            // No at-symbol - we append our hostname.
            return std::make_pair(deviceName, host);
        }

        // Split host from device
        std::string devName(deviceName);
        devName.resize(atLocation);
        return std::make_pair(devName, devName.substr(atLocation + 1));
    }

    bool processDeviceDescriptorForPathTree(PathTree &tree,
                                            std::string const &deviceName,
                                            std::string const &jsonDescriptor,
                                            int listenPort,
                                            std::string const &host) {
        std::string devName{deviceName};
        if (getPathSeparatorCharacter() == devName.at(0)) {
            // Leading slash, which we'll need to drop from the device name
            devName.erase(begin(devName));
        }
        util::Flag changed;

        /// Set up device node
        auto &devNode = tree.getNodeByPath(getPathSeparator() + devName);
        auto devElt = boost::get<elements::DeviceElement>(&devNode.value());
        if (nullptr == devElt) {
            std::string dev;
            std::string devHost;

            // Split host from device, or use the default host and port.
            std::tie(dev, devHost) = getDevHost(devName, host);
            devNode.value() =
                elements::DeviceElement::createDeviceElement(dev, devHost, listenPort);
            devElt = boost::get<elements::DeviceElement>(&devNode.value());
            changed.set();
        }

        /// normalize device descriptor
        const std::string normalizedDescriptor =
            normalizeDeviceDescriptor(jsonDescriptor);

        /// Parse JSON to stuff into device node.
        Json::Value descriptor;
        {
            Json::Reader reader;
            if (!reader.parse(normalizedDescriptor, descriptor)) {
                /// @todo warn about failed descriptor parse?
                return changed.get();
            }
        }
        if (descriptor == devElt->getDescriptor() && !changed) {
            /// @todo no change in descriptor so no processing?
            return changed.get();
        }
        devElt->getDescriptor() = descriptor;

        changed += processDeviceDescriptorFromExistingDevice(devNode, *devElt);

        return changed.get();
    }

    bool processDeviceDescriptorFromExistingDevice(
        PathNode &devNode, elements::DeviceElement const &devElt) {
        util::Flag changed;

        changed +=
            processInterfacesFromDescriptor(devNode, devElt.getDescriptor());

        changed +=
            processSemanticFromDescriptor(devNode, devElt.getDescriptor());

        changed +=
            processAutomaticFromDescriptor(devNode, devElt.getDescriptor());

        changed += processArticulationSpecFromDescriptor(
            devNode, devElt.getDescriptor());

        return changed.get();
    }

} // namespace common
} // namespace osvr
<|endoftext|>


<commit_before>#include "cellmlplugin.h"
#include "coreutils.h"

#include "CellMLBootstrap.hpp"
#include "IfaceCellML_APISPEC.hxx"
#include "cellml-api-cxx-support.hpp"

#include <QDebug>
#include <QDir>
#include <QUrl>

namespace OpenCOR {
namespace CellML {

PLUGININFO_FUNC CellMLPluginInfo()
{
    Descriptions descriptions;

    descriptions.insert("en", "A plugin to use the CellML API");
    descriptions.insert("fr", "Une extension pour utiliser l'API CellML");

    return PluginInfo(PluginInfo::V001,
                      PluginInfo::General,
                      PluginInfo::Api,
                      false,
                      QStringList(),
                      descriptions);
}

Q_EXPORT_PLUGIN2(CellML, CellMLPlugin)

void CellMLPlugin::initialize()
{
    using namespace iface::cellml_api;

    // Fetch a bootstrap object

    CellMLBootstrap *cbs = CreateCellMLBootstrap();

    // Retrieve the model loader

    DOMModelLoader *ml = cbs->modelLoader();

    // Load our test CellML model and return its cmeta:id
    // Note: we do this within a try...catch statement since we might get an
    //       exception...

    QString testCellmlModelFileName = QDir::tempPath()+QDir::separator()+"test_cellml_model.cellml";

    Core::saveResourceAs(":test_cellml_model", testCellmlModelFileName);

    try {
        qDebug("Loading the model...");

        Model *model = ml->loadFromURL(QUrl::fromLocalFile(testCellmlModelFileName).toString().toStdWString().c_str());

        qDebug();
        qDebug("Retrieving some model properties...");

        // Retrieve the model's name

        qDebug("    Model '%s'",
               QString::fromWCharArray(model->name()).toLatin1().constData());

        // Go through the model's components and their respective variables

        CellMLComponentSet *comps = model->modelComponents();
        CellMLComponentIterator *compsIter = comps->iterateComponents();

        for (int i = 0; i < comps->length(); i++) {
            CellMLComponent *comp = compsIter->nextComponent();

            qDebug("        Component '%s'",
                   QString::fromWCharArray(comp->name()).toLatin1().constData());

            CellMLVariableSet *vars = comp->variables();
            CellMLVariableIterator *varsIter = vars->iterateVariables();

            for(int j = 0; j < vars->length(); ++j)
                qDebug("            Variable '%s'",
                       QString::fromStdWString(varsIter->nextVariable()->name()).toLatin1().constData());
        }

        // Go through the model's connections and information

        ConnectionSet *conns = model->connections();
        ConnectionIterator *connsIter = conns->iterateConnections();

        for (int i = 0; i < conns->length(); i++) {
            Connection *conn = connsIter->nextConnection();
            MapComponents *compMap = conn->componentMapping();

            qDebug("        Connection between '%s' and '%s'",
                   QString::fromStdWString(compMap->firstComponentName()).toLatin1().constData(),
                   QString::fromStdWString(compMap->secondComponentName()).toLatin1().constData());

            MapVariablesSet *varMaps = conn->variableMappings();
            MapVariablesIterator *varMapsIter = varMaps->iterateMapVariables();

            for(int j = 0; j < varMaps->length(); ++j) {
                MapVariables *mapVars = varMapsIter->nextMapVariables();

                qDebug("            For variables '%s' and '%s'",
                       QString::fromStdWString(mapVars->firstVariableName()).toLatin1().constData(),
                       QString::fromStdWString(mapVars->secondVariableName()).toLatin1().constData());
            }
        }

        // All done...

        qDebug();
        qDebug("All done...");
    } catch (CellMLException &) {
        qDebug("ERROR: %s.",
               QString::fromWCharArray(ml->lastErrorMessage()).toLatin1().constData());
    }

    QFile::remove(testCellmlModelFileName);
}

QList<FileType> CellMLPlugin::fileTypes() const
{
    // Return the CellML file type that the CellML plugin supports

    return QList<FileType>() << FileType(qobject_cast<FileInterface *>(this),
                                         CellmlMimeType, "cellml");
}

QString CellMLPlugin::fileTypeDescription(const QString &mMimeType) const
{
    // Return the description for the requested Mime type, that is as long as it
    // is for the CellML Mime type

    if (!mMimeType.compare(CellmlMimeType))
        return tr("CellML File");
    else
        // Not a Mime type that we can recognise, so...

        return FileInterface::fileTypeDescription(mMimeType);
}

} }
<commit_msg>Got some code to run a CellML file. However, though it all works fine, it looks rather cumbersome to me.<commit_after>#include "cellmlplugin.h"
#include "coreutils.h"

#include "cellml-api-cxx-support.hpp"
#include "CellMLBootstrap.hpp"
#include "IfaceCellML_APISPEC.hxx"
#include "IfaceCIS.hxx"
#include "CISBootstrap.hpp"

#include <QDebug>
#include <QDir>
#include <QThread>
#include <QUrl>

namespace OpenCOR {
namespace CellML {

PLUGININFO_FUNC CellMLPluginInfo()
{
    Descriptions descriptions;

    descriptions.insert("en", "A plugin to use the CellML API");
    descriptions.insert("fr", "Une extension pour utiliser l'API CellML");

    return PluginInfo(PluginInfo::V001,
                      PluginInfo::General,
                      PluginInfo::Api,
                      false,
                      QStringList(),
                      descriptions);
}

Q_EXPORT_PLUGIN2(CellML, CellMLPlugin)

class SleeperThread : public QThread
{
public:
    static void msleep(unsigned long msecs)
    {
        QThread::msleep(msecs);
    }
};

class TestProgressObserver : public iface::cellml_services::IntegrationProgressObserver
{
public:
    TestProgressObserver(iface::cellml_services::CellMLCompiledModel *pCompiledModel) :
        mCompiledModel(pCompiledModel),
        mCodeInformation(pCompiledModel->codeInformation()),
        mRefCount(1),
        mFinished(false)
    {
        iface::cellml_services::ComputationTargetIterator *cti = mCodeInformation->iterateTargets();

        bool first = true;

        mHeader = QString();

        while (true) {
            iface::cellml_services::ComputationTarget* ct = cti->nextComputationTarget();

            if (!ct)
                break;

            if (     ((ct->type() == iface::cellml_services::STATE_VARIABLE)
                   || (ct->type() == iface::cellml_services::ALGEBRAIC)
                   || (ct->type() == iface::cellml_services::VARIABLE_OF_INTEGRATION))
                && (ct->degree() == 0)) {
                if (!first)
                    mHeader += ",";
                else
                    first = false;

                mHeader += QString::fromWCharArray(ct->variable()->name());
            }
        }
    }

    virtual void add_ref() throw(std::exception &)
    {
        ++mRefCount;
    }

    virtual void release_ref() throw(std::exception &)
    {
        --mRefCount;

        if (!mRefCount)
            delete this;
    }

    virtual char * objid() throw (std::exception &)
    {
        return strdup("singletonTestProgressObserver");
    }

    virtual void * query_interface(const char *pIface) throw (std::exception &)
    {
        if (!strcmp(pIface, "xpcom::IObject"))
            return static_cast<iface::XPCOM::IObject *>(this);
        else if (!strcmp(pIface, "cellml_services::IntegrationProgressObserver"))
            return static_cast<iface::cellml_services::IntegrationProgressObserver *> (this);

        return 0;
    }

    virtual void computedConstants(uint32_t, double *pValues) throw (std::exception &)
    {
        iface::cellml_services::ComputationTargetIterator *targetsIter = mCodeInformation->iterateTargets();

        qDebug("    Computed constants...");

        while (true) {
            iface::cellml_services::ComputationTarget *target = targetsIter->nextComputationTarget();

            if (!target)
                break;

            if (   (target->type() == iface::cellml_services::CONSTANT)
                && !target->degree())
                qDebug("        %s = %g",
                       QString::fromWCharArray(target->variable()->name()).toLatin1().constData(),
                       pValues[target->assignedIndex()]);
        }
    }

    virtual void results(uint32_t pNbOfValues, double *pValues) throw (std::exception&)
    {
        static bool needInit = true;

        if (needInit) {
            qDebug("    Results...");
            qDebug("        %s", mHeader.toLatin1().constData());

            needInit = false;
        }

        uint32_t rateIndexCount = mCodeInformation->rateIndexCount();
        uint32_t recSize =  2*rateIndexCount
                           +mCodeInformation->algebraicIndexCount()
                           +1;

        if (!recSize)
            return;

        for (uint32_t i = 0; i < pNbOfValues; i += recSize)
        {
            bool first = true;

            iface::cellml_services::ComputationTargetIterator *targetsIter = mCodeInformation->iterateTargets();

            QString values = QString();

            while (true)
            {
                iface::cellml_services::ComputationTarget *target = targetsIter->nextComputationTarget();

                if (!target)
                    break;

                if (target->degree())
                    continue;

                uint32_t varOff = 0;

                switch (target->type()) {
                case iface::cellml_services::STATE_VARIABLE:
                    varOff = 1+target->assignedIndex();

                    break;
                case iface::cellml_services::VARIABLE_OF_INTEGRATION:
                    varOff = 0;

                    break;
                case iface::cellml_services::ALGEBRAIC:
                    varOff = 1+2*rateIndexCount+target->assignedIndex();

                    break;
                default:
                    continue;
                }

                if (!first)
                    values += ",";
                else
                    first = false;

                values += QString::number(pValues[i+varOff]);
            }

            qDebug("        %s", values.toLatin1().constData());
        }
    }

    virtual void done() throw (std::exception &)
    {
        mFinished = true;
    }

    virtual void failed(const char *pErrorMsg) throw (std::exception &)
    {
        qDebug("ERROR: integration failed (%s).", pErrorMsg);

        mFinished = true;
    }

    bool finished()
    {
        return mFinished;
    }

private:
    iface::cellml_services::CellMLCompiledModel *mCompiledModel;
    iface::cellml_services::CodeInformation *mCodeInformation;

    uint32_t mRefCount;
    bool mFinished;

    QString mHeader;
};

void CellMLPlugin::initialize()
{
    // Fetch a bootstrap object

    iface::cellml_api::CellMLBootstrap *cbs = CreateCellMLBootstrap();

    // Retrieve the model loader

    iface::cellml_api::DOMModelLoader *ml = cbs->modelLoader();

    // Load our test CellML model and return its cmeta:id
    // Note: we do this within a try...catch statement since we might get an
    //       exception...

    QString testCellmlModelFileName = QDir::tempPath()+QDir::separator()+"test_cellml_model.cellml";

    Core::saveResourceAs(":test_cellml_model", testCellmlModelFileName);

    qDebug("Loading the model...");

    iface::cellml_api::Model *model;

    try {
        model = ml->loadFromURL(QUrl::fromLocalFile(testCellmlModelFileName).toString().toStdWString().c_str());
    } catch (iface::cellml_api::CellMLException &) {
        qDebug("ERROR: %s.",
               QString::fromWCharArray(ml->lastErrorMessage()).toLatin1().constData());

        return;
    }

    qDebug();
    qDebug("Retrieving some model properties...");

    // Retrieve the model's name

    qDebug("    Model '%s'",
           QString::fromWCharArray(model->name()).toLatin1().constData());

    // Go through the model's components and their respective variables

    iface::cellml_api::CellMLComponentSet *comps = model->modelComponents();
    iface::cellml_api::CellMLComponentIterator *compsIter = comps->iterateComponents();

    for (int i = 0; i < comps->length(); i++) {
        iface::cellml_api::CellMLComponent *comp = compsIter->nextComponent();

        qDebug("        Component '%s'",
               QString::fromWCharArray(comp->name()).toLatin1().constData());

        iface::cellml_api::CellMLVariableSet *vars = comp->variables();
        iface::cellml_api::CellMLVariableIterator *varsIter = vars->iterateVariables();

        for(int j = 0; j < vars->length(); ++j)
            qDebug("            Variable '%s'",
                   QString::fromStdWString(varsIter->nextVariable()->name()).toLatin1().constData());
    }

    // Go through the model's connections and information

    iface::cellml_api::ConnectionSet *conns = model->connections();
    iface::cellml_api::ConnectionIterator *connsIter = conns->iterateConnections();

    for (int i = 0; i < conns->length(); i++) {
        iface::cellml_api::Connection *conn = connsIter->nextConnection();
        iface::cellml_api::MapComponents *compMap = conn->componentMapping();

        qDebug("        Connection between '%s' and '%s'",
               QString::fromStdWString(compMap->firstComponentName()).toLatin1().constData(),
               QString::fromStdWString(compMap->secondComponentName()).toLatin1().constData());

        iface::cellml_api::MapVariablesSet *varMaps = conn->variableMappings();
        iface::cellml_api::MapVariablesIterator *varMapsIter = varMaps->iterateMapVariables();

        for(int j = 0; j < varMaps->length(); ++j) {
            iface::cellml_api::MapVariables *mapVars = varMapsIter->nextMapVariables();

            qDebug("            For variables '%s' and '%s'",
                   QString::fromStdWString(mapVars->firstVariableName()).toLatin1().constData(),
                   QString::fromStdWString(mapVars->secondVariableName()).toLatin1().constData());
        }
    }

    // Run the model

    qDebug();
    qDebug("Running the model...");

    iface::cellml_services::CellMLIntegrationService* cis = CreateIntegrationService();
    iface::cellml_services::ODESolverCompiledModel* compiledModel = 0;

    qDebug("    Compiling the model...");

    try
    {
        compiledModel = cis->compileModelODE(model);
    } catch (iface::cellml_api::CellMLException &) {
        qDebug("ERROR: %s.",
               QString::fromWCharArray(cis->lastError()).toLatin1().constData());

        return;
    } catch (...) {
        qDebug("ERROR: unexpected exception calling compileModel.");

        return;
    }

    qDebug("    Creating the ODE solver run...");

    iface::cellml_services::ODESolverRun *odeSolverRun = cis->createODEIntegrationRun(compiledModel);

    TestProgressObserver* progressObserver = new TestProgressObserver(compiledModel);

    odeSolverRun->setProgressObserver(progressObserver);

    odeSolverRun->stepType(iface::cellml_services::BDF_IMPLICIT_1_5_SOLVE);
    odeSolverRun->setResultRange(0, 50, 0);
    odeSolverRun->setTabulationStepControl(1, true);

    odeSolverRun->start();

    while (!progressObserver->finished())
        SleeperThread::msleep(1000);

    // All done...

    qDebug();
    qDebug("All done...");

    QFile::remove(testCellmlModelFileName);
}

QList<FileType> CellMLPlugin::fileTypes() const
{
    // Return the CellML file type that the CellML plugin supports

    return QList<FileType>() << FileType(qobject_cast<FileInterface *>(this),
                                         CellmlMimeType, "cellml");
}

QString CellMLPlugin::fileTypeDescription(const QString &mMimeType) const
{
    // Return the description for the requested Mime type, that is as long as it
    // is for the CellML Mime type

    if (!mMimeType.compare(CellmlMimeType))
        return tr("CellML File");
    else
        // Not a Mime type that we can recognise, so...

        return FileInterface::fileTypeDescription(mMimeType);
}

} }
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "basetextdocument.h"

#include "basetextdocumentlayout.h"
#include "basetexteditor.h"
#include "storagesettings.h"

#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QTextStream>
#include <QtCore/QTextCodec>
#include <QtGui/QMainWindow>
#include <QtGui/QSyntaxHighlighter>
#include <QtGui/QApplication>

#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <utils/reloadpromptutils.h>

using namespace TextEditor;

DocumentMarker::DocumentMarker(QTextDocument *doc)
  : ITextMarkable(doc), document(doc)
{
}

bool DocumentMarker::addMark(TextEditor::ITextMark *mark, int line)
{
    QTC_ASSERT(line >= 1, return false);
    int blockNumber = line - 1;
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(document->documentLayout());
    QTC_ASSERT(documentLayout, return false);
    QTextBlock block = document->findBlockByNumber(blockNumber);

    if (block.isValid()) {
        TextBlockUserData *userData = BaseTextDocumentLayout::userData(block);
        userData->addMark(mark);
        mark->updateLineNumber(blockNumber + 1);
        mark->updateBlock(block);
        documentLayout->hasMarks = true;
        documentLayout->requestUpdate();
        return true;
    }
    return false;
}

TextEditor::TextMarks DocumentMarker::marksAt(int line) const
{
    QTC_ASSERT(line >= 1, return TextMarks());
    int blockNumber = line - 1;
    QTextBlock block = document->findBlockByNumber(blockNumber);

    if (block.isValid()) {
        if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(block))
            return userData->marks();
    }
    return TextMarks();
}

void DocumentMarker::removeMark(TextEditor::ITextMark *mark)
{
    bool needUpdate = false;
    QTextBlock block = document->begin();
    while (block.isValid()) {
        if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) {
            needUpdate |= data->removeMark(mark);
        }
        block = block.next();
    }
    if (needUpdate)
        updateMark(0);
}

bool DocumentMarker::hasMark(TextEditor::ITextMark *mark) const
{
    QTextBlock block = document->begin();
    while (block.isValid()) {
        if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) {
            if (data->hasMark(mark))
                return true;
        }
        block = block.next();
    }
    return false;
}

void DocumentMarker::updateMark(ITextMark *mark)
{
    Q_UNUSED(mark)
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(document->documentLayout());
    QTC_ASSERT(documentLayout, return);
    documentLayout->requestUpdate();
}

BaseTextDocument::BaseTextDocument()
  : m_document(new QTextDocument(this)),
    m_highlighter(0)
{
    m_documentMarker = new DocumentMarker(m_document);
    m_lineTerminatorMode = NativeLineTerminator;
    m_fileIsReadOnly = false;
    m_isBinaryData = false;
    m_codec = QTextCodec::codecForLocale();
    QSettings *settings = Core::ICore::instance()->settings();
    if (QTextCodec *candidate = QTextCodec::codecForName(
            settings->value(QLatin1String("General/DefaultFileEncoding")).toByteArray()))
        m_codec = candidate;

    m_hasDecodingError = false;
}

BaseTextDocument::~BaseTextDocument()
{
    documentClosing();

    delete m_document;
    m_document = 0;
}

QString BaseTextDocument::mimeType() const
{
    return m_mimeType;
}

void BaseTextDocument::setMimeType(const QString &mt)
{
    m_mimeType = mt;
}

bool BaseTextDocument::save(const QString &fileName)
{
    QTextCursor cursor(m_document);

    cursor.beginEditBlock();
    if (m_storageSettings.m_cleanWhitespace)
        cleanWhitespace(cursor, m_storageSettings.m_cleanIndentation, m_storageSettings.m_inEntireDocument);
    if (m_storageSettings.m_addFinalNewLine)
        ensureFinalNewLine(cursor);
    cursor.endEditBlock();

    QString fName = m_fileName;
    if (!fileName.isEmpty())
        fName = fileName;

    QFile file(fName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
        return false;

    QString plainText = m_document->toPlainText();

    if (m_lineTerminatorMode == CRLFLineTerminator)
        plainText.replace(QLatin1Char('\n'), QLatin1String("\r\n"));

    file.write(m_codec->fromUnicode(plainText));
    if (!file.flush())
        return false;
    file.close();

    const QFileInfo fi(fName);
    m_fileName = QDir::cleanPath(fi.absoluteFilePath());

    m_document->setModified(false);
    emit titleChanged(fi.fileName());
    emit changed();

    m_isBinaryData = false;
    m_hasDecodingError = false;
    m_decodingErrorSample.clear();

    return true;
}

bool BaseTextDocument::isReadOnly() const
{
    if (m_isBinaryData || m_hasDecodingError)
        return true;
    if (m_fileName.isEmpty()) //have no corresponding file, so editing is ok
        return false;
    return m_fileIsReadOnly;
}

bool BaseTextDocument::isModified() const
{
    return m_document->isModified();
}

void BaseTextDocument::checkPermissions()
{
    bool previousReadOnly = m_fileIsReadOnly;
    if (!m_fileName.isEmpty()) {
        const QFileInfo fi(m_fileName);
        m_fileIsReadOnly = !fi.isWritable();
    } else {
        m_fileIsReadOnly = false;
    }
    if (previousReadOnly != m_fileIsReadOnly)
        emit changed();
}

bool BaseTextDocument::open(const QString &fileName)
{
    QString title = tr("untitled");
    if (!fileName.isEmpty()) {
        const QFileInfo fi(fileName);
        m_fileIsReadOnly = !fi.isWritable();
        m_fileName = QDir::cleanPath(fi.absoluteFilePath());

        QFile file(fileName);
        if (!file.open(QIODevice::ReadOnly))
            return false;

        title = fi.fileName();

        QByteArray buf = file.readAll();
        int bytesRead = buf.size();

        QTextCodec *codec = m_codec;

        // code taken from qtextstream
        if (bytesRead >= 4 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe && uchar(buf[2]) == 0 && uchar(buf[3]) == 0)
                               || (uchar(buf[0]) == 0 && uchar(buf[1]) == 0 && uchar(buf[2]) == 0xfe && uchar(buf[3]) == 0xff))) {
            codec = QTextCodec::codecForName("UTF-32");
        } else if (bytesRead >= 2 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe)
                                      || (uchar(buf[0]) == 0xfe && uchar(buf[1]) == 0xff))) {
            codec = QTextCodec::codecForName("UTF-16");
        } else if (!codec) {
            codec = QTextCodec::codecForLocale();
        }
        // end code taken from qtextstream

        m_codec = codec;

#if 0 // should work, but does not, Qt bug with "system" codec
        QTextDecoder *decoder = m_codec->makeDecoder();
        QString text = decoder->toUnicode(buf);
        m_hasDecodingError = (decoder->hasFailure());
        delete decoder;
#else
        QString text = m_codec->toUnicode(buf);
        QByteArray verifyBuf = m_codec->fromUnicode(text); // slow
        // the minSize trick lets us ignore unicode headers
        int minSize = qMin(verifyBuf.size(), buf.size());
        m_hasDecodingError = (minSize < buf.size()- 4
                              || memcmp(verifyBuf.constData() + verifyBuf.size() - minSize,
                                        buf.constData() + buf.size() - minSize, minSize));
#endif

        if (m_hasDecodingError) {
            int p = buf.indexOf('\n', 16384);
            if (p < 0)
                m_decodingErrorSample = buf;
            else
                m_decodingErrorSample = buf.left(p);
        } else {
            m_decodingErrorSample.clear();
        }

        int lf = text.indexOf('\n');
        if (lf > 0 && text.at(lf-1) == QLatin1Char('\r')) {
            m_lineTerminatorMode = CRLFLineTerminator;
        } else if (lf >= 0) {
            m_lineTerminatorMode = LFLineTerminator;
        } else {
            m_lineTerminatorMode = NativeLineTerminator;
        }

        m_document->setModified(false);
        if (m_isBinaryData)
            m_document->setHtml(tr("Binary data"));
        else
            m_document->setPlainText(text);
        BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(m_document->documentLayout());
        QTC_ASSERT(documentLayout, return true);
        documentLayout->lastSaveRevision = m_document->revision();
        m_document->setModified(false);
        emit titleChanged(title);
        emit changed();
    }
    return true;
}

void BaseTextDocument::reload(QTextCodec *codec)
{
    QTC_ASSERT(codec, return);
    m_codec = codec;
    reload();
}

void BaseTextDocument::reload()
{
    emit aboutToReload();
    documentClosing(); // removes text marks non-permanently

    if (open(m_fileName))
        emit reloaded();
}

Core::IFile::ReloadBehavior BaseTextDocument::reloadBehavior(ChangeTrigger state, ChangeType type) const
{
    if (type == TypePermissions)
        return BehaviorSilent;
    if (type == TypeContents) {
        if (state == TriggerInternal && !isModified())
            return BehaviorSilent;
        return BehaviorAsk;
    }
    return BehaviorAsk;
}

void BaseTextDocument::reload(ReloadFlag flag, ChangeType type)
{
    if (flag == FlagIgnore)
        return;
    if (type == TypePermissions) {
        checkPermissions();
    } else {
        reload();
    }
}

void BaseTextDocument::setSyntaxHighlighter(QSyntaxHighlighter *highlighter)
{
    if (m_highlighter)
        delete m_highlighter;
    m_highlighter = highlighter;
    m_highlighter->setParent(this);
    m_highlighter->setDocument(m_document);
}

void BaseTextDocument::cleanWhitespace(const QTextCursor &cursor)
{
    bool hasSelection = cursor.hasSelection();
    QTextCursor copyCursor = cursor;
    copyCursor.beginEditBlock();
    cleanWhitespace(copyCursor, true, true);
    if (!hasSelection)
        ensureFinalNewLine(copyCursor);
    copyCursor.endEditBlock();
}

void BaseTextDocument::cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, bool inEntireDocument)
{
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(m_document->documentLayout());

    QTextBlock block = m_document->findBlock(cursor.selectionStart());
    QTextBlock end;
    if (cursor.hasSelection())
        end = m_document->findBlock(cursor.selectionEnd()-1).next();

    while (block.isValid() && block != end) {

        if (inEntireDocument || block.revision() != documentLayout->lastSaveRevision) {

            QString blockText = block.text();
            if (int trailing = m_tabSettings.trailingWhitespaces(blockText)) {
                cursor.setPosition(block.position() + block.length() - 1);
                cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, trailing);
                cursor.removeSelectedText();
            }
            if (cleanIndentation && !m_tabSettings.isIndentationClean(block)) {
                cursor.setPosition(block.position());
                int firstNonSpace = m_tabSettings.firstNonSpace(blockText);
                if (firstNonSpace == blockText.length()) {
                    cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
                    cursor.removeSelectedText();
                } else {
                    int column = m_tabSettings.columnAt(blockText, firstNonSpace);
                    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace);
                    QString indentationString = m_tabSettings.indentationString(0, column, block);
                    cursor.insertText(indentationString);
                }
            }
        }

        block = block.next();
    }
}

void BaseTextDocument::ensureFinalNewLine(QTextCursor& cursor)
{
    cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
    bool emptyFile = !cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);

    if (!emptyFile && cursor.selectedText().at(0) != QChar::ParagraphSeparator)
    {
        cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
        cursor.insertText(QLatin1String("\n"));
    }
}

void BaseTextDocument::documentClosing()
{
    QTextBlock block = m_document->begin();
    while (block.isValid()) {
        if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData()))
            data->documentClosing();
        block = block.next();
    }
}
<commit_msg>Maintain the right cursor when undoing "clean whitespace" after saving<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "basetextdocument.h"

#include "basetextdocumentlayout.h"
#include "basetexteditor.h"
#include "storagesettings.h"

#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QTextStream>
#include <QtCore/QTextCodec>
#include <QtGui/QMainWindow>
#include <QtGui/QSyntaxHighlighter>
#include <QtGui/QApplication>

#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <utils/reloadpromptutils.h>

using namespace TextEditor;

DocumentMarker::DocumentMarker(QTextDocument *doc)
  : ITextMarkable(doc), document(doc)
{
}

bool DocumentMarker::addMark(TextEditor::ITextMark *mark, int line)
{
    QTC_ASSERT(line >= 1, return false);
    int blockNumber = line - 1;
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(document->documentLayout());
    QTC_ASSERT(documentLayout, return false);
    QTextBlock block = document->findBlockByNumber(blockNumber);

    if (block.isValid()) {
        TextBlockUserData *userData = BaseTextDocumentLayout::userData(block);
        userData->addMark(mark);
        mark->updateLineNumber(blockNumber + 1);
        mark->updateBlock(block);
        documentLayout->hasMarks = true;
        documentLayout->requestUpdate();
        return true;
    }
    return false;
}

TextEditor::TextMarks DocumentMarker::marksAt(int line) const
{
    QTC_ASSERT(line >= 1, return TextMarks());
    int blockNumber = line - 1;
    QTextBlock block = document->findBlockByNumber(blockNumber);

    if (block.isValid()) {
        if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(block))
            return userData->marks();
    }
    return TextMarks();
}

void DocumentMarker::removeMark(TextEditor::ITextMark *mark)
{
    bool needUpdate = false;
    QTextBlock block = document->begin();
    while (block.isValid()) {
        if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) {
            needUpdate |= data->removeMark(mark);
        }
        block = block.next();
    }
    if (needUpdate)
        updateMark(0);
}

bool DocumentMarker::hasMark(TextEditor::ITextMark *mark) const
{
    QTextBlock block = document->begin();
    while (block.isValid()) {
        if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) {
            if (data->hasMark(mark))
                return true;
        }
        block = block.next();
    }
    return false;
}

void DocumentMarker::updateMark(ITextMark *mark)
{
    Q_UNUSED(mark)
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(document->documentLayout());
    QTC_ASSERT(documentLayout, return);
    documentLayout->requestUpdate();
}

BaseTextDocument::BaseTextDocument()
  : m_document(new QTextDocument(this)),
    m_highlighter(0)
{
    m_documentMarker = new DocumentMarker(m_document);
    m_lineTerminatorMode = NativeLineTerminator;
    m_fileIsReadOnly = false;
    m_isBinaryData = false;
    m_codec = QTextCodec::codecForLocale();
    QSettings *settings = Core::ICore::instance()->settings();
    if (QTextCodec *candidate = QTextCodec::codecForName(
            settings->value(QLatin1String("General/DefaultFileEncoding")).toByteArray()))
        m_codec = candidate;

    m_hasDecodingError = false;
}

BaseTextDocument::~BaseTextDocument()
{
    documentClosing();

    delete m_document;
    m_document = 0;
}

QString BaseTextDocument::mimeType() const
{
    return m_mimeType;
}

void BaseTextDocument::setMimeType(const QString &mt)
{
    m_mimeType = mt;
}

bool BaseTextDocument::save(const QString &fileName)
{
    QTextCursor cursor(m_document);

    // When saving the current editor, make sure to maintain the cursor position for undo
    Core::IEditor *currentEditor = Core::EditorManager::instance()->currentEditor();
    if (BaseTextEditorEditable *editable = qobject_cast<BaseTextEditorEditable*>(currentEditor)) {
        if (editable->file() == this)
            cursor = editable->editor()->textCursor();
    }

    cursor.beginEditBlock();
    cursor.movePosition(QTextCursor::Start);

    if (m_storageSettings.m_cleanWhitespace)
        cleanWhitespace(cursor, m_storageSettings.m_cleanIndentation, m_storageSettings.m_inEntireDocument);
    if (m_storageSettings.m_addFinalNewLine)
        ensureFinalNewLine(cursor);
    cursor.endEditBlock();

    QString fName = m_fileName;
    if (!fileName.isEmpty())
        fName = fileName;

    QFile file(fName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
        return false;

    QString plainText = m_document->toPlainText();

    if (m_lineTerminatorMode == CRLFLineTerminator)
        plainText.replace(QLatin1Char('\n'), QLatin1String("\r\n"));

    file.write(m_codec->fromUnicode(plainText));
    if (!file.flush())
        return false;
    file.close();

    const QFileInfo fi(fName);
    m_fileName = QDir::cleanPath(fi.absoluteFilePath());

    m_document->setModified(false);
    emit titleChanged(fi.fileName());
    emit changed();

    m_isBinaryData = false;
    m_hasDecodingError = false;
    m_decodingErrorSample.clear();

    return true;
}

bool BaseTextDocument::isReadOnly() const
{
    if (m_isBinaryData || m_hasDecodingError)
        return true;
    if (m_fileName.isEmpty()) //have no corresponding file, so editing is ok
        return false;
    return m_fileIsReadOnly;
}

bool BaseTextDocument::isModified() const
{
    return m_document->isModified();
}

void BaseTextDocument::checkPermissions()
{
    bool previousReadOnly = m_fileIsReadOnly;
    if (!m_fileName.isEmpty()) {
        const QFileInfo fi(m_fileName);
        m_fileIsReadOnly = !fi.isWritable();
    } else {
        m_fileIsReadOnly = false;
    }
    if (previousReadOnly != m_fileIsReadOnly)
        emit changed();
}

bool BaseTextDocument::open(const QString &fileName)
{
    QString title = tr("untitled");
    if (!fileName.isEmpty()) {
        const QFileInfo fi(fileName);
        m_fileIsReadOnly = !fi.isWritable();
        m_fileName = QDir::cleanPath(fi.absoluteFilePath());

        QFile file(fileName);
        if (!file.open(QIODevice::ReadOnly))
            return false;

        title = fi.fileName();

        QByteArray buf = file.readAll();
        int bytesRead = buf.size();

        QTextCodec *codec = m_codec;

        // code taken from qtextstream
        if (bytesRead >= 4 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe && uchar(buf[2]) == 0 && uchar(buf[3]) == 0)
                               || (uchar(buf[0]) == 0 && uchar(buf[1]) == 0 && uchar(buf[2]) == 0xfe && uchar(buf[3]) == 0xff))) {
            codec = QTextCodec::codecForName("UTF-32");
        } else if (bytesRead >= 2 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe)
                                      || (uchar(buf[0]) == 0xfe && uchar(buf[1]) == 0xff))) {
            codec = QTextCodec::codecForName("UTF-16");
        } else if (!codec) {
            codec = QTextCodec::codecForLocale();
        }
        // end code taken from qtextstream

        m_codec = codec;

#if 0 // should work, but does not, Qt bug with "system" codec
        QTextDecoder *decoder = m_codec->makeDecoder();
        QString text = decoder->toUnicode(buf);
        m_hasDecodingError = (decoder->hasFailure());
        delete decoder;
#else
        QString text = m_codec->toUnicode(buf);
        QByteArray verifyBuf = m_codec->fromUnicode(text); // slow
        // the minSize trick lets us ignore unicode headers
        int minSize = qMin(verifyBuf.size(), buf.size());
        m_hasDecodingError = (minSize < buf.size()- 4
                              || memcmp(verifyBuf.constData() + verifyBuf.size() - minSize,
                                        buf.constData() + buf.size() - minSize, minSize));
#endif

        if (m_hasDecodingError) {
            int p = buf.indexOf('\n', 16384);
            if (p < 0)
                m_decodingErrorSample = buf;
            else
                m_decodingErrorSample = buf.left(p);
        } else {
            m_decodingErrorSample.clear();
        }

        int lf = text.indexOf('\n');
        if (lf > 0 && text.at(lf-1) == QLatin1Char('\r')) {
            m_lineTerminatorMode = CRLFLineTerminator;
        } else if (lf >= 0) {
            m_lineTerminatorMode = LFLineTerminator;
        } else {
            m_lineTerminatorMode = NativeLineTerminator;
        }

        m_document->setModified(false);
        if (m_isBinaryData)
            m_document->setHtml(tr("Binary data"));
        else
            m_document->setPlainText(text);
        BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(m_document->documentLayout());
        QTC_ASSERT(documentLayout, return true);
        documentLayout->lastSaveRevision = m_document->revision();
        m_document->setModified(false);
        emit titleChanged(title);
        emit changed();
    }
    return true;
}

void BaseTextDocument::reload(QTextCodec *codec)
{
    QTC_ASSERT(codec, return);
    m_codec = codec;
    reload();
}

void BaseTextDocument::reload()
{
    emit aboutToReload();
    documentClosing(); // removes text marks non-permanently

    if (open(m_fileName))
        emit reloaded();
}

Core::IFile::ReloadBehavior BaseTextDocument::reloadBehavior(ChangeTrigger state, ChangeType type) const
{
    if (type == TypePermissions)
        return BehaviorSilent;
    if (type == TypeContents) {
        if (state == TriggerInternal && !isModified())
            return BehaviorSilent;
        return BehaviorAsk;
    }
    return BehaviorAsk;
}

void BaseTextDocument::reload(ReloadFlag flag, ChangeType type)
{
    if (flag == FlagIgnore)
        return;
    if (type == TypePermissions) {
        checkPermissions();
    } else {
        reload();
    }
}

void BaseTextDocument::setSyntaxHighlighter(QSyntaxHighlighter *highlighter)
{
    if (m_highlighter)
        delete m_highlighter;
    m_highlighter = highlighter;
    m_highlighter->setParent(this);
    m_highlighter->setDocument(m_document);
}

void BaseTextDocument::cleanWhitespace(const QTextCursor &cursor)
{
    bool hasSelection = cursor.hasSelection();
    QTextCursor copyCursor = cursor;
    copyCursor.beginEditBlock();
    cleanWhitespace(copyCursor, true, true);
    if (!hasSelection)
        ensureFinalNewLine(copyCursor);
    copyCursor.endEditBlock();
}

void BaseTextDocument::cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, bool inEntireDocument)
{
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(m_document->documentLayout());

    QTextBlock block = m_document->findBlock(cursor.selectionStart());
    QTextBlock end;
    if (cursor.hasSelection())
        end = m_document->findBlock(cursor.selectionEnd()-1).next();

    while (block.isValid() && block != end) {

        if (inEntireDocument || block.revision() != documentLayout->lastSaveRevision) {

            QString blockText = block.text();
            if (int trailing = m_tabSettings.trailingWhitespaces(blockText)) {
                cursor.setPosition(block.position() + block.length() - 1);
                cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, trailing);
                cursor.removeSelectedText();
            }
            if (cleanIndentation && !m_tabSettings.isIndentationClean(block)) {
                cursor.setPosition(block.position());
                int firstNonSpace = m_tabSettings.firstNonSpace(blockText);
                if (firstNonSpace == blockText.length()) {
                    cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
                    cursor.removeSelectedText();
                } else {
                    int column = m_tabSettings.columnAt(blockText, firstNonSpace);
                    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace);
                    QString indentationString = m_tabSettings.indentationString(0, column, block);
                    cursor.insertText(indentationString);
                }
            }
        }

        block = block.next();
    }
}

void BaseTextDocument::ensureFinalNewLine(QTextCursor& cursor)
{
    cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
    bool emptyFile = !cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);

    if (!emptyFile && cursor.selectedText().at(0) != QChar::ParagraphSeparator)
    {
        cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
        cursor.insertText(QLatin1String("\n"));
    }
}

void BaseTextDocument::documentClosing()
{
    QTextBlock block = m_document->begin();
    while (block.isValid()) {
        if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData()))
            data->documentClosing();
        block = block.next();
    }
}
<|endoftext|>


<commit_before>#ifndef CONST_NODE_HPP
#define CONST_NODE_HPP
#include <core/deterministic_node.hpp>
#include <helper/vnode.hpp>
#include <helper/node_counter.hpp>
#include <helper/abstract_node_factory.hpp>
#include <string>

namespace mcmc_utilities
{
  template <typename T>
  class const_node
    :public deterministic_node<T>
  {
  private:
    T v;
  public:
    const_node(T v1)
      :deterministic_node<T>(0,1),v(v1)
    {
    }

    T do_calc(size_t idx,const std::vector<T>&)const override
    {
      return v;
    }

    std::shared_ptr<node<T> > do_clone()const override
    {
      return std::shared_ptr<node<T> >(new const_node(v));
    }
  };

  template <typename T>
  class const_vnode
    :public vnode<T>
  {
  public:
    T value;
  public:
    const_vnode(std::string n,T v)
      :vnode<T>("const",n),value(v)
    {
      this->binded=true;
    }

  public:
    std::shared_ptr<node<T> > get_node()const override
    {
      return std::shared_ptr<node<T> >(new const_node<T>(value));
    }

    std::shared_ptr<vnode<T> > clone()const override
    {
      return std::shared_ptr<vnode<T> >(new const_vnode<T>(*this));
    }
  };

  template <typename T>
  class const_node_factory
    :public abstract_node_factory<T>
  {
  public:
    const_node_factory()
      :abstract_node_factory<T>({},{"v"},{"value"})
    {}

  public:
    std::shared_ptr<node<T> >
    do_get_node(const std::vector<T>& hparam)const override
    {
      return std::shared_ptr<node<T> >(new const_node<T>(hparam[0]));
    }

    std::string do_get_node_type()const override
    {
      return std::string("deterministic");
    }
  };

  using vconst=const_vnode<double>;
};

#endif
<commit_msg>make const_node able to be set new value<commit_after>#ifndef CONST_NODE_HPP
#define CONST_NODE_HPP
#include <core/deterministic_node.hpp>
#include <helper/vnode.hpp>
#include <helper/node_counter.hpp>
#include <helper/abstract_node_factory.hpp>
#include <string>

namespace mcmc_utilities
{
  template <typename T>
  class const_node
    :public deterministic_node<T>
  {
  private:
    T v;
  public:
    const_node(T v1)
      :deterministic_node<T>(0,1),v(v1)
    {
    }

    T do_calc(size_t idx,const std::vector<T>&)const override
    {
      return v;
    }

    std::shared_ptr<node<T> > do_clone()const override
    {
      return std::shared_ptr<node<T> >(new const_node(v));
    }

    void set_value(T v1)
    {
      v=v1;
    }
  };

  template <typename T>
  class const_vnode
    :public vnode<T>
  {
  public:
    T value;
  public:
    const_vnode(std::string n,T v)
      :vnode<T>("const",n),value(v)
    {
      this->binded=true;
    }

  public:
    std::shared_ptr<node<T> > get_node()const override
    {
      return std::shared_ptr<node<T> >(new const_node<T>(value));
    }

    std::shared_ptr<vnode<T> > clone()const override
    {
      return std::shared_ptr<vnode<T> >(new const_vnode<T>(*this));
    }
  };

  template <typename T>
  class const_node_factory
    :public abstract_node_factory<T>
  {
  public:
    const_node_factory()
      :abstract_node_factory<T>({},{"v"},{"value"})
    {}

  public:
    std::shared_ptr<node<T> >
    do_get_node(const std::vector<T>& hparam)const override
    {
      return std::shared_ptr<node<T> >(new const_node<T>(hparam[0]));
    }

    std::string do_get_node_type()const override
    {
      return std::string("deterministic");
    }
  };

  using vconst=const_vnode<double>;
};

#endif
<|endoftext|>


<commit_before>#include "QsLog.h"
#include "configuration.h"
#include "ParamCompareDialog.h"
#include "ui_ParamCompareDialog.h"
#include <QMessageBox>
#include <QTableWidget>
#include <QDesktopServices>
#include <QDir>
#include <QFileDialog>
#include <QCheckBox>
#include <QPushButton>
#include <QTimer>

#define PCD_COLUMN_PARAM_NAME 0
#define PCD_COLUMN_VALUE 1
#define PCD_COLUMN_NEW_VALUE 2
#define PCD_COLUMN_CHECKBOX 3

ParamCompareDialog::ParamCompareDialog(QMap<QString, UASParameter* >& paramaterList,
                                       const QString& filename, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ParamCompareDialog),
    m_currentList(&paramaterList),
    m_newList(new QMap<QString, UASParameter*>()),
    m_fileToCompare(filename)
{
    ui->setupUi(this);

    QStringList headerList;
    headerList << tr("Parameter") << tr("Value") << tr("New Value") << tr("Use");

    QTableWidget* table = ui->compareTableWidget;
    table->setColumnCount(headerList.count());
    table->setHorizontalHeaderLabels(headerList);
    table->setSelectionBehavior(QAbstractItemView::SelectRows);
    table->setAlternatingRowColors(true);
    table->setColumnWidth(PCD_COLUMN_CHECKBOX, 40);

    initConnections();

    if(filename.count()>0){
        QTimer::singleShot(200, this, SLOT(loadParameterWithFile()));
    }

}

ParamCompareDialog::~ParamCompareDialog()
{
    delete ui;
}

void ParamCompareDialog::initConnections()
{
    connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
    connect(ui->loadButton, SIGNAL(clicked()), this, SLOT(loadParameterFile()));
    connect(ui->continueButton, SIGNAL(clicked()), this, SLOT(saveNewParameters()));
    connect(ui->checkAllBox, SIGNAL(clicked()), this, SLOT(checkAll()));

}

void ParamCompareDialog::setAcceptButtonLabel(const QString &label)
{
    if (ui) ui->continueButton->setText(label);
}

void ParamCompareDialog::loadParameterFile()
{
    ui->compareTableWidget->setRowCount(0);

    QDir parameterDir = QDir(QGC::parameterDirectory());

    if(!parameterDir.exists())
        parameterDir.mkdir(parameterDir.path());

    QString filename = QFileDialog::getOpenFileName(this,tr("Open File To Compare"),
                                                    QGC::parameterDirectory(), "*.param");
    QApplication::processEvents(); // Helps clear dialog from screen

    if(filename.length() == 0) {
        return;
    }
    QApplication::processEvents(); // Helps clear dialog from screen

    loadParameterFile(filename);
}

void ParamCompareDialog::loadParameterWithFile()
{
    ui->loadButton->hide();
    loadParameterFile(m_fileToCompare);
}

void ParamCompareDialog::loadParameterFile(const QString &filename)
{
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly))
    {
        QMessageBox::information(this,"Error",tr("Unable to open the file.").arg(filename));
        return;
    }

    QString filestring = file.readAll();
    file.close();

    populateParamListFromString(filestring, m_newList, this);

    compareLists();
}

void ParamCompareDialog::populateParamListFromString(QString paramString, QMap<QString, UASParameter*>* list,
                                                     QWidget* widget = NULL)
{
    QStringList paramSplit = paramString.split("\n");
    bool summaryComplete = false;
    bool summaryShown = false;
    QString summaryText;

    foreach (QString paramLine, paramSplit) {
        if (!paramLine.startsWith("#")) {
            summaryComplete = true;
            QStringList lineSplit = paramLine.split(",");
            if (lineSplit.size() == 2)
            {
                bool ok;
                QLOG_DEBUG() << "load param: " << lineSplit[0] << "=" << lineSplit[1];
                UASParameter* param = new UASParameter();
                param->setName(lineSplit[0]);

                double value = lineSplit[1].toFloat(&ok);
                if (ok){
                    param->setValue(QVariant(value));
                } else {
                    QLOG_ERROR() << "Conversion Failure";
                    param->setValue(QVariant("NaN"));
                }

                list->insert(param->name(), param);

            }
        } else {
            QLOG_DEBUG() << "Comment: " << paramLine;
            if (!summaryShown && !summaryComplete){
                // removes the '#' and any whites space before or after
                summaryText.append(paramLine.remove(0,1).trimmed() + "\n");
            }
        }
        if (!summaryShown && summaryComplete){
            QLOG_DEBUG() << "Show Summary: " << summaryText;
            if (summaryText.count()>0){
                QMessageBox::information(widget,tr("Param File Summary"),summaryText,QMessageBox::Ok);
            }
            summaryShown = true;
        }
    }
}

void ParamCompareDialog::compareLists()
{
    QList<QString> keys = m_newList->keys(); // This needs to be an amalgamated list of all keys

    ui->compareTableWidget->setSortingEnabled(false);
    for(int count = 0; count < keys.count(); ++count){

        UASParameter* currentParam = m_currentList->value(keys[count]);

        if (currentParam != NULL){
            UASParameter* newParam = m_newList->value(keys[count]);

            if (currentParam->value() != newParam->value() ){
                QLOG_DEBUG() << "Difference : " << currentParam->name()
                             << " current: " << currentParam->value() << " new:" << newParam->value();

                int rowCount = ui->compareTableWidget->rowCount();
                ui->compareTableWidget->setRowCount(ui->compareTableWidget->rowCount()+1);

                QTableWidgetItem* widgetItemParam = new QTableWidgetItem(keys[count]);
                widgetItemParam->setFlags(Qt::NoItemFlags | Qt::ItemIsEnabled );
                ui->compareTableWidget->setItem(rowCount, PCD_COLUMN_PARAM_NAME, widgetItemParam);

                QTableWidgetItem* widgetItemValue = new QTableWidgetItem();
                widgetItemValue->setData(Qt::DisplayRole, currentParam->value());
                widgetItemValue->setFlags(Qt::NoItemFlags  | Qt::ItemIsEnabled);
                ui->compareTableWidget->setItem(rowCount, PCD_COLUMN_VALUE, widgetItemValue);

                QTableWidgetItem* widgetItemNewValue = new QTableWidgetItem();
                widgetItemNewValue->setData(Qt::DisplayRole, newParam->value());
                widgetItemNewValue->setFlags(Qt::NoItemFlags | Qt::ItemIsEnabled | Qt::ItemIsEditable);
                ui->compareTableWidget->setItem(rowCount, PCD_COLUMN_NEW_VALUE, widgetItemNewValue);

                QTableWidgetItem* widgetItemCheckbox= new QTableWidgetItem();
                widgetItemCheckbox->setFlags(Qt::NoItemFlags | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
                widgetItemCheckbox->setCheckState(Qt::Checked);
                ui->compareTableWidget->setItem(rowCount, PCD_COLUMN_CHECKBOX, widgetItemCheckbox);

            }
        }
    }
}

void ParamCompareDialog::saveNewParameters()
{
    QLOG_DEBUG() << " Save selected Parameters";
    QTableWidget* table = ui->compareTableWidget;

    for(int rowCount = 0; rowCount < table->rowCount(); ++rowCount){
        // Get hold of the UASParameter
        QTableWidgetItem* paramName = table->item(rowCount, PCD_COLUMN_PARAM_NAME);
        QTableWidgetItem* paramCheck= table->item(rowCount, PCD_COLUMN_CHECKBOX);
        if (paramName && (paramCheck->checkState() == Qt::Checked)){
            UASParameter* param = m_currentList->value(paramName->text());

            // then update it's value in the current list
            // [TODO] this would be an action of the Param Manager
            param->setValue(table->item(rowCount, PCD_COLUMN_NEW_VALUE)->data(Qt::DisplayRole));
            QLOG_DEBUG() << "Applied to m_currentList:" << param << " = "
                         << table->item(rowCount, PCD_COLUMN_NEW_VALUE)->data(Qt::DisplayRole);
        }
    }

    accept(); // dismiss the dialog
}

void ParamCompareDialog::checkAll()
{
    QLOG_DEBUG() << " check uncheck all parameters";
    QTableWidget* table = ui->compareTableWidget;

    if(ui->checkAllBox->isChecked()){
        for(int rowCount = 0; rowCount < table->rowCount(); ++rowCount){
            QTableWidgetItem* item = table->item(rowCount, PCD_COLUMN_CHECKBOX);
            if (item) item->setCheckState(Qt::Checked);
        }
    } else {
        for(int rowCount = 0; rowCount < table->rowCount(); ++rowCount){
            QTableWidgetItem* item = table->item(rowCount, PCD_COLUMN_CHECKBOX);
            if (item) item->setCheckState(Qt::Unchecked);
        }
    }
}
<commit_msg>Fixes #239: Missed file for commit f8eab2987c  (oops friday afternoon!)<commit_after>#include "QsLog.h"
#include "configuration.h"
#include "ParamCompareDialog.h"
#include "ui_ParamCompareDialog.h"
#include <QMessageBox>
#include <QTableWidget>
#include <QDesktopServices>
#include <QDir>
#include <QFileDialog>
#include <QCheckBox>
#include <QPushButton>
#include <QTimer>

#define PCD_COLUMN_PARAM_NAME 0
#define PCD_COLUMN_VALUE 1
#define PCD_COLUMN_NEW_VALUE 2
#define PCD_COLUMN_CHECKBOX 3

ParamCompareDialog::ParamCompareDialog(QMap<QString, UASParameter* >& paramaterList,
                                       const QString& filename, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ParamCompareDialog),
    m_currentList(&paramaterList),
    m_newList(new QMap<QString, UASParameter*>()),
    m_fileToCompare(filename)
{
    ui->setupUi(this);

    QStringList headerList;
    headerList << tr("Parameter") << tr("Value") << tr("New Value") << tr("Use");

    QTableWidget* table = ui->compareTableWidget;
    table->setColumnCount(headerList.count());
    table->setHorizontalHeaderLabels(headerList);
    table->setSelectionBehavior(QAbstractItemView::SelectRows);
    table->setAlternatingRowColors(true);
    table->setColumnWidth(PCD_COLUMN_CHECKBOX, 40);

    initConnections();

    if(filename.count()>0){
        QTimer::singleShot(200, this, SLOT(loadParameterWithFile()));
    }

}

ParamCompareDialog::~ParamCompareDialog()
{
    delete ui;
}

void ParamCompareDialog::initConnections()
{
    connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
    connect(ui->loadButton, SIGNAL(clicked()), this, SLOT(loadParameterFile()));
    connect(ui->continueButton, SIGNAL(clicked()), this, SLOT(saveNewParameters()));
    connect(ui->checkAllBox, SIGNAL(clicked()), this, SLOT(checkAll()));

}

void ParamCompareDialog::setAcceptButtonLabel(const QString &label)
{
    if (ui) ui->continueButton->setText(label);
}

void ParamCompareDialog::loadParameterFile()
{
    ui->compareTableWidget->setRowCount(0);

    QDir parameterDir = QDir(QGC::parameterDirectory());

    if(!parameterDir.exists())
        parameterDir.mkdir(parameterDir.path());

    QString filename = QFileDialog::getOpenFileName(this,tr("Open File To Compare"),
                                                    QGC::parameterDirectory(), "*.param");
    QApplication::processEvents(); // Helps clear dialog from screen

    if(filename.length() == 0) {
        return;
    }
    QApplication::processEvents(); // Helps clear dialog from screen

    loadParameterFile(filename);
}

void ParamCompareDialog::loadParameterWithFile()
{
    ui->loadButton->hide();
    loadParameterFile(m_fileToCompare);
}

void ParamCompareDialog::loadParameterFile(const QString &filename)
{
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly))
    {
        QMessageBox::information(this,"Error",tr("Unable to open the file.").arg(filename));
        return;
    }

    QString filestring = file.readAll();
    file.close();

    populateParamListFromString(filestring, m_newList, this);

    compareLists();
}

void ParamCompareDialog::populateParamListFromString(QString paramString, QMap<QString, UASParameter*>* list,
                                                     QWidget* widget = NULL)
{
    QStringList paramSplit = paramString.split("\n");
    bool summaryComplete = false;
    bool summaryShown = false;
    QString summaryText;

    foreach (QString paramLine, paramSplit) {
        if (!paramLine.startsWith("#")) {
            summaryComplete = true;
            QStringList lineSplit = paramLine.split(",");
            if (lineSplit.size() == 2)
            {
                bool ok;
                QLOG_DEBUG() << "load param: " << lineSplit[0] << "=" << lineSplit[1];
                UASParameter* param = new UASParameter();
                param->setName(lineSplit[0]);

                double value = lineSplit[1].toFloat(&ok);
                if (ok){
                    param->setValue(QVariant(value));
                } else {
                    QLOG_ERROR() << "Conversion Failure";
                    param->setValue(QVariant("NaN"));
                }

                list->insert(param->name(), param);

            }
        } else {
            QLOG_DEBUG() << "Comment: " << paramLine;
            if (!summaryShown && !summaryComplete){
                // removes the '#' and any whites space before or after
                summaryText.append(paramLine.remove(0,1).trimmed() + "\n");
            }
        }
        if (!summaryShown && summaryComplete){
            QLOG_DEBUG() << "Show Summary: " << summaryText;
            if (summaryText.count()>0){
                QMessageBox::information(widget,tr("Param File Summary"),summaryText,QMessageBox::Ok);
            }
            summaryShown = true;
        }
    }
}

void ParamCompareDialog::compareLists()
{
    QList<QString> keys = m_newList->keys(); // This needs to be an amalgamated list of all keys

    ui->compareTableWidget->setSortingEnabled(false);
    for(int count = 0; count < keys.count(); ++count){

        UASParameter* currentParam = m_currentList->value(keys[count]);

        if (currentParam != NULL){
            UASParameter* newParam = m_newList->value(keys[count]);

            if (currentParam->value().toDouble() != newParam->value().toDouble() ){
                QLOG_DEBUG() << "Difference : " << currentParam->name()
                             << " current: " << currentParam->value() << " new:" << newParam->value();

                int rowCount = ui->compareTableWidget->rowCount();
                ui->compareTableWidget->setRowCount(ui->compareTableWidget->rowCount()+1);

                QTableWidgetItem* widgetItemParam = new QTableWidgetItem(keys[count]);
                widgetItemParam->setFlags(Qt::NoItemFlags | Qt::ItemIsEnabled );
                ui->compareTableWidget->setItem(rowCount, PCD_COLUMN_PARAM_NAME, widgetItemParam);

                QTableWidgetItem* widgetItemValue = new QTableWidgetItem();
                widgetItemValue->setData(Qt::DisplayRole, currentParam->value());
                widgetItemValue->setFlags(Qt::NoItemFlags  | Qt::ItemIsEnabled);
                ui->compareTableWidget->setItem(rowCount, PCD_COLUMN_VALUE, widgetItemValue);

                QTableWidgetItem* widgetItemNewValue = new QTableWidgetItem();
                widgetItemNewValue->setData(Qt::DisplayRole, newParam->value());
                widgetItemNewValue->setFlags(Qt::NoItemFlags | Qt::ItemIsEnabled | Qt::ItemIsEditable);
                ui->compareTableWidget->setItem(rowCount, PCD_COLUMN_NEW_VALUE, widgetItemNewValue);

                QTableWidgetItem* widgetItemCheckbox= new QTableWidgetItem();
                widgetItemCheckbox->setFlags(Qt::NoItemFlags | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
                widgetItemCheckbox->setCheckState(Qt::Checked);
                ui->compareTableWidget->setItem(rowCount, PCD_COLUMN_CHECKBOX, widgetItemCheckbox);

            }
        }
    }
}

void ParamCompareDialog::saveNewParameters()
{
    QLOG_DEBUG() << " Save selected Parameters";
    QTableWidget* table = ui->compareTableWidget;

    for(int rowCount = 0; rowCount < table->rowCount(); ++rowCount){
        // Get hold of the UASParameter
        QTableWidgetItem* paramName = table->item(rowCount, PCD_COLUMN_PARAM_NAME);
        QTableWidgetItem* paramCheck= table->item(rowCount, PCD_COLUMN_CHECKBOX);
        if (paramName && (paramCheck->checkState() == Qt::Checked)){
            UASParameter* param = m_currentList->value(paramName->text());

            // then update it's value in the current list
            // [TODO] this would be an action of the Param Manager
            param->setValue(table->item(rowCount, PCD_COLUMN_NEW_VALUE)->data(Qt::DisplayRole));
            QLOG_DEBUG() << "Applied to m_currentList:" << param << " = "
                         << table->item(rowCount, PCD_COLUMN_NEW_VALUE)->data(Qt::DisplayRole);
        }
    }

    accept(); // dismiss the dialog
}

void ParamCompareDialog::checkAll()
{
    QLOG_DEBUG() << " check uncheck all parameters";
    QTableWidget* table = ui->compareTableWidget;

    if(ui->checkAllBox->isChecked()){
        for(int rowCount = 0; rowCount < table->rowCount(); ++rowCount){
            QTableWidgetItem* item = table->item(rowCount, PCD_COLUMN_CHECKBOX);
            if (item) item->setCheckState(Qt::Checked);
        }
    } else {
        for(int rowCount = 0; rowCount < table->rowCount(); ++rowCount){
            QTableWidgetItem* item = table->item(rowCount, PCD_COLUMN_CHECKBOX);
            if (item) item->setCheckState(Qt::Unchecked);
        }
    }
}
<|endoftext|>