<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2013-2018 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/*
 * @file LandingTargetEstimator.cpp
 *
 * @author Nicolas de Palezieux (Sunflower Labs) <ndepal@gmail.com>
 * @author Mohammed Kabir <kabir@uasys.io>
 *
 */

#include <px4_config.h>
#include <px4_defines.h>
#include <drivers/drv_hrt.h>

#include "LandingTargetEstimator.h"

#define SEC2USEC 1000000.0f

namespace landing_target_estimator
{

LandingTargetEstimator::LandingTargetEstimator() :
	_targetPosePub(nullptr),
	_targetInnovationsPub(nullptr),
	_paramHandle(),
	_vehicleLocalPosition_valid(false),
	_vehicleAttitude_valid(false),
	_sensorBias_valid(false),
	_new_irlockReport(false),
	_estimator_initialized(false),
	_faulty(false),
	_last_predict(0),
	_last_update(0)
{
	_paramHandle.acc_unc = param_find("LTEST_ACC_UNC");
	_paramHandle.meas_unc = param_find("LTEST_MEAS_UNC");
	_paramHandle.pos_unc_init = param_find("LTEST_POS_UNC_IN");
	_paramHandle.vel_unc_init = param_find("LTEST_VEL_UNC_IN");
	_paramHandle.mode = param_find("LTEST_MODE");
	_paramHandle.scale_x = param_find("LTEST_SCALE_X");
	_paramHandle.scale_y = param_find("LTEST_SCALE_Y");

	// Initialize uORB topics.
	_initialize_topics();

	_check_params(true);
}

LandingTargetEstimator::~LandingTargetEstimator()
{
}

void LandingTargetEstimator::update()
{
	_check_params(false);

	_update_topics();

	/* predict */
	if (_estimator_initialized) {
		if (hrt_absolute_time() - _last_update > landing_target_estimator_TIMEOUT_US) {
			PX4_WARN("Timeout");
			_estimator_initialized = false;

		} else if (_vehicleLocalPosition_valid
			   && _vehicleLocalPosition.v_xy_valid) {
			float dt = (hrt_absolute_time() - _last_predict) / SEC2USEC;

			// predict target position with the help of accel data
			matrix::Vector3f a;

			if (_sensorBias_valid) {
				matrix::Quaternion<float> q_att(&_vehicleAttitude.q[0]);
				_R_att = matrix::Dcm<float>(q_att);
				a(0) = _sensorBias.accel_x;
				a(1) = _sensorBias.accel_y;
				a(2) = _sensorBias.accel_z;
				a = _R_att * a;

			} else {
				a.zero();
			}

			_kalman_filter_x.predict(dt, -a(0), _params.acc_unc);
			_kalman_filter_y.predict(dt, -a(1), _params.acc_unc);

			_last_predict = hrt_absolute_time();
		}
	}

	if (!_new_irlockReport) {
		// nothing to do
		return;
	}

	// mark this sensor measurement as consumed
	_new_irlockReport = false;

	if (!_vehicleAttitude_valid || !_vehicleLocalPosition_valid || !_vehicleLocalPosition.dist_bottom_valid) {
		// don't have the data needed for an update
		return;
	}

	if (!PX4_ISFINITE(_irlockReport.pos_y) || !PX4_ISFINITE(_irlockReport.pos_x)) {
		return;
	}

	// TODO account for sensor orientation as set by parameter
	// default orientation has camera x pointing in body y, camera y in body -x

	matrix::Vector<float, 3> sensor_ray; // ray pointing towards target in body frame
	sensor_ray(0) = -_irlockReport.pos_y * _params.scale_y; // forward
	sensor_ray(1) = _irlockReport.pos_x * _params.scale_x; // right
	sensor_ray(2) = 1.0f;

	// rotate the unit ray into the navigation frame, assume sensor frame = body frame
	matrix::Quaternion<float> q_att(&_vehicleAttitude.q[0]);
	_R_att = matrix::Dcm<float>(q_att);
	sensor_ray = _R_att * sensor_ray;

	if (fabsf(sensor_ray(2)) < 1e-6f) {
		// z component of measurement unsafe, don't use this measurement
		return;
	}

	float dist = _vehicleLocalPosition.dist_bottom;

	// scale the ray s.t. the z component has length of dist
	_rel_pos(0) = sensor_ray(0) / sensor_ray(2) * dist;
	_rel_pos(1) = sensor_ray(1) / sensor_ray(2) * dist;

	if (!_estimator_initialized) {
		PX4_INFO("Init");
		_kalman_filter_x.init(_rel_pos(0), 0, _params.pos_unc_init, _params.vel_unc_init);
		_kalman_filter_y.init(_rel_pos(1), 0, _params.pos_unc_init, _params.vel_unc_init);

		_estimator_initialized = true;
		_last_update = hrt_absolute_time();
		_last_predict = _last_update;

	} else {
		// update
		bool update_x = _kalman_filter_x.update(_rel_pos(0), _params.meas_unc * dist * dist);
		bool update_y = _kalman_filter_y.update(_rel_pos(1), _params.meas_unc * dist * dist);

		if (!update_x || !update_y) {
			if (!_faulty) {
				_faulty = true;
				PX4_WARN("Landing target measurement rejected:%s%s", update_x ? "" : " x", update_y ? "" : " y");
			}

		} else {
			_faulty = false;
		}

		if (!_faulty) {
			// only publish if both measurements were good

			_target_pose.timestamp = _irlockReport.timestamp;

			float x, xvel, y, yvel, covx, covx_v, covy, covy_v;
			_kalman_filter_x.getState(x, xvel);
			_kalman_filter_x.getCovariance(covx, covx_v);

			_kalman_filter_y.getState(y, yvel);
			_kalman_filter_y.getCovariance(covy, covy_v);

			_target_pose.is_static = (_params.mode == TargetMode::Stationary);

			_target_pose.rel_pos_valid = true;
			_target_pose.rel_vel_valid = true;
			_target_pose.x_rel = x;
			_target_pose.y_rel = y;
			_target_pose.z_rel = dist;
			_target_pose.vx_rel = xvel;
			_target_pose.vy_rel = yvel;

			_target_pose.cov_x_rel = covx;
			_target_pose.cov_y_rel = covy;

			_target_pose.cov_vx_rel = covx_v;
			_target_pose.cov_vy_rel = covy_v;

			if (_vehicleLocalPosition_valid && _vehicleLocalPosition.xy_valid) {
				_target_pose.x_abs = x + _vehicleLocalPosition.x;
				_target_pose.y_abs = y + _vehicleLocalPosition.y;
				_target_pose.z_abs = dist + _vehicleLocalPosition.z;
				_target_pose.abs_pos_valid = true;

			} else {
				_target_pose.abs_pos_valid = false;
			}

			if (_targetPosePub == nullptr) {
				_targetPosePub = orb_advertise(ORB_ID(landing_target_pose), &_target_pose);

			} else {
				orb_publish(ORB_ID(landing_target_pose), _targetPosePub, &_target_pose);
			}

			_last_update = hrt_absolute_time();
			_last_predict = _last_update;
		}

		float innov_x, innov_cov_x, innov_y, innov_cov_y;
		_kalman_filter_x.getInnovations(innov_x, innov_cov_x);
		_kalman_filter_y.getInnovations(innov_y, innov_cov_y);

		_target_innovations.timestamp = _irlockReport.timestamp;
		_target_innovations.innov_x = innov_x;
		_target_innovations.innov_cov_x = innov_cov_x;
		_target_innovations.innov_y = innov_y;
		_target_innovations.innov_cov_y = innov_cov_y;

		if (_targetInnovationsPub == nullptr) {
			_targetInnovationsPub = orb_advertise(ORB_ID(landing_target_innovations), &_target_innovations);

		} else {
			orb_publish(ORB_ID(landing_target_innovations), _targetInnovationsPub, &_target_innovations);
		}
	}

}

void LandingTargetEstimator::_check_params(const bool force)
{
	bool updated;
	parameter_update_s paramUpdate;

	orb_check(_parameterSub, &updated);

	if (updated) {
		orb_copy(ORB_ID(parameter_update), _parameterSub, &paramUpdate);
	}

	if (updated || force) {
		_update_params();
	}
}

void LandingTargetEstimator::_initialize_topics()
{
	// subscribe to position, attitude, arming and velocity changes
	_vehicleLocalPositionSub = orb_subscribe(ORB_ID(vehicle_local_position));
	_attitudeSub = orb_subscribe(ORB_ID(vehicle_attitude));
	_sensorBiasSub = orb_subscribe(ORB_ID(sensor_bias));
	_irlockReportSub = orb_subscribe(ORB_ID(irlock_report));
	_parameterSub = orb_subscribe(ORB_ID(parameter_update));
}

void LandingTargetEstimator::_update_topics()
{
	_vehicleLocalPosition_valid = _orb_update(ORB_ID(vehicle_local_position), _vehicleLocalPositionSub,
				      &_vehicleLocalPosition);
	_vehicleAttitude_valid = _orb_update(ORB_ID(vehicle_attitude), _attitudeSub, &_vehicleAttitude);
	_sensorBias_valid = _orb_update(ORB_ID(sensor_bias), _sensorBiasSub, &_sensorBias);

	_new_irlockReport = _orb_update(ORB_ID(irlock_report), _irlockReportSub, &_irlockReport);
}

bool LandingTargetEstimator::_orb_update(const struct orb_metadata *meta, int handle, void *buffer)
{
	bool newData = false;

	// check if there is new data to grab
	if (orb_check(handle, &newData) != OK) {
		return false;
	}

	if (!newData) {
		return false;
	}

	if (orb_copy(meta, handle, buffer) != OK) {
		return false;
	}

	return true;
}

void LandingTargetEstimator::_update_params()
{
	param_get(_paramHandle.acc_unc, &_params.acc_unc);
	param_get(_paramHandle.meas_unc, &_params.meas_unc);
	param_get(_paramHandle.pos_unc_init, &_params.pos_unc_init);
	param_get(_paramHandle.vel_unc_init, &_params.vel_unc_init);
	int mode = 0;
	param_get(_paramHandle.mode, &mode);
	_params.mode = (TargetMode)mode;
	param_get(_paramHandle.scale_x, &_params.scale_x);
	param_get(_paramHandle.scale_y, &_params.scale_y);
}

} // namespace landing_target_estimator
<commit_msg>landing target estimator: check validity of the correct data before computing acceleration<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2013-2018 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/*
 * @file LandingTargetEstimator.cpp
 *
 * @author Nicolas de Palezieux (Sunflower Labs) <ndepal@gmail.com>
 * @author Mohammed Kabir <kabir@uasys.io>
 *
 */

#include <px4_config.h>
#include <px4_defines.h>
#include <drivers/drv_hrt.h>

#include "LandingTargetEstimator.h"

#define SEC2USEC 1000000.0f

