349 lines
8.6 KiB
C++
349 lines
8.6 KiB
C++
/* This file is part of FlowCanvas.
|
|
* Copyright (C) 2007-2009 David Robillard <http://drobilla.net>
|
|
*
|
|
* FlowCanvas 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.
|
|
*
|
|
* FlowCanvas 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 details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <string>
|
|
|
|
#include <libgnomecanvasmm.h>
|
|
|
|
#include "Canvas.hpp"
|
|
#include "Connectable.hpp"
|
|
#include "Connection.hpp"
|
|
#include "Ellipse.hpp"
|
|
|
|
namespace FlowCanvas {
|
|
|
|
|
|
Connection::Connection(boost::shared_ptr<Canvas> canvas,
|
|
boost::shared_ptr<Connectable> source,
|
|
boost::shared_ptr<Connectable> dest,
|
|
uint32_t color,
|
|
bool show_arrowhead)
|
|
: Gnome::Canvas::Group(*canvas->root())
|
|
, _canvas(canvas)
|
|
, _source(source)
|
|
, _dest(dest)
|
|
, _bpath(*this)
|
|
, _path(gnome_canvas_path_def_new())
|
|
, _handle(NULL)
|
|
, _color(color)
|
|
, _handle_style(HANDLE_NONE)
|
|
, _selected(false)
|
|
, _show_arrowhead(show_arrowhead)
|
|
{
|
|
_bpath.property_width_units() = 2.0;
|
|
set_color(color);
|
|
|
|
update_location();
|
|
raise_to_top();
|
|
}
|
|
|
|
|
|
Connection::~Connection()
|
|
{
|
|
gnome_canvas_path_def_unref(_path);
|
|
}
|
|
|
|
|
|
void
|
|
Connection::set_color(uint32_t color)
|
|
{
|
|
_color = color;
|
|
_bpath.property_outline_color_rgba() = _color;
|
|
if (_handle) {
|
|
if (_handle->text) {
|
|
_handle->text->property_fill_color_rgba() = _color;
|
|
}
|
|
if (_handle->shape) {
|
|
_handle->shape->property_fill_color_rgba() = 0x000000FF;
|
|
_handle->shape->property_outline_color_rgba() = _color;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** Updates the path of the connection to match it's ports if they've moved.
|
|
*/
|
|
void
|
|
Connection::update_location()
|
|
{
|
|
boost::shared_ptr<Connectable> src = _source.lock();
|
|
boost::shared_ptr<Connectable> dst = _dest.lock();
|
|
|
|
if (!src || !dst)
|
|
return;
|
|
|
|
bool straight = (boost::dynamic_pointer_cast<Ellipse>(src)
|
|
|| boost::dynamic_pointer_cast<Ellipse>(dst));
|
|
|
|
const Gnome::Art::Point src_point = src->src_connection_point();
|
|
const Gnome::Art::Point dst_point = dst->dst_connection_point(src_point);
|
|
|
|
const double src_x = src_point.get_x();
|
|
const double src_y = src_point.get_y();
|
|
const double dst_x = dst_point.get_x();
|
|
const double dst_y = dst_point.get_y();
|
|
|
|
if (straight) {
|
|
|
|
gnome_canvas_path_def_reset(_path);
|
|
gnome_canvas_path_def_moveto(_path, src_x, src_y);
|
|
gnome_canvas_path_def_lineto(_path, dst_x, dst_y);
|
|
double dx = src_x - dst_x;
|
|
double dy = src_y - dst_y;
|
|
|
|
if (_handle) {
|
|
_handle->property_x() = src_x - dx/2.0;
|
|
_handle->property_y() = src_y - dy/2.0;
|
|
_handle->move(0, 0);
|
|
}
|
|
|
|
if (_show_arrowhead) {
|
|
|
|
const double h = sqrt(dx*dx + dy*dy);
|
|
|
|
dx = dx / h * 10;
|
|
dy = dy / h * 10;
|
|
|
|
gnome_canvas_path_def_lineto(_path,
|
|
dst_x + dx - dy/1.5,
|
|
dst_y + dy + dx/1.5);
|
|
|
|
gnome_canvas_path_def_moveto(_path, dst_x, dst_y);
|
|
|
|
gnome_canvas_path_def_lineto(_path,
|
|
dst_x + dx + dy/1.5,
|
|
dst_y + dy - dx/1.5);
|
|
}
|
|
|
|
} else {
|
|
|
|
const double join_x = (src_x + dst_x)/2.0;
|
|
const double join_y = (src_y + dst_y)/2.0;
|
|
|
|
double dx = fabs(dst_x - src_x);
|
|
double dy = fabs(dst_y - src_y);
|
|
|
|
Gnome::Art::Point src_vec = src->connection_point_vector(dx/3.0, dy/3.0);
|
|
Gnome::Art::Point dst_vec = dst->connection_point_vector(dx/3.0, dy/3.0);
|
|
|
|
double src_point_dx = src_vec.get_x();
|
|
double src_point_dy = src_vec.get_y();
|
|
double dst_point_dx = dst_vec.get_x();
|
|
double dst_point_dy = dst_vec.get_y();
|
|
|
|
// Path 1 (src_x, src_y) -> (join_x, join_y)
|
|
// Control point 1
|
|
const double src_x1 = src_x + src_point_dx;
|
|
const double src_y1 = src_y + src_point_dy;
|
|
// Control point 2
|
|
const double src_x2 = (join_x + src_x1) / 2.0;
|
|
const double src_y2 = (join_y + src_y1) / 2.0;
|
|
|
|
// Path 2, (join_x, join_y) -> (dst_x, dst_y)
|
|
// Control point 1
|
|
const double dst_x1 = dst_x - dst_point_dx;
|
|
const double dst_y1 = dst_y - dst_point_dy;
|
|
// Control point 2
|
|
const double dst_x2 = (join_x + dst_x1) / 2.0;
|
|
const double dst_y2 = (join_y + dst_y1) / 2.0;
|
|
|
|
// libgnomecanvasmm + GTK 2.8 screwed up the Path API; use the C one.
|
|
gnome_canvas_path_def_reset(_path);
|
|
gnome_canvas_path_def_moveto(_path, src_x, src_y);
|
|
gnome_canvas_path_def_curveto(_path, src_x1, src_y1, src_x2, src_y2, join_x, join_y);
|
|
gnome_canvas_path_def_curveto(_path, dst_x2, dst_y2, dst_x1, dst_y1, dst_x, dst_y);
|
|
|
|
// Uncomment to see control point path as straight lines
|
|
/*gnome_canvas_path_def_reset(_path);
|
|
gnome_canvas_path_def_moveto(_path, src_x, src_y);
|
|
gnome_canvas_path_def_lineto(_path, src_x1, src_y1);
|
|
gnome_canvas_path_def_lineto(_path, src_x2, src_y2);
|
|
gnome_canvas_path_def_lineto(_path, join_x, join_y);
|
|
gnome_canvas_path_def_lineto(_path, dst_x2, dst_y2);
|
|
gnome_canvas_path_def_lineto(_path, dst_x1, dst_y1);
|
|
gnome_canvas_path_def_lineto(_path, dst_x, dst_y);*/
|
|
|
|
if (_show_arrowhead) {
|
|
|
|
//const double h = sqrt(dx*dx + dy*dy);
|
|
|
|
//dx = dx / h * 10;
|
|
//dy = dy / h * 10;
|
|
|
|
gnome_canvas_path_def_lineto(_path,
|
|
dst_x - 12,
|
|
dst_y - 4);
|
|
|
|
gnome_canvas_path_def_moveto(_path, dst_x, dst_y);
|
|
|
|
gnome_canvas_path_def_lineto(_path,
|
|
dst_x - 12,
|
|
dst_y + 4);
|
|
}
|
|
}
|
|
|
|
GnomeCanvasBpath* c_obj = _bpath.gobj();
|
|
gnome_canvas_item_set(GNOME_CANVAS_ITEM(c_obj), "bpath", _path, NULL);
|
|
}
|
|
|
|
|
|
/** Set label text displayed next to the edge.
|
|
*
|
|
* Passing the empty string will remove the label.
|
|
*/
|
|
void
|
|
Connection::set_label(const std::string& str)
|
|
{
|
|
if (str != "") {
|
|
if (!_handle)
|
|
_handle = new Handle(*this);
|
|
|
|
if (!_handle->text) {
|
|
_handle->text = new Gnome::Canvas::Text(*_handle, 0, 0, str);
|
|
_handle->text->property_size_set() = true;
|
|
_handle->text->property_size() = 9000;
|
|
_handle->text->property_weight_set() = true;
|
|
_handle->text->property_weight() = 200;
|
|
_handle->text->property_fill_color_rgba() = _color;
|
|
_handle->text->show();
|
|
} else {
|
|
_handle->text->property_text() = str;
|
|
}
|
|
|
|
if (_handle->shape)
|
|
show_handle(true);
|
|
|
|
_handle->text->raise(1);
|
|
update_location();
|
|
} else if (_handle) {
|
|
delete _handle->text;
|
|
_handle->text = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
Connection::show_handle(bool show)
|
|
{
|
|
if (show) {
|
|
if (!_handle)
|
|
_handle = new Handle(*this);
|
|
|
|
double handle_width = 8.0;
|
|
double handle_height = 8.0;
|
|
if (_handle->text) {
|
|
handle_width = _handle->text->property_text_width();
|
|
handle_height = _handle->text->property_text_height();
|
|
}
|
|
|
|
// FIXME: slow
|
|
delete _handle->shape;
|
|
|
|
if (_handle_style != HANDLE_NONE) {
|
|
if (_handle_style == HANDLE_RECT)
|
|
_handle->shape = new Gnome::Canvas::Rect(*_handle,
|
|
-handle_width/2.0 - 1.0, -handle_height/2.0,
|
|
handle_width/2.0 + 1.0, handle_height/2.0);
|
|
else// if (_handle_style == HANDLE_CIRCLE)
|
|
_handle->shape = new Gnome::Canvas::Ellipse(*_handle,
|
|
-handle_width/2.0 - 1.0, -handle_height/2.0,
|
|
handle_width/2.0 + 1.0, handle_height/2.0);
|
|
}
|
|
|
|
_handle->shape->property_fill_color_rgba() = 0x000000FF;
|
|
_handle->shape->property_outline_color_rgba() = _color;
|
|
_handle->shape->show();
|
|
_handle->show();
|
|
|
|
} else {
|
|
delete _handle;
|
|
_handle = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
Connection::set_highlighted(bool b)
|
|
{
|
|
if (b)
|
|
_bpath.property_outline_color_rgba() = 0xFF0000FF;
|
|
else
|
|
_bpath.property_outline_color_rgba() = _color;
|
|
}
|
|
|
|
|
|
void
|
|
Connection::set_selected(bool selected)
|
|
{
|
|
_selected = selected;
|
|
|
|
if (selected) {
|
|
_bpath.property_dash() = _canvas.lock()->select_dash();
|
|
} else {
|
|
_bpath.property_dash() = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/** Overloaded Gnome::Canvas::Item::raise_to_top to ensure src and dst
|
|
* are still above connections (to hide the part behind connected Ellipses).
|
|
*/
|
|
void
|
|
Connection::raise_to_top()
|
|
{
|
|
Gnome::Canvas::Item::raise_to_top();
|
|
|
|
// Raise source above us
|
|
boost::shared_ptr<Item> item = boost::dynamic_pointer_cast<Item>(_source.lock());
|
|
if (item)
|
|
item->raise_to_top();
|
|
|
|
// Raise dest above us
|
|
item = boost::dynamic_pointer_cast<Item>(_dest.lock());
|
|
if (item)
|
|
item->raise_to_top();
|
|
|
|
/* Raise the roof
|
|
\o/
|
|
|
|
|
/ \
|
|
*/
|
|
}
|
|
|
|
|
|
void
|
|
Connection::select_tick()
|
|
{
|
|
_bpath.property_dash() = _canvas.lock()->select_dash();
|
|
}
|
|
|
|
|
|
void
|
|
Connection::zoom(double z)
|
|
{
|
|
if (_handle && _handle->text) {
|
|
_handle->text->property_size() = static_cast<int>(floor((double)9000.0f * z));
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace FlowCanvas
|
|
|