namespace landing_target_estimator
{

LandingTargetEstimator::LandingTargetEstimator() :
	_targetPosePub(nullptr),
	_targetInnovationsPub(nullptr),
	_paramHandle(),
	_vehicleLocalPosition_valid(false),
	_vehicleAttitude_valid(false),
	_sensorBias_valid(false),
	_new_irlockReport(false),
	_estimator_initialized(false),
	_faulty(false),
	_last_predict(0),
	_last_update(0)
{
	_paramHandle.acc_unc = param_find("LTEST_ACC_UNC");
	_paramHandle.meas_unc = param_find("LTEST_MEAS_UNC");
	_paramHandle.pos_unc_init = param_find("LTEST_POS_UNC_IN");
	_paramHandle.vel_unc_init = param_find("LTEST_VEL_UNC_IN");
	_paramHandle.mode = param_find("LTEST_MODE");
	_paramHandle.scale_x = param_find("LTEST_SCALE_X");
	_paramHandle.scale_y = param_find("LTEST_SCALE_Y");

	// Initialize uORB topics.
	_initialize_topics();

	_check_params(true);
}

LandingTargetEstimator::~LandingTargetEstimator()
{
}

void LandingTargetEstimator::update()
{
	_check_params(false);

	_update_topics();

	/* predict */
	if (_estimator_initialized) {
		if (hrt_absolute_time() - _last_update > landing_target_estimator_TIMEOUT_US) {
			PX4_WARN("Timeout");
			_estimator_initialized = false;

		} else {
			float dt = (hrt_absolute_time() - _last_predict) / SEC2USEC;

			// predict target position with the help of accel data
			matrix::Vector3f a;

			if (_vehicleAttitude_valid && _sensorBias_valid) {
				matrix::Quaternion<float> q_att(&_vehicleAttitude.q[0]);
				_R_att = matrix::Dcm<float>(q_att);
				a(0) = _sensorBias.accel_x;
				a(1) = _sensorBias.accel_y;
				a(2) = _sensorBias.accel_z;
				a = _R_att * a;

			} else {
				a.zero();
			}

			_kalman_filter_x.predict(dt, -a(0), _params.acc_unc);
			_kalman_filter_y.predict(dt, -a(1), _params.acc_unc);

			_last_predict = hrt_absolute_time();
		}
	}

	if (!_new_irlockReport) {
		// nothing to do
		return;
	}

	// mark this sensor measurement as consumed
	_new_irlockReport = false;

	if (!_vehicleAttitude_valid || !_vehicleLocalPosition_valid || !_vehicleLocalPosition.dist_bottom_valid) {
		// don't have the data needed for an update
		return;
	}

	if (!PX4_ISFINITE(_irlockReport.pos_y) || !PX4_ISFINITE(_irlockReport.pos_x)) {
		return;
	}

	// TODO account for sensor orientation as set by parameter
	// default orientation has camera x pointing in body y, camera y in body -x

	matrix::Vector<float, 3> sensor_ray; // ray pointing towards target in body frame
	sensor_ray(0) = -_irlockReport.pos_y * _params.scale_y; // forward
	sensor_ray(1) = _irlockReport.pos_x * _params.scale_x; // right
	sensor_ray(2) = 1.0f;

	// rotate the unit ray into the navigation frame, assume sensor frame = body frame
	matrix::Quaternion<float> q_att(&_vehicleAttitude.q[0]);
	_R_att = matrix::Dcm<float>(q_att);
	sensor_ray = _R_att * sensor_ray;

	if (fabsf(sensor_ray(2)) < 1e-6f) {
		// z component of measurement unsafe, don't use this measurement
		return;
	}

	float dist = _vehicleLocalPosition.dist_bottom;

	// scale the ray s.t. the z component has length of dist
	_rel_pos(0) = sensor_ray(0) / sensor_ray(2) * dist;
	_rel_pos(1) = sensor_ray(1) / sensor_ray(2) * dist;

	if (!_estimator_initialized) {
		PX4_INFO("Init");
		_kalman_filter_x.init(_rel_pos(0), 0, _params.pos_unc_init, _params.vel_unc_init);
		_kalman_filter_y.init(_rel_pos(1), 0, _params.pos_unc_init, _params.vel_unc_init);

		_estimator_initialized = true;
		_last_update = hrt_absolute_time();
		_last_predict = _last_update;

	} else {
		// update
		bool update_x = _kalman_filter_x.update(_rel_pos(0), _params.meas_unc * dist * dist);
		bool update_y = _kalman_filter_y.update(_rel_pos(1), _params.meas_unc * dist * dist);

		if (!update_x || !update_y) {
			if (!_faulty) {
				_faulty = true;
				PX4_WARN("Landing target measurement rejected:%s%s", update_x ? "" : " x", update_y ? "" : " y");
			}

		} else {
			_faulty = false;
		}

		if (!_faulty) {
			// only publish if both measurements were good

			_target_pose.timestamp = _irlockReport.timestamp;

			float x, xvel, y, yvel, covx, covx_v, covy, covy_v;
			_kalman_filter_x.getState(x, xvel);
			_kalman_filter_x.getCovariance(covx, covx_v);

			_kalman_filter_y.getState(y, yvel);
			_kalman_filter_y.getCovariance(covy, covy_v);

			_target_pose.is_static = (_params.mode == TargetMode::Stationary);

			_target_pose.rel_pos_valid = true;
			_target_pose.rel_vel_valid = true;
			_target_pose.x_rel = x;
			_target_pose.y_rel = y;
			_target_pose.z_rel = dist;
			_target_pose.vx_rel = xvel;
			_target_pose.vy_rel = yvel;

			_target_pose.cov_x_rel = covx;
			_target_pose.cov_y_rel = covy;

			_target_pose.cov_vx_rel = covx_v;
			_target_pose.cov_vy_rel = covy_v;

			if (_vehicleLocalPosition_valid && _vehicleLocalPosition.xy_valid) {
				_target_pose.x_abs = x + _vehicleLocalPosition.x;
				_target_pose.y_abs = y + _vehicleLocalPosition.y;
				_target_pose.z_abs = dist + _vehicleLocalPosition.z;
				_target_pose.abs_pos_valid = true;

			} else {
				_target_pose.abs_pos_valid = false;
			}

			if (_targetPosePub == nullptr) {
				_targetPosePub = orb_advertise(ORB_ID(landing_target_pose), &_target_pose);

			} else {
				orb_publish(ORB_ID(landing_target_pose), _targetPosePub, &_target_pose);
			}

			_last_update = hrt_absolute_time();
			_last_predict = _last_update;
		}

		float innov_x, innov_cov_x, innov_y, innov_cov_y;
		_kalman_filter_x.getInnovations(innov_x, innov_cov_x);
		_kalman_filter_y.getInnovations(innov_y, innov_cov_y);

		_target_innovations.timestamp = _irlockReport.timestamp;
		_target_innovations.innov_x = innov_x;
		_target_innovations.innov_cov_x = innov_cov_x;
		_target_innovations.innov_y = innov_y;
		_target_innovations.innov_cov_y = innov_cov_y;

		if (_targetInnovationsPub == nullptr) {
			_targetInnovationsPub = orb_advertise(ORB_ID(landing_target_innovations), &_target_innovations);

		} else {
			orb_publish(ORB_ID(landing_target_innovations), _targetInnovationsPub, &_target_innovations);
		}
	}

}

void LandingTargetEstimator::_check_params(const bool force)
{
	bool updated;
	parameter_update_s paramUpdate;

	orb_check(_parameterSub, &updated);

	if (updated) {
		orb_copy(ORB_ID(parameter_update), _parameterSub, &paramUpdate);
	}

	if (updated || force) {
		_update_params();
	}
}

void LandingTargetEstimator::_initialize_topics()
{
	_vehicleLocalPositionSub = orb_subscribe(ORB_ID(vehicle_local_position));
	_attitudeSub = orb_subscribe(ORB_ID(vehicle_attitude));
	_sensorBiasSub = orb_subscribe(ORB_ID(sensor_bias));
	_irlockReportSub = orb_subscribe(ORB_ID(irlock_report));
	_parameterSub = orb_subscribe(ORB_ID(parameter_update));
}

void LandingTargetEstimator::_update_topics()
{
	_vehicleLocalPosition_valid = _orb_update(ORB_ID(vehicle_local_position), _vehicleLocalPositionSub,
				      &_vehicleLocalPosition);
	_vehicleAttitude_valid = _orb_update(ORB_ID(vehicle_attitude), _attitudeSub, &_vehicleAttitude);
	_sensorBias_valid = _orb_update(ORB_ID(sensor_bias), _sensorBiasSub, &_sensorBias);

	_new_irlockReport = _orb_update(ORB_ID(irlock_report), _irlockReportSub, &_irlockReport);
}

bool LandingTargetEstimator::_orb_update(const struct orb_metadata *meta, int handle, void *buffer)
{
	bool newData = false;

	// check if there is new data to grab
	if (orb_check(handle, &newData) != OK) {
		return false;
	}

	if (!newData) {
		return false;
	}

	if (orb_copy(meta, handle, buffer) != OK) {
		return false;
	}

	return true;
}

void LandingTargetEstimator::_update_params()
{
	param_get(_paramHandle.acc_unc, &_params.acc_unc);
	param_get(_paramHandle.meas_unc, &_params.meas_unc);
	param_get(_paramHandle.pos_unc_init, &_params.pos_unc_init);
	param_get(_paramHandle.vel_unc_init, &_params.vel_unc_init);
	int mode = 0;
	param_get(_paramHandle.mode, &mode);
	_params.mode = (TargetMode)mode;
	param_get(_paramHandle.scale_x, &_params.scale_x);
	param_get(_paramHandle.scale_y, &_params.scale_y);
}

} // namespace landing_target_estimator
<|endoftext|>


<commit_before>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions 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.

#include <gtest/gtest.h>

#include <string>
#include <memory>

#include <SurgSim/Framework/Runtime.h>
#include <SurgSim/Physics/PhysicsManager.h>
#include <SurgSim/Physics/FixedActor.h>
#include <SurgSim/Physics/RigidActor.h>
#include <SurgSim/Physics/RigidActorParameters.h>
#include <SurgSim/Physics/SphereShape.h>
#include <SurgSim/Physics/FreeMotion.h>
#include <SurgSim/Math/Vector.h>
#include <SurgSim/Math/Quaternion.h>
#include <SurgSim/Math/RigidTransform.h>

using SurgSim::Physics::Actor;
using SurgSim::Physics::RigidActor;
using SurgSim::Physics::RigidActorParameters;
using SurgSim::Physics::SphereShape;
using SurgSim::Physics::FreeMotion;

struct FreeMotionTest: public ::testing::Test
{
	virtual void SetUp()
	{
	}

	virtual void TearDown()
	{
	}
};

TEST(FreeMotionTest, RunTest)
{
	std::shared_ptr<std::vector<std::shared_ptr<Actor>>> actors =
		std::make_shared<std::vector<std::shared_ptr<Actor>>>();
	std::shared_ptr<RigidActor> actor = std::make_shared<RigidActor>("TestSphere");

	RigidActorParameters params;
	params.setDensity(700.0); // Wood

	std::shared_ptr<SphereShape> shape = std::make_shared<SphereShape>(0.01); // 1cm Sphere
	params.setShapeUsedForMassInertia(shape);

	actor->setInitialParameters(params);
	actor->setInitialPose(SurgSim::Math::makeRigidTransform(SurgSim::Math::Quaterniond(), Vector3d(0.0,0.0,0.0)));

	actors->push_back(actor);

	FreeMotion computation(actors);

	EXPECT_TRUE(Vector3d(0.0,0.0,0.0).isApprox(actor->getPose().translation()));
	computation.update(1.0);
	EXPECT_FALSE(Vector3d(0.0,0.0,0.0).isApprox(actor->getPose().translation()));

}

<commit_msg>Fix 0 vector tests in FreeMotionTests<commit_after>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions 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.

#include <gtest/gtest.h>

#include <string>
#include <memory>

#include <SurgSim/Framework/Runtime.h>
#include <SurgSim/Physics/PhysicsManager.h>
#include <SurgSim/Physics/FixedActor.h>
#include <SurgSim/Physics/RigidActor.h>
#include <SurgSim/Physics/RigidActorParameters.h>
#include <SurgSim/Physics/SphereShape.h>
#include <SurgSim/Physics/FreeMotion.h>
#include <SurgSim/Math/Vector.h>
#include <SurgSim/Math/Quaternion.h>
#include <SurgSim/Math/RigidTransform.h>

using SurgSim::Physics::Actor;
using SurgSim::Physics::RigidActor;
using SurgSim::Physics::RigidActorParameters;
using SurgSim::Physics::SphereShape;
using SurgSim::Physics::FreeMotion;

struct FreeMotionTest: public ::testing::Test
{
	virtual void SetUp()
	{
	}

	virtual void TearDown()
	{
	}
};

TEST(FreeMotionTest, RunTest)
{
	std::shared_ptr<std::vector<std::shared_ptr<Actor>>> actors =
		std::make_shared<std::vector<std::shared_ptr<Actor>>>();
	std::shared_ptr<RigidActor> actor = std::make_shared<RigidActor>("TestSphere");

	RigidActorParameters params;
	params.setDensity(700.0); // Wood

	std::shared_ptr<SphereShape> shape = std::make_shared<SphereShape>(0.01); // 1cm Sphere
	params.setShapeUsedForMassInertia(shape);

	actor->setInitialParameters(params);
	actor->setInitialPose(SurgSim::Math::makeRigidTransform(SurgSim::Math::Quaterniond(), Vector3d(0.0,0.0,0.0)));

	actors->push_back(actor);

	FreeMotion computation(actors);

	EXPECT_TRUE(actor->getPose().translation().isZero());
	computation.update(1.0);
	EXPECT_FALSE(actor->getPose().translation().isZero());

}

<|endoftext|>


<commit_before>
#include "GateFinder.h"

GateFinder::GateFinder()
{
}

GateFinder::~GateFinder()
{
}

extern void drawLine(cv::Mat & img, cv::Mat & img2, cv::Vec4f line, int thickness, CvScalar color, bool nightVision = false);

cv::Point2i GateFinder::LocateOnScreen(ThresholdedImages &HSVRanges, cv::Mat &frameHSV, cv::Mat &frameBGR, OBJECT target) {
	int smallestGateArea = 1000;
	double growGateHeight = 1.2;
	cv::Point2d center(-1, -1);
	cv::Mat imgThresholded = HSVRanges[target]; // reference counted, I think
	if (imgThresholded.rows == 0) return center;

	cv::Mat dst(imgThresholded.rows, imgThresholded.cols, CV_8U, cv::Scalar::all(0));

	cv::Scalar color(0, 0, 0);
	cv::Scalar color2(255, 255, 255);

	//biggest area calculation
	std::vector<std::vector<cv::Point>> contours; // Vector for storing contour
	std::vector<cv::Vec4i> hierarchy;
	cv::Rect bounding_rect;

	findContours(imgThresholded, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); // Find the contours in the image
	if (contours.size() == 0){
		return center;
	}

	double largest_area = 0;
	double area = 0;
	size_t largest_contour_index = 0;
	for (size_t i = 0; i < contours.size(); i++) // iterate through each contour.
	{
		area = cv::contourArea(contours[i], false);  //  Find the area of contour
		if (area > largest_area){
			largest_area = area;
			largest_contour_index = i;                //Store the index of largest contour
		}
	}

	//validate gate area
	if (largest_area < smallestGateArea){
		return cv::Point2i(-1, -1);
	}

	//find center
	if (contours.size() > largest_contour_index){
		cv::Moments M = cv::moments(contours[largest_contour_index]);
		center = cv::Point2i((int)(M.m10 / M.m00), (int)(M.m01 / M.m00));
	}
	else {
		assert(false);
	}

/*
	//Cutting out gate from ball frame
	bounding_rect = cv::boundingRect(contours[largest_contour_index]);
	bounding_rect.height = bounding_rect.height * growGateHeight;
	rectangle(HSVRanges[BALL], bounding_rect.tl(), bounding_rect.br(), color, -1, 8, 0);
	//for clear visual:
	rectangle(frameBGR, bounding_rect.tl(), bounding_rect.br(), color, -1, 8, 0);
	if (HSVRanges.find(SIGHT_MASK) != HSVRanges.end()) {
		rectangle(HSVRanges[SIGHT_MASK], bounding_rect.tl(), bounding_rect.br(), color, -1, 8, 0);
	}
*/
	cv::Scalar color4(255, 0, 0);
	/*

	cv::RotatedRect bounding_rect2 = cv::minAreaRect(contours[largest_contour_index]);
	cv::Point2f rect_points[4]; bounding_rect2.points(rect_points);
	int shift = bounding_rect2.size.height * 0.09 +0.2;
	//std::cout << "shift: " << shift << " height: " << bounding_rect2.size.height << std::endl;
	for (int j = 0; j < 4; j++) {
		line(frameBGR, rect_points[j], rect_points[(j + 1) % 4], color2, 1, 8);

		std::vector<cv::Point2i> points;
		points.push_back(rect_points[j]);
		points.push_back(rect_points[(j + 1) % 4]);
		cv::Vec4f newLine;
		cv::fitLine(points, newLine, CV_DIST_L2, 0, 0.1, 0.1);
		//std::cout << rect_points[j] << ", " << rect_points[(j + 1) % 4] << ": " << atan2(newLine[1], newLine[0])*180/PI << std::endl;
		if (abs(atan2(newLine[1], newLine[0]) * 180 / PI) < 45) {
			newLine[3] += shift; // shift line down
			drawLine(frameBGR, HSVRanges[BALL], newLine, 1, cv::Scalar(0, 255 * (1 + 0.3), 0));
			drawLine(frameBGR, HSVRanges[SIGHT_MASK], newLine, 1, cv::Scalar(0, 255 * (1 + 0.3), 0));
		}

	}
	*/

	// find out lowest line
	return center;
}
<commit_msg>Changed goal position from whole detected object centre to closest side centre.<commit_after>
#include "GateFinder.h"

GateFinder::GateFinder()
{
}

GateFinder::~GateFinder()
{
}

extern void drawLine(cv::Mat & img, cv::Mat & img2, cv::Vec4f line, int thickness, CvScalar color, bool nightVision = false);

cv::Point2i GateFinder::LocateOnScreen(ThresholdedImages &HSVRanges, cv::Mat &frameHSV, cv::Mat &frameBGR, OBJECT target) {
	int smallestGateArea = 1000;
	double growGateHeight = 1.2;
	cv::Point2d center(-1, -1);
	cv::Mat imgThresholded = HSVRanges[target]; // reference counted, I think
	if (imgThresholded.rows == 0) return center;

	cv::Mat dst(imgThresholded.rows, imgThresholded.cols, CV_8U, cv::Scalar::all(0));

	cv::Scalar color(0, 0, 0);
	cv::Scalar color2(255, 255, 255);

	//biggest area calculation
	std::vector<std::vector<cv::Point>> contours; // Vector for storing contour
	std::vector<cv::Vec4i> hierarchy;
	cv::Rect bounding_rect;

	findContours(imgThresholded, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); // Find the contours in the image
	if (contours.size() == 0){
		return center;
	}

	double largest_area = 0;
	double area = 0;
	size_t largest_contour_index = 0;
	for (size_t i = 0; i < contours.size(); i++) // iterate through each contour.
	{
		area = cv::contourArea(contours[i], false);  //  Find the area of contour
		if (area > largest_area){
			largest_area = area;
			largest_contour_index = i;                //Store the index of largest contour
		}
	}

	//validate gate area
	if (largest_area < smallestGateArea){
		return cv::Point2i(-1, -1);
	}

	//find center
	if (contours.size() > largest_contour_index){
		cv::Moments M = cv::moments(contours[largest_contour_index]);
		center = cv::Point2i((int)(M.m10 / M.m00), (int)(M.m01 / M.m00));
	}
	else {
		assert(false);
	}

/*
	//Cutting out gate from ball frame
	bounding_rect = cv::boundingRect(contours[largest_contour_index]);
	bounding_rect.height = bounding_rect.height * growGateHeight;
	rectangle(HSVRanges[BALL], bounding_rect.tl(), bounding_rect.br(), color, -1, 8, 0);
	//for clear visual:
	rectangle(frameBGR, bounding_rect.tl(), bounding_rect.br(), color, -1, 8, 0);
	if (HSVRanges.find(SIGHT_MASK) != HSVRanges.end()) {
		rectangle(HSVRanges[SIGHT_MASK], bounding_rect.tl(), bounding_rect.br(), color, -1, 8, 0);
	}
*/
	cv::Scalar color4(255, 0, 0);

	cv::RotatedRect bounding_rect2 = cv::minAreaRect(contours[largest_contour_index]);
	cv::Point2f rect_points[4]; bounding_rect2.points(rect_points);
	int min_dist = INT_MAX;
	int min_index = 0;
	int min_dist2 = INT_MAX;
	int min_index2 = 0;
	for (int i = 0; i < 4; i++){
		int dist = cv::norm(rect_points[i] - cv::Point2f(imgThresholded.cols / 2, imgThresholded.rows / 2));
		if (dist < min_dist){
			min_dist2 = min_dist;
			min_index2 = min_index;
			min_dist = dist;
			min_index = i;
		}
		else if (dist < min_dist2){
			min_dist2 = dist;
			min_index2 = i;
		}
	}
	center = (rect_points[min_index]+ rect_points[min_index2])/2;
	circle(frameBGR,  center, 7, color, -1, 8, 0);

	/*
	int shift = bounding_rect2.size.height * 0.09 +0.2;
	//std::cout << "shift: " << shift << " height: " << bounding_rect2.size.height << std::endl;
	for (int j = 0; j < 4; j++) {
		line(frameBGR, rect_points[j], rect_points[(j + 1) % 4], color2, 1, 8);

		std::vector<cv::Point2i> points;
		points.push_back(rect_points[j]);
		points.push_back(rect_points[(j + 1) % 4]);
		cv::Vec4f newLine;
		cv::fitLine(points, newLine, CV_DIST_L2, 0, 0.1, 0.1);
		//std::cout << rect_points[j] << ", " << rect_points[(j + 1) % 4] << ": " << atan2(newLine[1], newLine[0])*180/PI << std::endl;
		if (abs(atan2(newLine[1], newLine[0]) * 180 / PI) < 45) {
			newLine[3] += shift; // shift line down
			drawLine(frameBGR, HSVRanges[BALL], newLine, 1, cv::Scalar(0, 255 * (1 + 0.3), 0));
			drawLine(frameBGR, HSVRanges[SIGHT_MASK], newLine, 1, cv::Scalar(0, 255 * (1 + 0.3), 0));
		}

	}
	*/

	// find out lowest line
	return center;
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <limits>
#include "library/io_state.h"
#include "library/util.h"
#include "library/app_builder.h"
#include "library/vm/vm.h"
#include "library/vm/vm_expr.h"
#include "library/vm/vm_format.h"
#include "library/vm/vm_list.h"
#include "library/vm/vm_nat.h"
#include "library/vm/vm_name.h"
#include "library/vm/vm_option.h"
#include "library/tactic/tactic_state.h"
#include "library/tactic/smt/congruence_closure.h"
#include "library/tactic/smt/hinst_lemmas.h"
#include "library/tactic/smt/ematch.h"

namespace lean {
struct vm_cc_state : public vm_external {
    congruence_closure::state m_val;
    vm_cc_state(congruence_closure::state const & v):m_val(v) {}
    virtual ~vm_cc_state() {}
    virtual void dealloc() override {
        this->~vm_cc_state(); get_vm_allocator().deallocate(sizeof(vm_cc_state), this);
    }
    virtual vm_external * ts_clone(vm_clone_fn const &) override { return new vm_cc_state(m_val); }
    virtual vm_external * clone(vm_clone_fn const &) override { return new (get_vm_allocator().allocate(sizeof(vm_cc_state))) vm_cc_state(m_val); }
};

bool is_cc_state(vm_obj const & o) {
    return is_external(o) && dynamic_cast<vm_cc_state*>(to_external(o));
}

congruence_closure::state const & to_cc_state(vm_obj const & o) {
    lean_vm_check(dynamic_cast<vm_cc_state*>(to_external(o)));
    return static_cast<vm_cc_state*>(to_external(o))->m_val;
}

vm_obj to_obj(congruence_closure::state const & s) {
    return mk_vm_external(new (get_vm_allocator().allocate(sizeof(vm_cc_state))) vm_cc_state(s));
}

/*
structure cc_config :=
(ignore_instances : bool)
(ac               : bool)
(ho_fns           : option (list name))
(em               : bool)
*/
pair<name_set, congruence_closure::config> to_ho_fns_cc_config(vm_obj const & cfg) {
    congruence_closure::config c;
    name_set ho_fns;
    c.m_ignore_instances = to_bool(cfield(cfg, 0));
    c.m_ac               = to_bool(cfield(cfg, 1));
    if (is_none(cfield(cfg, 2))) {
        c.m_all_ho       = true;
    } else {
        c.m_all_ho       = false;
        ho_fns           = to_name_set(to_list_name(get_some_value(cfield(cfg, 2))));
    }
    c.m_em               = to_bool(cfield(cfg, 3));
    return mk_pair(ho_fns, c);
}

static congruence_closure::state mk_core(vm_obj const & cfg) {
    congruence_closure::config c;
    name_set ho_fns;
    std::tie(ho_fns, c) = to_ho_fns_cc_config(cfg);
    return congruence_closure::state(ho_fns, c);
}

vm_obj cc_state_mk_core(vm_obj const & cfg) {
    return to_obj(mk_core(cfg));
}

vm_obj cc_state_mk_using_hs_core(vm_obj const & cfg, vm_obj const & _s) {
    tactic_state const & s   = tactic::to_state(_s);
    optional<metavar_decl> g = s.get_main_goal_decl();
    if (!g) return mk_no_goals_exception(s);
    try {
        local_context lctx          = g->get_context();
        type_context ctx            = mk_type_context_for(s);
        defeq_can_state dcs         = s.dcs();
        congruence_closure::state r = mk_core(cfg);
        congruence_closure cc(ctx, r, dcs);
        lctx.for_each([&](local_decl const & d) {
                if (ctx.is_prop(d.get_type())) {
                    cc.add(d.get_type(), d.mk_ref(), 0);
                }
            });
        tactic_state new_s = set_dcs(s, dcs);
        return tactic::mk_success(to_obj(r), new_s);
    } catch (exception & ex) {
        return tactic::mk_exception(ex, s);
    }
}

vm_obj cc_state_pp_core(vm_obj const & ccs, vm_obj const & nonsingleton, vm_obj const & _s) {
    tactic_state const & s   = tactic::to_state(_s);
    type_context ctx         = mk_type_context_for(s);
    formatter_factory const & fmtf = get_global_ios().get_formatter_factory();
    formatter fmt            = fmtf(s.env(), s.get_options(), ctx);
    format r                 = to_cc_state(ccs).pp_eqcs(fmt, to_bool(nonsingleton));
    return tactic::mk_success(to_obj(r), s);
}

vm_obj cc_state_pp_eqc(vm_obj const & ccs, vm_obj const & e, vm_obj const & _s) {
    tactic_state const & s   = tactic::to_state(_s);
    type_context ctx         = mk_type_context_for(s);
    formatter_factory const & fmtf = get_global_ios().get_formatter_factory();
    formatter fmt            = fmtf(s.env(), s.get_options(), ctx);
    format r                 = to_cc_state(ccs).pp_eqc(fmt, to_expr(e));
    return tactic::mk_success(to_obj(r), s);
}

vm_obj cc_state_next(vm_obj const & ccs, vm_obj const & e) {
    return to_obj(to_cc_state(ccs).get_next(to_expr(e)));
}

vm_obj cc_state_root(vm_obj const & ccs, vm_obj const & e) {
    return to_obj(to_cc_state(ccs).get_root(to_expr(e)));
}

vm_obj cc_state_is_cg_root(vm_obj const & ccs, vm_obj const & e) {
    return mk_vm_bool(to_cc_state(ccs).is_congr_root(to_expr(e)));
}

vm_obj cc_state_roots_core(vm_obj const & ccs, vm_obj const & nonsingleton) {
    buffer<expr> roots;
    to_cc_state(ccs).get_roots(roots, to_bool(nonsingleton));
    return to_obj(roots);
}

vm_obj cc_state_inconsistent(vm_obj const & ccs) {
    return mk_vm_bool(to_cc_state(ccs).inconsistent());
}

vm_obj cc_state_mt(vm_obj const & ccs, vm_obj const & e) {
    return mk_vm_nat(to_cc_state(ccs).get_mt(to_expr(e)));
}

vm_obj cc_state_gmt(vm_obj const & ccs) {
    return mk_vm_nat(to_cc_state(ccs).get_gmt());
}

vm_obj cc_state_inc_gmt(vm_obj const & ccs) {
    congruence_closure::state s = to_cc_state(ccs);
    s.inc_gmt();
    return to_obj(s);
}

#define cc_state_proc(CODE)                                     \
    tactic_state const & s   = tactic::to_state(_s);             \
    try {                                                       \
        type_context ctx            = mk_type_context_for(s);   \
        congruence_closure::state S = to_cc_state(ccs);         \
        defeq_can_state dcs         = s.dcs();                  \
        congruence_closure cc(ctx, S, dcs);                     \
        CODE                                                    \
    } catch (exception & ex) {                                  \
        return tactic::mk_exception(ex, s);                      \
    }

#define cc_state_updt_proc(CODE) cc_state_proc({ CODE; return tactic::mk_success(to_obj(S), set_dcs(s, dcs)); })

vm_obj cc_state_add(vm_obj const & ccs, vm_obj const & H, vm_obj const & _s) {
    cc_state_updt_proc({
            expr type                   = ctx.infer(to_expr(H));
            if (ctx.is_prop(type))
                return tactic::mk_exception("cc_state.add failed, given expression is not a proof term", s);
            cc.add(type, to_expr(H), 0);
    });
}

vm_obj cc_state_internalize(vm_obj const & ccs, vm_obj const & e, vm_obj const & _s) {
    cc_state_updt_proc({
            cc.internalize(to_expr(e), 0);
        });
}

vm_obj cc_state_is_eqv(vm_obj const & ccs, vm_obj const & e1, vm_obj const & e2, vm_obj const & _s) {
    cc_state_proc({
            bool r = cc.is_eqv(to_expr(e1), to_expr(e2));
            return tactic::mk_success(mk_vm_bool(r), s);
        });
}

vm_obj cc_state_is_not_eqv(vm_obj const & ccs, vm_obj const & e1, vm_obj const & e2, vm_obj const & _s) {
    cc_state_proc({
            bool r = cc.is_not_eqv(to_expr(e1), to_expr(e2));
            return tactic::mk_success(mk_vm_bool(r), s);
        });
}

vm_obj cc_state_eqv_proof(vm_obj const & ccs, vm_obj const & e1, vm_obj const & e2, vm_obj const & _s) {
    cc_state_proc({
            if (optional<expr> r = cc.get_proof(to_expr(e1), to_expr(e2))) {
                return tactic::mk_success(to_obj(*r), s);
            } else {
                return tactic::mk_exception("cc_state.eqv_proof failed to build proof", s);
            }
        });
}

vm_obj cc_state_proof_for(vm_obj const & ccs, vm_obj const & e, vm_obj const & _s) {
    cc_state_proc({
            if (optional<expr> r = cc.get_eq_proof(to_expr(e), mk_true())) {
                return tactic::mk_success(to_obj(mk_of_eq_true(cc.ctx(), *r)), s);
            } else {
                return tactic::mk_exception("cc_state.get_proof_for failed to build proof", s);
            }
        });
}

vm_obj cc_state_refutation_for(vm_obj const & ccs, vm_obj const & e, vm_obj const & _s) {
    cc_state_proc({
            if (optional<expr> r = cc.get_eq_proof(to_expr(e), mk_false())) {
                return tactic::mk_success(to_obj(mk_not_of_eq_false(cc.ctx(), *r)), s);
            } else {
                return tactic::mk_exception("cc_state.get_refutation_for failed to build proof", s);
            }
        });
}

vm_obj cc_state_proof_for_false(vm_obj const & ccs, vm_obj const & _s) {
    cc_state_proc({
            if (auto pr = cc.get_inconsistency_proof()) {
                return tactic::mk_success(to_obj(*pr), s);
            } else {
                return tactic::mk_exception("cc_state.false_proof failed, state is not inconsistent", s);
            }
        });
}

void initialize_congruence_tactics() {
    DECLARE_VM_BUILTIN(name({"cc_state", "mk_core"}),              cc_state_mk_core);
    DECLARE_VM_BUILTIN(name({"cc_state", "next"}),                 cc_state_next);
    DECLARE_VM_BUILTIN(name({"cc_state", "mk_using_hs_core"}),     cc_state_mk_using_hs_core);
    DECLARE_VM_BUILTIN(name({"cc_state", "pp_core"}),              cc_state_pp_core);
    DECLARE_VM_BUILTIN(name({"cc_state", "pp_eqc"}),               cc_state_pp_eqc);
    DECLARE_VM_BUILTIN(name({"cc_state", "next"}),                 cc_state_next);
    DECLARE_VM_BUILTIN(name({"cc_state", "root"}),                 cc_state_root);
    DECLARE_VM_BUILTIN(name({"cc_state", "mt"}),                   cc_state_mt);
    DECLARE_VM_BUILTIN(name({"cc_state", "gmt"}),                  cc_state_gmt);
    DECLARE_VM_BUILTIN(name({"cc_state", "inc_gmt"}),              cc_state_inc_gmt);
    DECLARE_VM_BUILTIN(name({"cc_state", "is_cg_root"}),           cc_state_is_cg_root);
    DECLARE_VM_BUILTIN(name({"cc_state", "roots_core"}),           cc_state_roots_core);
    DECLARE_VM_BUILTIN(name({"cc_state", "internalize"}),          cc_state_internalize);
    DECLARE_VM_BUILTIN(name({"cc_state", "add"}),                  cc_state_add);
    DECLARE_VM_BUILTIN(name({"cc_state", "is_eqv"}),               cc_state_is_eqv);
    DECLARE_VM_BUILTIN(name({"cc_state", "is_not_eqv"}),           cc_state_is_not_eqv);
    DECLARE_VM_BUILTIN(name({"cc_state", "inconsistent"}),         cc_state_inconsistent);
    DECLARE_VM_BUILTIN(name({"cc_state", "proof_for_false"}),      cc_state_proof_for_false);
    DECLARE_VM_BUILTIN(name({"cc_state", "eqv_proof"}),            cc_state_eqv_proof);
    DECLARE_VM_BUILTIN(name({"cc_state", "proof_for"}),            cc_state_proof_for);
    DECLARE_VM_BUILTIN(name({"cc_state", "refutation_for"}),       cc_state_refutation_for);
}

void finalize_congruence_tactics() {
}
}
<commit_msg>fix(library/tactic/smt/congruence_tactics): fix cc_state.add<commit_after>/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <limits>
#include "library/io_state.h"
#include "library/util.h"
#include "library/app_builder.h"
#include "library/vm/vm.h"
#include "library/vm/vm_expr.h"
#include "library/vm/vm_format.h"
#include "library/vm/vm_list.h"
#include "library/vm/vm_nat.h"
#include "library/vm/vm_name.h"
#include "library/vm/vm_option.h"
#include "library/tactic/tactic_state.h"
#include "library/tactic/smt/congruence_closure.h"
#include "library/tactic/smt/hinst_lemmas.h"
#include "library/tactic/smt/ematch.h"

namespace lean {
struct vm_cc_state : public vm_external {
    congruence_closure::state m_val;
    vm_cc_state(congruence_closure::state const & v):m_val(v) {}
    virtual ~vm_cc_state() {}
    virtual void dealloc() override {
        this->~vm_cc_state(); get_vm_allocator().deallocate(sizeof(vm_cc_state), this);
    }
    virtual vm_external * ts_clone(vm_clone_fn const &) override { return new vm_cc_state(m_val); }
    virtual vm_external * clone(vm_clone_fn const &) override { return new (get_vm_allocator().allocate(sizeof(vm_cc_state))) vm_cc_state(m_val); }
};

bool is_cc_state(vm_obj const & o) {
    return is_external(o) && dynamic_cast<vm_cc_state*>(to_external(o));
}

congruence_closure::state const & to_cc_state(vm_obj const & o) {
    lean_vm_check(dynamic_cast<vm_cc_state*>(to_external(o)));
    return static_cast<vm_cc_state*>(to_external(o))->m_val;
}

vm_obj to_obj(congruence_closure::state const & s) {
    return mk_vm_external(new (get_vm_allocator().allocate(sizeof(vm_cc_state))) vm_cc_state(s));
}

/*
structure cc_config :=
(ignore_instances : bool)
(ac               : bool)
(ho_fns           : option (list name))
(em               : bool)
*/
pair<name_set, congruence_closure::config> to_ho_fns_cc_config(vm_obj const & cfg) {
    congruence_closure::config c;
    name_set ho_fns;
    c.m_ignore_instances = to_bool(cfield(cfg, 0));
    c.m_ac               = to_bool(cfield(cfg, 1));
    if (is_none(cfield(cfg, 2))) {
        c.m_all_ho       = true;
    } else {
        c.m_all_ho       = false;
        ho_fns           = to_name_set(to_list_name(get_some_value(cfield(cfg, 2))));
    }
    c.m_em               = to_bool(cfield(cfg, 3));
    return mk_pair(ho_fns, c);
}

static congruence_closure::state mk_core(vm_obj const & cfg) {
    congruence_closure::config c;
    name_set ho_fns;
    std::tie(ho_fns, c) = to_ho_fns_cc_config(cfg);
    return congruence_closure::state(ho_fns, c);
}

vm_obj cc_state_mk_core(vm_obj const & cfg) {
    return to_obj(mk_core(cfg));
}

vm_obj cc_state_mk_using_hs_core(vm_obj const & cfg, vm_obj const & _s) {
    tactic_state const & s   = tactic::to_state(_s);
    optional<metavar_decl> g = s.get_main_goal_decl();
    if (!g) return mk_no_goals_exception(s);
    try {
        local_context lctx          = g->get_context();
        type_context ctx            = mk_type_context_for(s);
        defeq_can_state dcs         = s.dcs();
        congruence_closure::state r = mk_core(cfg);
        congruence_closure cc(ctx, r, dcs);
        lctx.for_each([&](local_decl const & d) {
                if (ctx.is_prop(d.get_type())) {
                    cc.add(d.get_type(), d.mk_ref(), 0);
                }
            });
        tactic_state new_s = set_dcs(s, dcs);
        return tactic::mk_success(to_obj(r), new_s);
    } catch (exception & ex) {
        return tactic::mk_exception(ex, s);
    }
}

vm_obj cc_state_pp_core(vm_obj const & ccs, vm_obj const & nonsingleton, vm_obj const & _s) {
    tactic_state const & s   = tactic::to_state(_s);
    type_context ctx         = mk_type_context_for(s);
    formatter_factory const & fmtf = get_global_ios().get_formatter_factory();
    formatter fmt            = fmtf(s.env(), s.get_options(), ctx);
    format r                 = to_cc_state(ccs).pp_eqcs(fmt, to_bool(nonsingleton));
    return tactic::mk_success(to_obj(r), s);
}

vm_obj cc_state_pp_eqc(vm_obj const & ccs, vm_obj const & e, vm_obj const & _s) {
    tactic_state const & s   = tactic::to_state(_s);
    type_context ctx         = mk_type_context_for(s);
    formatter_factory const & fmtf = get_global_ios().get_formatter_factory();
    formatter fmt            = fmtf(s.env(), s.get_options(), ctx);
    format r                 = to_cc_state(ccs).pp_eqc(fmt, to_expr(e));
    return tactic::mk_success(to_obj(r), s);
}

vm_obj cc_state_next(vm_obj const & ccs, vm_obj const & e) {
    return to_obj(to_cc_state(ccs).get_next(to_expr(e)));
}

vm_obj cc_state_root(vm_obj const & ccs, vm_obj const & e) {
    return to_obj(to_cc_state(ccs).get_root(to_expr(e)));
}

vm_obj cc_state_is_cg_root(vm_obj const & ccs, vm_obj const & e) {
    return mk_vm_bool(to_cc_state(ccs).is_congr_root(to_expr(e)));
}

vm_obj cc_state_roots_core(vm_obj const & ccs, vm_obj const & nonsingleton) {
    buffer<expr> roots;
    to_cc_state(ccs).get_roots(roots, to_bool(nonsingleton));
    return to_obj(roots);
}

vm_obj cc_state_inconsistent(vm_obj const & ccs) {
    return mk_vm_bool(to_cc_state(ccs).inconsistent());
}

vm_obj cc_state_mt(vm_obj const & ccs, vm_obj const & e) {
    return mk_vm_nat(to_cc_state(ccs).get_mt(to_expr(e)));
}

vm_obj cc_state_gmt(vm_obj const & ccs) {
    return mk_vm_nat(to_cc_state(ccs).get_gmt());
}

vm_obj cc_state_inc_gmt(vm_obj const & ccs) {
    congruence_closure::state s = to_cc_state(ccs);
    s.inc_gmt();
    return to_obj(s);
}

#define cc_state_proc(CODE)                                     \
    tactic_state const & s   = tactic::to_state(_s);             \
    try {                                                       \
        type_context ctx            = mk_type_context_for(s);   \
        congruence_closure::state S = to_cc_state(ccs);         \
        defeq_can_state dcs         = s.dcs();                  \
        congruence_closure cc(ctx, S, dcs);                     \
        CODE                                                    \
    } catch (exception & ex) {                                  \
        return tactic::mk_exception(ex, s);                      \
    }

#define cc_state_updt_proc(CODE) cc_state_proc({ CODE; return tactic::mk_success(to_obj(S), set_dcs(s, dcs)); })

vm_obj cc_state_add(vm_obj const & ccs, vm_obj const & H, vm_obj const & _s) {
    cc_state_updt_proc({
            expr type                   = ctx.infer(to_expr(H));
            if (!ctx.is_prop(type))
                return tactic::mk_exception("cc_state.add failed, given expression is not a proof term", s);
            cc.add(type, to_expr(H), 0);
    });
}

vm_obj cc_state_internalize(vm_obj const & ccs, vm_obj const & e, vm_obj const & _s) {
    cc_state_updt_proc({
            cc.internalize(to_expr(e), 0);
        });
}

vm_obj cc_state_is_eqv(vm_obj const & ccs, vm_obj const & e1, vm_obj const & e2, vm_obj const & _s) {
    cc_state_proc({
            bool r = cc.is_eqv(to_expr(e1), to_expr(e2));
            return tactic::mk_success(mk_vm_bool(r), s);
        });
}

vm_obj cc_state_is_not_eqv(vm_obj const & ccs, vm_obj const & e1, vm_obj const & e2, vm_obj const & _s) {
    cc_state_proc({
            bool r = cc.is_not_eqv(to_expr(e1), to_expr(e2));
            return tactic::mk_success(mk_vm_bool(r), s);
        });
}

vm_obj cc_state_eqv_proof(vm_obj const & ccs, vm_obj const & e1, vm_obj const & e2, vm_obj const & _s) {
    cc_state_proc({
            if (optional<expr> r = cc.get_proof(to_expr(e1), to_expr(e2))) {
                return tactic::mk_success(to_obj(*r), s);
            } else {
                return tactic::mk_exception("cc_state.eqv_proof failed to build proof", s);
            }
        });
}

vm_obj cc_state_proof_for(vm_obj const & ccs, vm_obj const & e, vm_obj const & _s) {
    cc_state_proc({
            if (optional<expr> r = cc.get_eq_proof(to_expr(e), mk_true())) {
                return tactic::mk_success(to_obj(mk_of_eq_true(cc.ctx(), *r)), s);
            } else {
                return tactic::mk_exception("cc_state.get_proof_for failed to build proof", s);
            }
        });
}

vm_obj cc_state_refutation_for(vm_obj const & ccs, vm_obj const & e, vm_obj const & _s) {
    cc_state_proc({
            if (optional<expr> r = cc.get_eq_proof(to_expr(e), mk_false())) {
                return tactic::mk_success(to_obj(mk_not_of_eq_false(cc.ctx(), *r)), s);
            } else {
                return tactic::mk_exception("cc_state.get_refutation_for failed to build proof", s);
            }
        });
}

vm_obj cc_state_proof_for_false(vm_obj const & ccs, vm_obj const & _s) {
    cc_state_proc({
            if (auto pr = cc.get_inconsistency_proof()) {
                return tactic::mk_success(to_obj(*pr), s);
            } else {
                return tactic::mk_exception("cc_state.false_proof failed, state is not inconsistent", s);
            }
        });
}

void initialize_congruence_tactics() {
    DECLARE_VM_BUILTIN(name({"cc_state", "mk_core"}),              cc_state_mk_core);
    DECLARE_VM_BUILTIN(name({"cc_state", "next"}),                 cc_state_next);
    DECLARE_VM_BUILTIN(name({"cc_state", "mk_using_hs_core"}),     cc_state_mk_using_hs_core);
    DECLARE_VM_BUILTIN(name({"cc_state", "pp_core"}),              cc_state_pp_core);
    DECLARE_VM_BUILTIN(name({"cc_state", "pp_eqc"}),               cc_state_pp_eqc);
    DECLARE_VM_BUILTIN(name({"cc_state", "next"}),                 cc_state_next);
    DECLARE_VM_BUILTIN(name({"cc_state", "root"}),                 cc_state_root);
    DECLARE_VM_BUILTIN(name({"cc_state", "mt"}),                   cc_state_mt);
    DECLARE_VM_BUILTIN(name({"cc_state", "gmt"}),                  cc_state_gmt);
    DECLARE_VM_BUILTIN(name({"cc_state", "inc_gmt"}),              cc_state_inc_gmt);
    DECLARE_VM_BUILTIN(name({"cc_state", "is_cg_root"}),           cc_state_is_cg_root);
    DECLARE_VM_BUILTIN(name({"cc_state", "roots_core"}),           cc_state_roots_core);
    DECLARE_VM_BUILTIN(name({"cc_state", "internalize"}),          cc_state_internalize);
    DECLARE_VM_BUILTIN(name({"cc_state", "add"}),                  cc_state_add);
    DECLARE_VM_BUILTIN(name({"cc_state", "is_eqv"}),               cc_state_is_eqv);
    DECLARE_VM_BUILTIN(name({"cc_state", "is_not_eqv"}),           cc_state_is_not_eqv);
    DECLARE_VM_BUILTIN(name({"cc_state", "inconsistent"}),         cc_state_inconsistent);
    DECLARE_VM_BUILTIN(name({"cc_state", "proof_for_false"}),      cc_state_proof_for_false);
    DECLARE_VM_BUILTIN(name({"cc_state", "eqv_proof"}),            cc_state_eqv_proof);
    DECLARE_VM_BUILTIN(name({"cc_state", "proof_for"}),            cc_state_proof_for);
    DECLARE_VM_BUILTIN(name({"cc_state", "refutation_for"}),       cc_state_refutation_for);
}

void finalize_congruence_tactics() {
}
}
<|endoftext|>


<commit_before>/*
	This class parse a command that was given from the command line.
	You can use `makemaker` to run all these commands or just `mm` (an alias).

	Specification TODO:
		- Think about <command> abstraction. What if user wants to compiler
		  a lot of files in a single command? ("g++ rola.cpp hard.cpp shora.c -o main")

	Current specification:

		# Creating/Editing Target:
			This command will create a target if it doesn't exist,
			and edit it, otherwise.

			mm <target>

		# Adding command to target
			mm <target> <command>
			mm <target> "<literal_command>"

			Where <command> is just:
				<compiler> <all|*.extension|filename.*|filename.extension> <flags>

			Where "<literal_command>" can be any command you like:
				grep -js | "pasdpokasd"

		# Replacing command:
			mm <target>:<number_line> <command>
			mm <target>:<number_line> "<literal_command>"

			Where <command> is just:
				<compiler> <all|*.extension|filename.*|filename.extension> <flags>

		# Deleting target
			mm ! <target>

		# Deleting full command
			mm ! <target>:<number_line>

		# Deleting compiler
			mm ! <target>:<number_line> -c

		# Deleting filename
			mm ! <target>:<number_line> -f <all|*.extension|filename.*|filename.extension>

		# Deleting flag
			mm ! <target>:<number_line> -F <flags>

*/

#ifndef COMMAND_INTERPRETER_HPP_
#define COMMAND_INTERPRETER_HPP_

#include <stdio.h>
#include <iostream>
#include <string>
#include <string.h>
#include <vector>
#include <exception>
#include "MakeFile.hpp"
#include "Target.hpp"
#include "Command.hpp"
#include "exceptions/InsufficientArguments.hpp"

class CommandInterpreter {

 public:
 	CommandInterpreter (int argc, char** argv) : _argc(argc), _argv(argv), _mk("makefile", false)  {} /* ----------- the problem may be originated here ----------- */

 	MakeFile parse() {
 		if (_argc < 2) {
 			print_usage();
 		} else if (!strcasecmp(_argv[1], "list")){
	 		parse_list();
	 	} else {
 			if(_argc == 2) {
	 			print_usage();
	 		} else if(!strcasecmp(_argv[1], "add")) {
	 			parse_add();
	 		} else if(!strcasecmp(_argv[1], "remove")) {
	 			parse_delete();
	 		} else if(!strcasecmp(_argv[1], "edit")) {
	 			parse_edit();
	 		} else {
	 			throw -2;
	 		}
	 		_mk.save();
 		}
		return _mk;
 	}

 private:

 	void parse_add() {

		Target target(_argv[2]);
 		std::string targetName = std::string(target.get_title());

		char* name = get_target_name(targetName);
		target = _mk.get_or_create_target(name);

		Command command;
 		if (_argc > 3) {
 			std::string myAwesomeCommand = std::string(_argv[3]) + " ";
			for(int i = 4; i<_argc; i++) {
				myAwesomeCommand += std::string(_argv[i]) + " ";
			}
			command.parse(myAwesomeCommand);
			target.add_command(command);
		}

 		// TODO:
		//	Differences between "mm <target> <command>" and "mm <target>:<lineOfCommand> <command>", where:
		//      - In the first case, add a new target.
		//      - In the second case, overwrites a command (given by the "lineOfCommand").

 		// 	Compiler compiler(_argv[3]);
 		// 	command.set_compiler(compiler);

 		// 	std::vector<File> files = parse_files();
 		// 	command.add_files(files);

 		// 	int startsAt = 4 + files.size();
 		// 	std::vector<Flag> flags = parse_flags(startsAt);
 		// 	command.add_flags(flags);

 		_mk.add_target(target, true);

 	}

 	void parse_delete() {
 		if(_argc == 3) {
 			char* targetName = get_target_name(std::string(_argv[2]));
 			Target t(targetName);

 			int line_number = get_line_number(std::string(_argv[2]));
 			if (line_number != -1) {
 				_mk.remove_command_from_target(t, line_number);
 			} else {
	 			_mk.remove_target(t);
 			}
 		}
 	}

 	void parse_edit() {
 		int lineNumber = -1;
		Target* target = new Target(_argv[2]);
 		std::string targetName = std::string(target->get_title());

		lineNumber = get_line_number(targetName);
		char* name = get_target_name(targetName);
		target = _mk.get_target_as_pointer(name);

		Command command;

 		if(lineNumber == -1 && target != NULL && _argc == 4) {
 			name = _argv[3];
 			target->set_title(name);
 		} else if(lineNumber != -1) {
 			Target old = *target;
 			Compiler compiler(_argv[3]);
 			command.set_compiler(compiler);
 			std::vector<File> files = parse_files();
 			command.add_files(files);
 			int startsAt = 4 + files.size();
 			std::vector<Flag> flags = parse_flags(startsAt);
 			command.add_flags(flags);
 			old.add_command(command, lineNumber);
 			_mk.add_target(old, true);
 		} else if (target == NULL) {
 			throw -3;
 		} else {
 			throw -4;
 		}
 	}

 	void parse_list() {
 		if (_argc == 2) {
	 		std::cout << _mk.list_targets() << std::endl;
 			return;
 		} else if (_argc == 3) {
 			if (!strcasecmp(_argv[2], "-A")) {
 				std::cout << _mk.to_string() << std::endl;
 			} else {
	 			std::cout << _mk.list_target(_argv[2]) << std::endl;
 			}
 		} else {
 			throw -2;
 		}
 	}

 	std::vector<Flag> parse_flags(const int startsAt) {
 		int i = startsAt;
 		std::vector<Flag> flags;

 		while (i < _argc) {
 			std::string flg = _argv[i];
 			Flag flag(flg);
 			flags.push_back(flag);
 			i++;
 		}

 		return flags;
 	}

 	std::vector<File> parse_files() {
 		int i = 4;
 		std::vector<File> files;
 		std::string input = _argv[4];

 		while (input[0] != '-') {
 			File file(input);
 			files.push_back(file);
 			i++;
	 		input = _argv[i];
 		}

 		return files;
 	}

 	int get_line_number(std::string targetName) {
 		int temp = targetName.find_last_of(":");
 		if (temp != -1) {
 			temp = std::stoi(targetName.substr(temp + 1, targetName.length()));
 		}
 		return temp;
 	}

 	char* get_target_name(std::string targetName) {
 		int temp = targetName.find_last_of(":");
 		if (temp != -1) {
	 		std::string newTargetName = targetName.substr(0, temp);
	 		char* name = (char*) malloc (newTargetName.size() + 1);
    		memcpy(name, newTargetName.c_str(), newTargetName.size() + 1);
	 		return name;
	 	}
	 	char* name = (char*) malloc (targetName.size() + 1);
    	memcpy(name, targetName.c_str(), targetName.size() + 1);
    	return name;
 	}

 	void print_usage() {
 		std::cout << usage << std::endl;
 	}

 	int _argc;
 	char** _argv;
 	MakeFile _mk;

 	const std::string usage = "======================================================================\n" \
 				"    MakeMaker is an easy-to-use tool that generate makefiles.\n" \
 				"\n    Currently there's no GUI interface for this tool, so you must" \
 				"\n    create your makefiles by command line. Following you can check" \
 				"\n    the usage of this tool. Hope it helps you.\n"
 				"\n    This software's distribued under the MIT license.\n" \
 				"======================================================================\n" \
 				"\n    Usage:\n" \
 				"\n\t# Create / Edit a Target:" \
 				"\n\tmm <target>\n" \
 				"\n\t# Adding command to Target:" \
 				"\n\tmm <target> <command>\n";
};
#endif
<commit_msg>New documentation in CommandInterpreter.<commit_after>/*
	This class parse a command that was given from the command line.
	You can use `makemaker` to run all these commands or just `mm` (an alias).

	Current specification:

		# Creating/Editing Target:
			This command will create a target if it doesn't exist,
			and edit it, otherwise.

			mm add <target>

		# Adding command to target
			mm add <target> <command>

			Where <command> is:
				<compiler> <filename.ext, filename2.ext, ...> <flags>

		# Target rename:
			mm edit <oldTargetName> <newTargetName>

		# Replacing command:
			mm edit <target>:<number_line> <command>

			Where <command> is just:
				<compiler> <filename.ext, filename2.ext, ...> <flags>

		# Deleting target
			mm remove <target>

		# Deleting full command
			mm remove <target>:<number_line>

*/

#ifndef COMMAND_INTERPRETER_HPP_
#define COMMAND_INTERPRETER_HPP_

#include <stdio.h>
#include <iostream>
#include <string>
#include <string.h>
#include <vector>
#include <exception>
#include "MakeFile.hpp"
#include "Target.hpp"
#include "Command.hpp"
#include "exceptions/InsufficientArguments.hpp"

class CommandInterpreter {

 public:
 	CommandInterpreter (int argc, char** argv) : _argc(argc), _argv(argv), _mk("makefile", false)  {}

 	MakeFile parse() {
 		if (_argc < 2) {
 			print_usage();
 		} else if (!strcasecmp(_argv[1], "list")){
	 		parse_list();
	 	} else {
 			if(_argc == 2) {
	 			print_usage();
	 		} else if(!strcasecmp(_argv[1], "add")) {
	 			parse_add();
	 		} else if(!strcasecmp(_argv[1], "remove")) {
	 			parse_delete();
	 		} else if(!strcasecmp(_argv[1], "edit")) {
	 			parse_edit();
	 		} else {
	 			throw -2;
	 		}
	 		_mk.save();
 		}
		return _mk;
 	}

 private:

 	void parse_add() {

		Target target(_argv[2]);
 		std::string targetName = std::string(target.get_title());

		char* name = get_target_name(targetName);
		target = _mk.get_or_create_target(name);

		Command command;
 		if (_argc > 3) {
 			std::string myAwesomeCommand = std::string(_argv[3]) + " ";
			for(int i = 4; i<_argc; i++) {
				myAwesomeCommand += std::string(_argv[i]) + " ";
			}
			command.parse(myAwesomeCommand);
			target.add_command(command);
		}

 		// TODO:
		//	Differences between "mm <target> <command>" and "mm <target>:<lineOfCommand> <command>", where:
		//      - In the first case, add a new target.
		//      - In the second case, overwrites a command (given by the "lineOfCommand").

 		// 	Compiler compiler(_argv[3]);
 		// 	command.set_compiler(compiler);

 		// 	std::vector<File> files = parse_files();
 		// 	command.add_files(files);

 		// 	int startsAt = 4 + files.size();
 		// 	std::vector<Flag> flags = parse_flags(startsAt);
 		// 	command.add_flags(flags);

 		_mk.add_target(target, true);

 	}

 	void parse_delete() {
 		if(_argc == 3) {
 			char* targetName = get_target_name(std::string(_argv[2]));
 			Target t(targetName);

 			int line_number = get_line_number(std::string(_argv[2]));
 			if (line_number != -1) {
 				_mk.remove_command_from_target(t, line_number);
 			} else {
	 			_mk.remove_target(t);
 			}
 		}
 	}

 	void parse_edit() {
 		int lineNumber = -1;
		Target* target = new Target(_argv[2]);
 		std::string targetName = std::string(target->get_title());

		lineNumber = get_line_number(targetName);
		char* name = get_target_name(targetName);
		target = _mk.get_target_as_pointer(name);

		Command command;

 		if(lineNumber == -1 && target != NULL && _argc == 4) {
 			name = _argv[3];
 			target->set_title(name);
 		} else if(lineNumber != -1) {
 			Target old = *target;
 			Compiler compiler(_argv[3]);
 			command.set_compiler(compiler);
 			std::vector<File> files = parse_files();
 			command.add_files(files);
 			int startsAt = 4 + files.size();
 			std::vector<Flag> flags = parse_flags(startsAt);
 			command.add_flags(flags);
 			old.add_command(command, lineNumber);
 			_mk.add_target(old, true);
 		} else if (target == NULL) {
 			throw -3;
 		} else {
 			throw -4;
 		}
 	}

 	void parse_list() {
 		if (_argc == 2) {
	 		std::cout << _mk.list_targets() << std::endl;
 			return;
 		} else if (_argc == 3) {
 			if (!strcasecmp(_argv[2], "-A")) {
 				std::cout << _mk.to_string() << std::endl;
 			} else {
	 			std::cout << _mk.list_target(_argv[2]) << std::endl;
 			}
 		} else {
 			throw -2;
 		}
 	}

 	std::vector<Flag> parse_flags(const int startsAt) {
 		int i = startsAt;
 		std::vector<Flag> flags;

 		while (i < _argc) {
 			std::string flg = _argv[i];
 			Flag flag(flg);
 			flags.push_back(flag);
 			i++;
 		}

 		return flags;
 	}

 	std::vector<File> parse_files() {
 		int i = 4;
 		std::vector<File> files;
 		std::string input = _argv[4];

 		while (input[0] != '-') {
 			File file(input);
 			files.push_back(file);
 			i++;
	 		input = _argv[i];
 		}

 		return files;
 	}

 	int get_line_number(std::string targetName) {
 		int temp = targetName.find_last_of(":");
 		if (temp != -1) {
 			temp = std::stoi(targetName.substr(temp + 1, targetName.length()));
 		}
 		return temp;
 	}

 	char* get_target_name(std::string targetName) {
 		int temp = targetName.find_last_of(":");
 		if (temp != -1) {
	 		std::string newTargetName = targetName.substr(0, temp);
	 		char* name = (char*) malloc (newTargetName.size() + 1);
    		memcpy(name, newTargetName.c_str(), newTargetName.size() + 1);
	 		return name;
	 	}
	 	char* name = (char*) malloc (targetName.size() + 1);
    	memcpy(name, targetName.c_str(), targetName.size() + 1);
    	return name;
 	}

 	void print_usage() {
 		std::cout << usage << std::endl;
 	}

 	int _argc;
 	char** _argv;
 	MakeFile _mk;

 	const std::string usage = "======================================================================\n" \
 				"    MakeMaker is an easy-to-use tool that generate makefiles.\n" \
 				"\n    Currently there's no GUI interface for this tool, so you must" \
 				"\n    create your makefiles by command line. Following you can check" \
 				"\n    the usage of this tool. Hope it helps you.\n"
 				"\n    This software's distribued under the MIT license.\n" \
 				"======================================================================\n" \
 				"\n    Usage:\n" \
 				"\n\t# Create / Edit a Target:" \
 				"\n\tmm <target>\n" \
 				"\n\t# Adding command to Target:" \
 				"\n\tmm <target> <command>\n";
};
#endif
<|endoftext|>


<commit_before>/*
    This file is part of Akregator.

    Copyright (C) 2007 Frank Osterfeld <osterfeld@kde.org>

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

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include "subscriptionlistmodel.h"
#include "feed.h"
#include "feedlist.h"
#include "folder.h"
#include "subscriptionlistjobs.h"
#include "treenode.h"

#include <KDebug>
#include <KIconLoader>
#include <KLocalizedString>

#include <QByteArray>
#include <QDataStream>
#include <QFont>
#include <QIcon>
#include <QList>
#include <QMimeData>
#include <QUrl>
#include <QVariant>

#include <cassert>

using namespace Akregator;
using namespace Syndication;

#define AKREGATOR_TREENODE_MIMETYPE "akregator/treenode-id"

namespace {

    QString errorCodeToString( Syndication::ErrorCode err )
    {
        switch ( err )
        {
            case Timeout:
                return i18n( "Timeout on remote server" );
            case UnknownHost:
                return i18n( "Unknown host" );
            case FileNotFound:
                return i18n( "Feed file not found on remote server" );
            case InvalidXml:
                return i18n( "Could not read feed (invalid XML)" );
            case XmlNotAccepted:
                return i18n( "Could not read feed (unknown format)" );
            case InvalidFormat:
                return i18n( "Could not read feed (invalid feed)" );
            case Success:
            case Aborted:
            default:
                return QString();
        }
    }

    static const Akregator::TreeNode* nodeForIndex( const QModelIndex& index, const Akregator::FeedList* feedList )
    {
        return ( !index.isValid() || !feedList ) ? 0 : feedList->findByID( index.internalId() );
    }
}

Akregator::SubscriptionListModel::SubscriptionListModel( const Akregator::FeedList* feedList, QObject* parent ) : QAbstractItemModel( parent ), m_feedList( feedList ), m_beganRemoval( false )
{
    if ( feedList )
    {
        connect( feedList, SIGNAL( signalDestroyed( Akregator::FeedList* ) ),
                 this, SLOT( feedListDestroyed( Akregator::FeedList* ) ) );
        connect( feedList, SIGNAL( signalNodeAdded( Akregator::TreeNode* ) ),
                 this, SLOT( subscriptionAdded( Akregator::TreeNode* ) ) );
        connect( feedList, SIGNAL( signalAboutToRemoveNode( Akregator::TreeNode* ) ),
                 this, SLOT( aboutToRemoveSubscription( Akregator::TreeNode* ) ) );
        connect( feedList, SIGNAL( signalNodeRemoved( Akregator::TreeNode* ) ),
                   this, SLOT( subscriptionRemoved( Akregator::TreeNode* ) ) );
        connect( feedList, SIGNAL( signalNodeChanged( Akregator::TreeNode* ) ),
                 this, SLOT( subscriptionChanged( Akregator::TreeNode* ) ) );
        connect( feedList, SIGNAL( fetchStarted( Akregator::Feed* ) ),
                 this, SLOT( fetchStarted( Akregator::Feed* ) ) );
        connect( feedList, SIGNAL( fetched( Akregator::Feed* ) ),
                 this, SLOT( fetched( Akregator::Feed* ) ) );
        connect( feedList, SIGNAL( fetchAborted( Akregator::Feed* ) ),
                 this, SLOT( fetchAborted( Akregator::Feed* ) ) );
    }
}

int Akregator::SubscriptionListModel::columnCount( const QModelIndex& ) const
{
    return 3;
}

int Akregator::SubscriptionListModel::rowCount( const QModelIndex& parent ) const
{
    if ( !parent.isValid() )
        return 1;

    const Akregator::TreeNode* const node = nodeForIndex( parent, m_feedList );
    return node ? node->children().count() : 0;
}

uint Akregator::SubscriptionListModel::nodeIdForIndex( const QModelIndex& idx ) const
{
    return idx.isValid() ? idx.internalId() : 0;
}

QVariant Akregator::SubscriptionListModel::data( const QModelIndex& index, int role ) const
{
    if ( !index.isValid() )
        return QVariant();

    const Akregator::TreeNode* const node = nodeForIndex( index, m_feedList );

    if ( !node )
        return QVariant();

    switch ( role )
    {
        case Qt::EditRole:
        case Qt::DisplayRole:
        {
            switch ( index.column() )
            {
                case TitleColumn:
                    return node->title();
                case UnreadCountColumn:
                    return node->unread();
                case TotalCountColumn:
                    return node->totalCount();
            }
        }
        case Qt::FontRole:
        {
            if(node->unread() > 0) {
                QFont font;
                font.setBold(true);
                return font;
            }
        }
        case Qt::ToolTipRole:
        {
            if ( node->isGroup() || node->isAggregation() )
                return QString();
            const Feed* const feed = qobject_cast<const Feed* const>( node );
            if ( feed && feed->fetchErrorOccurred() )
                return i18n( "Could not fetch feed: %1", errorCodeToString( feed->fetchErrorCode() ) );
            else
                return QString();
        }
        case Qt::DecorationRole:
        {
            if ( index.column() != TitleColumn )
                return QVariant();
            const Feed* const feed = qobject_cast<const Feed* const>( node );
            return feed && feed->isFetching() ? node->icon().pixmap( KIconLoader::SizeSmall, QIcon::Active ) : node->icon();
        }
        case SubscriptionIdRole:
        {
            return node->id();
        }
        case IsGroupRole:
        {
            return node->isGroup();
        }
        case IsFetchableRole:
        {
            return !node->isGroup() && !node->isAggregation();
        }
        case IsAggregationRole:
        {
            return node->isAggregation();
        }
        case LinkRole:
        {
        	const Feed* const feed = qobject_cast<const Feed* const>( node );
        	return feed ? feed->xmlUrl() : QVariant();
        }
        case IsOpenRole:
        {
            if ( !node->isGroup() )
                return false;
            const Akregator::Folder* const folder = qobject_cast<const Akregator::Folder* const>( node );
            Q_ASSERT( folder );
            return folder->isOpen();
        }
    }

    return QVariant();
}

QVariant Akregator::SubscriptionListModel::headerData( int section, Qt::Orientation, int role ) const
{
    if ( role != Qt::DisplayRole )
        return QVariant();

    switch (section)
    {
        case TitleColumn:
            return i18n("Feeds");
        case UnreadCountColumn:
            return i18n("Unread");
        case TotalCountColumn:
            return i18n("Total");
    }

    return QVariant();
}

QModelIndex Akregator::SubscriptionListModel::parent( const QModelIndex& index ) const
{
    const Akregator::TreeNode* const node = nodeForIndex( index, m_feedList );

    if ( !node || !node->parent() )
        return QModelIndex();

    const Akregator::Folder* parent = node->parent();

    if ( !parent->parent() )
        return createIndex( 0, 0, parent->id() );

    const Akregator::Folder* const grandparent = parent->parent();

    const int row = grandparent->indexOf( parent );

    Q_ASSERT( row != -1 );

    return createIndex( row, 0, parent->id() );
}

QModelIndex Akregator::SubscriptionListModel::index( int row, int column, const QModelIndex& parent ) const
{
    if ( !parent.isValid() )
        return ( row == 0 && m_feedList ) ? createIndex( row, column , m_feedList->rootNode()->id() ) : QModelIndex();

    const Akregator::TreeNode* const parentNode = nodeForIndex( parent, m_feedList );
    const Akregator::TreeNode* const childNode = parentNode->childAt( row );
    return  childNode ? createIndex( row, column, childNode->id() ) : QModelIndex();
}

QModelIndex SubscriptionListModel::indexForNode( const TreeNode* node ) const
{
    if ( !node || !m_feedList )
        return QModelIndex();
    const Folder* const parent = node->parent();
    if ( !parent )
        return index( 0, 0 );
    const int row = parent->indexOf( node );
    Q_ASSERT( row >= 0 );
    const QModelIndex idx = index( row, 0, indexForNode( parent ) );
    Q_ASSERT( idx.internalId() == node->id() );
    return idx;
}

void Akregator::SubscriptionListModel::feedListDestroyed( Akregator::FeedList* )
{
    m_feedList = 0;
}

void Akregator::SubscriptionListModel::subscriptionAdded( Akregator::TreeNode* subscription )
{
    const Folder* const parent = subscription->parent();
    const int row = parent ? parent->indexOf( subscription ) : 0;
    Q_ASSERT( row >= 0 );
    beginInsertRows( indexForNode( parent ), row, row );
    endInsertRows();
}

void Akregator::SubscriptionListModel::aboutToRemoveSubscription( Akregator::TreeNode* subscription )
{
    kDebug() << subscription->id() << endl;
    const Folder* const parent = subscription->parent();
    const int row = parent ? parent->indexOf( subscription ) : -1;
    if ( row < 0 )
        return;
    beginRemoveRows( indexForNode( parent ), row, row );
    m_beganRemoval = true;
}

void Akregator::SubscriptionListModel::subscriptionRemoved( TreeNode* subscription )
{
    kDebug() << subscription->id() << endl;
    if ( m_beganRemoval )
    {
        m_beganRemoval = false;
        endRemoveRows();
    }
}

void Akregator::SubscriptionListModel::subscriptionChanged( TreeNode* node )
{
    const QModelIndex idx = indexForNode( node );
    if ( !idx.isValid() )
        return;
    emit dataChanged( index( idx.row(), 0, idx.parent() ),
                      index( idx.row(), ColumnCount - 1, idx.parent() ) );
}

void SubscriptionListModel::fetchStarted( Akregator::Feed* node )
{
    subscriptionChanged( node );
}

void SubscriptionListModel::fetched( Akregator::Feed* node )
{
    subscriptionChanged( node );
}

void SubscriptionListModel::fetchError( Akregator::Feed* node )
{
    subscriptionChanged( node );
}

void SubscriptionListModel::fetchAborted( Akregator::Feed* node )
{
    subscriptionChanged( node );
}

void Akregator::FolderExpansionHandler::itemExpanded( const QModelIndex& idx )
{
    setExpanded( idx, true );
}

void Akregator::FolderExpansionHandler::itemCollapsed( const QModelIndex& idx )
{
    setExpanded( idx, false );
}

void Akregator::FolderExpansionHandler::setExpanded( const QModelIndex& idx, bool expanded )
{
    if ( !m_feedList || !m_model )
        return;
    Akregator::TreeNode* const node = m_feedList->findByID( m_model->nodeIdForIndex( idx ) );
    if ( !node || !node->isGroup() )
        return;

    Akregator::Folder* const folder = qobject_cast<Akregator::Folder*>( node );
    Q_ASSERT( folder );
    folder->setOpen( expanded );
}

Akregator::FolderExpansionHandler::FolderExpansionHandler( QObject* parent ) : QObject( parent ), m_feedList( 0 ), m_model( 0 )
{
}

void Akregator::FolderExpansionHandler::setModel( Akregator::SubscriptionListModel* model )
{
    m_model = model;
}

void Akregator::FolderExpansionHandler::setFeedList( Akregator::FeedList* feedList )
{
    m_feedList = feedList;
}

Qt::ItemFlags SubscriptionListModel::flags( const QModelIndex& idx ) const
{
    const Qt::ItemFlags flags = QAbstractItemModel::flags( idx );
    if ( !idx.isValid() || ( idx.column() != TitleColumn ) )
        return flags;
    if ( !idx.parent().isValid() ) // the root folder is neither draggable nor editable
        return flags | Qt::ItemIsDropEnabled;
    return flags | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
}

QStringList SubscriptionListModel::mimeTypes() const
{
    QStringList types;
    types << "text/uri-list" << AKREGATOR_TREENODE_MIMETYPE;
    return types;
}

QMimeData* SubscriptionListModel::mimeData( const QModelIndexList& indexes ) const
{
    QMimeData* mimeData = new QMimeData;

    QList<QUrl> urls;
    Q_FOREACH ( const QModelIndex& i, indexes )
    {
        const QUrl url = i.data( LinkRole ).toString();
        if ( !url.isEmpty() )
            urls << url;
    }

    mimeData->setUrls( urls );

    QByteArray idList;
    QDataStream idStream( &idList, QIODevice::WriteOnly );
    Q_FOREACH ( const QModelIndex& i, indexes )
        if ( i.isValid() )
            idStream << i.data( SubscriptionIdRole ).toInt();

    mimeData->setData( AKREGATOR_TREENODE_MIMETYPE, idList );

    return mimeData;
}

bool SubscriptionListModel::setData( const QModelIndex& idx, const QVariant& value, int role )
{
    if ( !idx.isValid() || idx.column() != TitleColumn || role != Qt::EditRole )
        return false;
    const TreeNode* const node = nodeForIndex( idx, m_feedList );
    if ( !node )
        return false;
    RenameSubscriptionJob* job = new RenameSubscriptionJob( this );
    job->setSubscriptionId( node->id() );
    job->setName( value.toString() );
    job->start();
    return true;
}

bool SubscriptionListModel::dropMimeData( const QMimeData* data,
                                          Qt::DropAction action,
                                          int row,
                                          int column,
                                          const QModelIndex& parent )
{
    if ( action == Qt::IgnoreAction )
        return true;

    //if ( column != TitleColumn )
    //    return false;

    if ( data->hasFormat( AKREGATOR_TREENODE_MIMETYPE ) )
    {
        const TreeNode* const droppedOnNode = qobject_cast<const TreeNode*>( nodeForIndex( parent, m_feedList ) );

        const Folder* const destFolder = droppedOnNode->isGroup() ? qobject_cast<const Folder*>( droppedOnNode ) : droppedOnNode->parent();
        if ( !destFolder )
            return false;

        QByteArray idData = data->data( AKREGATOR_TREENODE_MIMETYPE );
        QList<int> ids;
        QDataStream stream( &idData, QIODevice::ReadOnly );
        while ( !stream.atEnd() )
        {
            int id;
            stream >> id;
            ids << id;
        }

        //don't drop nodes into their own subtree
        Q_FOREACH ( const int id, ids )
        {
            const Folder* const asFolder = qobject_cast<const Folder*>( m_feedList->findByID( id ) );
            if ( asFolder && asFolder->subtreeContains( destFolder ) )
                return false;
        }

        const TreeNode* const after = droppedOnNode->isGroup() ? destFolder->childAt( row ) : droppedOnNode;

        Q_FOREACH ( const int id, ids )
        {
            const TreeNode* const node = m_feedList->findByID( id );
            if ( !node )
                continue;
            MoveSubscriptionJob* job = new MoveSubscriptionJob( this );
            job->setSubscriptionId( node->id() );
            job->setDestination( destFolder->id(), after ? after->id() : -1 );
            job->start();
        }
        return true;
    }

    return false;
}

#include "subscriptionlistmodel.moc"
<commit_msg>backport: don't crash when dropping folders onto themselves CCBUG:161562<commit_after>/*
    This file is part of Akregator.

    Copyright (C) 2007 Frank Osterfeld <osterfeld@kde.org>

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

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include "subscriptionlistmodel.h"
#include "feed.h"
#include "feedlist.h"
#include "folder.h"
#include "subscriptionlistjobs.h"
#include "treenode.h"

#include <KDebug>
#include <KIconLoader>
#include <KLocalizedString>

#include <QByteArray>
#include <QDataStream>
#include <QFont>
#include <QIcon>
#include <QList>
#include <QMimeData>
#include <QUrl>
#include <QVariant>

#include <cassert>

using namespace Akregator;
using namespace Syndication;

#define AKREGATOR_TREENODE_MIMETYPE "akregator/treenode-id"

namespace {

    QString errorCodeToString( Syndication::ErrorCode err )
    {
        switch ( err )
        {
            case Timeout:
                return i18n( "Timeout on remote server" );
            case UnknownHost:
                return i18n( "Unknown host" );
            case FileNotFound:
                return i18n( "Feed file not found on remote server" );
            case InvalidXml:
                return i18n( "Could not read feed (invalid XML)" );
            case XmlNotAccepted:
                return i18n( "Could not read feed (unknown format)" );
            case InvalidFormat:
                return i18n( "Could not read feed (invalid feed)" );
            case Success:
            case Aborted:
            default:
                return QString();
        }
    }

    static const Akregator::TreeNode* nodeForIndex( const QModelIndex& index, const Akregator::FeedList* feedList )
    {
        return ( !index.isValid() || !feedList ) ? 0 : feedList->findByID( index.internalId() );
    }
}

Akregator::SubscriptionListModel::SubscriptionListModel( const Akregator::FeedList* feedList, QObject* parent ) : QAbstractItemModel( parent ), m_feedList( feedList ), m_beganRemoval( false )
{
    if ( feedList )
    {
        connect( feedList, SIGNAL( signalDestroyed( Akregator::FeedList* ) ),
                 this, SLOT( feedListDestroyed( Akregator::FeedList* ) ) );
        connect( feedList, SIGNAL( signalNodeAdded( Akregator::TreeNode* ) ),
                 this, SLOT( subscriptionAdded( Akregator::TreeNode* ) ) );
        connect( feedList, SIGNAL( signalAboutToRemoveNode( Akregator::TreeNode* ) ),
                 this, SLOT( aboutToRemoveSubscription( Akregator::TreeNode* ) ) );
        connect( feedList, SIGNAL( signalNodeRemoved( Akregator::TreeNode* ) ),
                   this, SLOT( subscriptionRemoved( Akregator::TreeNode* ) ) );
        connect( feedList, SIGNAL( signalNodeChanged( Akregator::TreeNode* ) ),
                 this, SLOT( subscriptionChanged( Akregator::TreeNode* ) ) );
        connect( feedList, SIGNAL( fetchStarted( Akregator::Feed* ) ),
                 this, SLOT( fetchStarted( Akregator::Feed* ) ) );
        connect( feedList, SIGNAL( fetched( Akregator::Feed* ) ),
                 this, SLOT( fetched( Akregator::Feed* ) ) );
        connect( feedList, SIGNAL( fetchAborted( Akregator::Feed* ) ),
                 this, SLOT( fetchAborted( Akregator::Feed* ) ) );
    }
}

int Akregator::SubscriptionListModel::columnCount( const QModelIndex& ) const
{
    return 3;
}

int Akregator::SubscriptionListModel::rowCount( const QModelIndex& parent ) const
{
    if ( !parent.isValid() )
        return 1;

    const Akregator::TreeNode* const node = nodeForIndex( parent, m_feedList );
    return node ? node->children().count() : 0;
}

uint Akregator::SubscriptionListModel::nodeIdForIndex( const QModelIndex& idx ) const
{
    return idx.isValid() ? idx.internalId() : 0;
}

QVariant Akregator::SubscriptionListModel::data( const QModelIndex& index, int role ) const
{
    if ( !index.isValid() )
        return QVariant();

    const Akregator::TreeNode* const node = nodeForIndex( index, m_feedList );

    if ( !node )
        return QVariant();

    switch ( role )
    {
        case Qt::EditRole:
        case Qt::DisplayRole:
        {
            switch ( index.column() )
            {
                case TitleColumn:
                    return node->title();
                case UnreadCountColumn:
                    return node->unread();
                case TotalCountColumn:
                    return node->totalCount();
            }
        }
        case Qt::FontRole:
        {
            if(node->unread() > 0) {
                QFont font;
                font.setBold(true);
                return font;
            }
        }
        case Qt::ToolTipRole:
        {
            if ( node->isGroup() || node->isAggregation() )
                return QString();
            const Feed* const feed = qobject_cast<const Feed* const>( node );
            if ( feed && feed->fetchErrorOccurred() )
                return i18n( "Could not fetch feed: %1", errorCodeToString( feed->fetchErrorCode() ) );
            else
                return QString();
        }
        case Qt::DecorationRole:
        {
            if ( index.column() != TitleColumn )
                return QVariant();
            const Feed* const feed = qobject_cast<const Feed* const>( node );
            return feed && feed->isFetching() ? node->icon().pixmap( KIconLoader::SizeSmall, QIcon::Active ) : node->icon();
        }
        case SubscriptionIdRole:
        {
            return node->id();
        }
        case IsGroupRole:
        {
            return node->isGroup();
        }
        case IsFetchableRole:
        {
            return !node->isGroup() && !node->isAggregation();
        }
        case IsAggregationRole:
        {
            return node->isAggregation();
        }
        case LinkRole:
        {
        	const Feed* const feed = qobject_cast<const Feed* const>( node );
        	return feed ? feed->xmlUrl() : QVariant();
        }
        case IsOpenRole:
        {
            if ( !node->isGroup() )
                return false;
            const Akregator::Folder* const folder = qobject_cast<const Akregator::Folder* const>( node );
            Q_ASSERT( folder );
            return folder->isOpen();
        }
    }

    return QVariant();
}

QVariant Akregator::SubscriptionListModel::headerData( int section, Qt::Orientation, int role ) const
{
    if ( role != Qt::DisplayRole )
        return QVariant();

    switch (section)
    {
        case TitleColumn:
            return i18n("Feeds");
        case UnreadCountColumn:
            return i18n("Unread");
        case TotalCountColumn:
            return i18n("Total");
    }

    return QVariant();
}

QModelIndex Akregator::SubscriptionListModel::parent( const QModelIndex& index ) const
{
    const Akregator::TreeNode* const node = nodeForIndex( index, m_feedList );

    if ( !node || !node->parent() )
        return QModelIndex();

    const Akregator::Folder* parent = node->parent();

    if ( !parent->parent() )
        return createIndex( 0, 0, parent->id() );

    const Akregator::Folder* const grandparent = parent->parent();

    const int row = grandparent->indexOf( parent );

    Q_ASSERT( row != -1 );

    return createIndex( row, 0, parent->id() );
}

QModelIndex Akregator::SubscriptionListModel::index( int row, int column, const QModelIndex& parent ) const
{
    if ( !parent.isValid() )
        return ( row == 0 && m_feedList ) ? createIndex( row, column , m_feedList->rootNode()->id() ) : QModelIndex();

    const Akregator::TreeNode* const parentNode = nodeForIndex( parent, m_feedList );
    const Akregator::TreeNode* const childNode = parentNode->childAt( row );
    return  childNode ? createIndex( row, column, childNode->id() ) : QModelIndex();
}

QModelIndex SubscriptionListModel::indexForNode( const TreeNode* node ) const
{
    if ( !node || !m_feedList )
        return QModelIndex();
    const Folder* const parent = node->parent();
    if ( !parent )
        return index( 0, 0 );
    const int row = parent->indexOf( node );
    Q_ASSERT( row >= 0 );
    const QModelIndex idx = index( row, 0, indexForNode( parent ) );
    Q_ASSERT( idx.internalId() == node->id() );
    return idx;
}

void Akregator::SubscriptionListModel::feedListDestroyed( Akregator::FeedList* )
{
    m_feedList = 0;
}

void Akregator::SubscriptionListModel::subscriptionAdded( Akregator::TreeNode* subscription )
{
    const Folder* const parent = subscription->parent();
    const int row = parent ? parent->indexOf( subscription ) : 0;
    Q_ASSERT( row >= 0 );
    beginInsertRows( indexForNode( parent ), row, row );
    endInsertRows();
}

void Akregator::SubscriptionListModel::aboutToRemoveSubscription( Akregator::TreeNode* subscription )
{
    kDebug() << subscription->id() << endl;
    const Folder* const parent = subscription->parent();
    const int row = parent ? parent->indexOf( subscription ) : -1;
    if ( row < 0 )
        return;
    beginRemoveRows( indexForNode( parent ), row, row );
    m_beganRemoval = true;
}

void Akregator::SubscriptionListModel::subscriptionRemoved( TreeNode* subscription )
{
    kDebug() << subscription->id() << endl;
    if ( m_beganRemoval )
    {
        m_beganRemoval = false;
        endRemoveRows();
    }
}

void Akregator::SubscriptionListModel::subscriptionChanged( TreeNode* node )
{
    const QModelIndex idx = indexForNode( node );
    if ( !idx.isValid() )
        return;
    emit dataChanged( index( idx.row(), 0, idx.parent() ),
                      index( idx.row(), ColumnCount - 1, idx.parent() ) );
}

void SubscriptionListModel::fetchStarted( Akregator::Feed* node )
{
    subscriptionChanged( node );
}

void SubscriptionListModel::fetched( Akregator::Feed* node )
{
    subscriptionChanged( node );
}

void SubscriptionListModel::fetchError( Akregator::Feed* node )
{
    subscriptionChanged( node );
}

void SubscriptionListModel::fetchAborted( Akregator::Feed* node )
{
    subscriptionChanged( node );
}

void Akregator::FolderExpansionHandler::itemExpanded( const QModelIndex& idx )
{
    setExpanded( idx, true );
}

void Akregator::FolderExpansionHandler::itemCollapsed( const QModelIndex& idx )
{
    setExpanded( idx, false );
}

void Akregator::FolderExpansionHandler::setExpanded( const QModelIndex& idx, bool expanded )
{
    if ( !m_feedList || !m_model )
        return;
    Akregator::TreeNode* const node = m_feedList->findByID( m_model->nodeIdForIndex( idx ) );
    if ( !node || !node->isGroup() )
        return;

    Akregator::Folder* const folder = qobject_cast<Akregator::Folder*>( node );
    Q_ASSERT( folder );
    folder->setOpen( expanded );
}

Akregator::FolderExpansionHandler::FolderExpansionHandler( QObject* parent ) : QObject( parent ), m_feedList( 0 ), m_model( 0 )
{
}

void Akregator::FolderExpansionHandler::setModel( Akregator::SubscriptionListModel* model )
{
    m_model = model;
}

void Akregator::FolderExpansionHandler::setFeedList( Akregator::FeedList* feedList )
{
    m_feedList = feedList;
}

Qt::ItemFlags SubscriptionListModel::flags( const QModelIndex& idx ) const
{
    const Qt::ItemFlags flags = QAbstractItemModel::flags( idx );
    if ( !idx.isValid() || ( idx.column() != TitleColumn ) )
        return flags;
    if ( !idx.parent().isValid() ) // the root folder is neither draggable nor editable
        return flags | Qt::ItemIsDropEnabled;
    return flags | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
}

QStringList SubscriptionListModel::mimeTypes() const
{
    QStringList types;
    types << "text/uri-list" << AKREGATOR_TREENODE_MIMETYPE;
    return types;
}

QMimeData* SubscriptionListModel::mimeData( const QModelIndexList& indexes ) const
{
    QMimeData* mimeData = new QMimeData;

    QList<QUrl> urls;
    Q_FOREACH ( const QModelIndex& i, indexes )
    {
        const QUrl url = i.data( LinkRole ).toString();
        if ( !url.isEmpty() )
            urls << url;
    }

    mimeData->setUrls( urls );

    QByteArray idList;
    QDataStream idStream( &idList, QIODevice::WriteOnly );
    Q_FOREACH ( const QModelIndex& i, indexes )
        if ( i.isValid() )
            idStream << i.data( SubscriptionIdRole ).toInt();

    mimeData->setData( AKREGATOR_TREENODE_MIMETYPE, idList );

    return mimeData;
}

bool SubscriptionListModel::setData( const QModelIndex& idx, const QVariant& value, int role )
{
    if ( !idx.isValid() || idx.column() != TitleColumn || role != Qt::EditRole )
        return false;
    const TreeNode* const node = nodeForIndex( idx, m_feedList );
    if ( !node )
        return false;
    RenameSubscriptionJob* job = new RenameSubscriptionJob( this );
    job->setSubscriptionId( node->id() );
    job->setName( value.toString() );
    job->start();
    return true;
}

bool SubscriptionListModel::dropMimeData( const QMimeData* data,
                                          Qt::DropAction action,
                                          int row,
                                          int column,
                                          const QModelIndex& parent )
{
    if ( action == Qt::IgnoreAction )
        return true;

    //if ( column != TitleColumn )
    //    return false;

    if ( data->hasFormat( AKREGATOR_TREENODE_MIMETYPE ) )
    {
        const TreeNode* const droppedOnNode = qobject_cast<const TreeNode*>( nodeForIndex( parent, m_feedList ) );

        const Folder* const destFolder = droppedOnNode->isGroup() ? qobject_cast<const Folder*>( droppedOnNode ) : droppedOnNode->parent();
        if ( !destFolder )
            return false;

        QByteArray idData = data->data( AKREGATOR_TREENODE_MIMETYPE );
        QList<int> ids;
        QDataStream stream( &idData, QIODevice::ReadOnly );
        while ( !stream.atEnd() )
        {
            int id;
            stream >> id;
            ids << id;
        }

        //don't drop nodes into their own subtree
        Q_FOREACH ( const int id, ids )
        {
            const Folder* const asFolder = qobject_cast<const Folder*>( m_feedList->findByID( id ) );
            if ( asFolder && ( asFolder == destFolder || asFolder->subtreeContains( destFolder ) ) )
                return false;
        }

        const TreeNode* const after = droppedOnNode->isGroup() ? destFolder->childAt( row ) : droppedOnNode;

        Q_FOREACH ( const int id, ids )
        {
            const TreeNode* const node = m_feedList->findByID( id );
            if ( !node )
                continue;
            MoveSubscriptionJob* job = new MoveSubscriptionJob( this );
            job->setSubscriptionId( node->id() );
            job->setDestination( destFolder->id(), after ? after->id() : -1 );
            job->start();
        }
        return true;
    }

    return false;
}

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