ladish/gui/flowcanvas/Ellipse.cpp

436 lines
10 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 <cstdlib>
#include <functional>
#include <list>
#include <string>
#include "Canvas.hpp"
#include "Ellipse.hpp"
#include "Item.hpp"
using std::string;
namespace FlowCanvas {
static const uint32_t ELLIPSE_FILL_COLOUR = 0x1E2224FF;
static const uint32_t ELLIPSE_HILITE_FILL_COLOUR = 0x2E3436FF;
static const uint32_t ELLIPSE_OUTLINE_COLOUR = 0xD3D7CFFF;
static const uint32_t ELLIPSE_HILITE_OUTLINE_COLOUR = 0xEEEEECFF;
static const uint32_t ELLIPSE_TITLE_COLOUR = 0xFFFFFFFF;
/** Construct a Ellipse
*
* Note you must call resize() at some point or the module will look ridiculous.
* This it to avoid unecessary text measuring and resizing, which is insanely
* expensive.
*
* If @a name is the empty string, the space where the title would usually be
* is not created (eg the module will be shorter).
*/
Ellipse::Ellipse(boost::shared_ptr<Canvas> canvas,
const string& name,
double x,
double y,
double x_radius,
double y_radius,
bool show_title)
: Item(canvas, name, x, y, ELLIPSE_FILL_COLOUR)
, _title_visible(show_title)
, _ellipse(*this, -x_radius, -y_radius, x_radius, y_radius)
, _label(NULL)
{
if (name != "")
_label = Gtk::manage(new Gnome::Canvas::Text(*this, 0, 0, name));
_ellipse.property_fill_color_rgba() = ELLIPSE_FILL_COLOUR;
_ellipse.property_outline_color_rgba() = ELLIPSE_OUTLINE_COLOUR;
_border_color = ELLIPSE_OUTLINE_COLOUR;
if (canvas->property_aa())
set_border_width(0.5);
else
set_border_width(1.0);
if (_label) {
if (show_title) {
_label->property_size_set() = true;
_label->property_size() = 9000;
_label->property_weight_set() = true;
_label->property_weight() = 400;
_label->property_fill_color_rgba() = ELLIPSE_TITLE_COLOUR;
} else {
_label->hide();
}
}
set_width(x_radius * 2.0);
set_height(y_radius * 2.0);
}
Ellipse::~Ellipse()
{
}
Gnome::Art::Point
Ellipse::dst_connection_point(const Gnome::Art::Point& src)
{
const double c_x = property_x();
const double c_y = property_y();
const double src_x = src.get_x();
const double src_y = src.get_y();
const double dx = src.get_x() - c_x;
const double dy = src.get_y() - c_y;
const double h = sqrt(dx*dx + dy*dy);
const double theta = asin(dx/(h + DBL_EPSILON));
const double y_mod = (c_y < src_y) ? 1 : -1;
const double ret_h = h - _width/2.0;
const double ret_x = src_x - sin(theta) * ret_h;
const double ret_y = src_y - cos(theta) * ret_h * y_mod;
return Gnome::Art::Point(ret_x, ret_y);
}
Gnome::Art::Point
Ellipse::connection_point_vector(double dx, double dy)
{
return Gnome::Art::Point(0, 0);
}
/** Set the border width of the module.
*
* Do NOT directly set the width_units property on the rect, use this function.
*/
void
Ellipse::set_border_width(double w)
{
_border_width = w;
_ellipse.property_width_units() = w;
}
void
Ellipse::zoom(double z)
{
if (_label)
_label->property_size() = static_cast<int>(floor(9000.0 * z));
}
void
Ellipse::set_highlighted(bool b)
{
if (b) {
_ellipse.property_fill_color_rgba() = ELLIPSE_HILITE_FILL_COLOUR;
_ellipse.property_outline_color_rgba() = ELLIPSE_HILITE_OUTLINE_COLOUR;
} else {
_ellipse.property_fill_color_rgba() = _color;
_ellipse.property_outline_color_rgba() = ELLIPSE_OUTLINE_COLOUR;
}
}
void
Ellipse::set_selected(bool selected)
{
Item::set_selected(selected);
assert(_selected == selected);
boost::shared_ptr<Canvas> canvas = _canvas.lock();
if (!canvas)
return;
if (selected) {
_ellipse.property_outline_color_rgba() = ELLIPSE_HILITE_OUTLINE_COLOUR;
_ellipse.property_dash() = canvas->select_dash();
} else {
_ellipse.property_fill_color_rgba() = _color;
_ellipse.property_outline_color_rgba() = ELLIPSE_OUTLINE_COLOUR;
_ellipse.property_dash() = NULL;
}
}
bool
Ellipse::is_within(const Gnome::Canvas::Rect& rect)
{
const double x1 = rect.property_x1();
const double y1 = rect.property_y1();
const double x2 = rect.property_x2();
const double y2 = rect.property_y2();
if (x1 < x2 && y1 < y2) {
return (property_x() > x1
&& property_y() > y1
&& property_x() + width() < x2
&& property_y() + height() < y2);
} else if (x2 < x1 && y2 < y1) {
return (property_x() > x2
&& property_y() > y2
&& property_x() + width() < x1
&& property_y() + height() < y1);
} else if (x1 < x2 && y2 < y1) {
return (property_x() > x1
&& property_y() > y2
&& property_x() + width() < x2
&& property_y() + height() < y1);
} else if (x2 < x1 && y1 < y2) {
return (property_x() > x2
&& property_y() > y1
&& property_x() + width() < x1
&& property_y() + height() < y2);
} else {
return false;
}
}
void
Ellipse::set_width(double w)
{
_width = w;
// _ellipse.property_x2() = _ellipse.property_x1() + w;
}
void
Ellipse::set_height(double h)
{
_height = h;
// _ellipse.property_y2() = _ellipse.property_y1() + h;
}
/** Move relative to current location.
*
* @param dx distance to move along x axis (in world units)
* @param dy distance to move along y axis (in world units)
*/
void
Ellipse::move(double dx, double dy)
{
boost::shared_ptr<Canvas> canvas = _canvas.lock();
if (!canvas)
return;
double new_x = property_x() + dx;
double new_y = property_y() + dy;
if (new_x < 0)
dx = property_x() * -1;
else if (new_x + _width > canvas->width())
dx = canvas->width() - property_x() - _width;
if (new_y < 0)
dy = property_y() * -1;
else if (new_y + _height > canvas->height())
dy = canvas->height() - property_y() - _height;
Gnome::Canvas::Group::move(dx, dy);
move_connections();
}
/** Move to the specified absolute coordinate on the canvas.
*
* @param x x coordinate to move to (in world units)
* @param y y coordinate to move to (in world units)
*/
void
Ellipse::move_to(double x, double y)
{
boost::shared_ptr<Canvas> canvas = _canvas.lock();
if (!canvas)
return;
assert(canvas->width() > 0);
assert(canvas->height() > 0);
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + _width > canvas->width()) x = canvas->width() - _width - 1;
if (y + _height > canvas->height()) y = canvas->height() - _height - 1;
assert(x >= 0);
assert(x + _width < canvas->width());
assert(y >= 0);
assert(y + _height < canvas->height());
property_x() = x;
property_y() = y;
Gnome::Canvas::Group::move(0, 0);
move_connections();
}
void
Ellipse::set_name(const string& str)
{
if (str != "") {
if (!_label)
_label = new Gnome::Canvas::Text(*this, 0, 0, str);
_label->property_size_set() = true;
_label->property_size() = 9000;
_label->property_weight_set() = true;
_label->property_weight() = 200;
_label->property_fill_color_rgba() = ELLIPSE_TITLE_COLOUR;
_label->property_text() = str;
_label->show();
} else {
delete _label;
_label = NULL;
}
}
/** Resize the module to fit its contents best.
*/
void
Ellipse::resize()
{
#if 0
double widest_in = 0.0;
double widest_out = 0.0;
// The amount of space between a port edge and the module edge (on the
// side that the port isn't right on the edge).
double hor_pad = 5.0;
if (!_title_visible)
hor_pad = 15.0; // leave more room for a mouse target for dragging
boost::shared_ptr<Port> p;
// Find widest in/out ports
for (PortVector::iterator i = _ports.begin(); i != _ports.end(); ++i) {
p = (*i);
assert(p);
if (p->is_input() && p->width() > widest_in)
widest_in = p->width();
else if (p->is_output() && p->width() > widest_out)
widest_out = p->width();
}
// Make sure module is wide enough for ports
set_width(std::max(widest_in, widest_out) + hor_pad + border_width()*2.0);
// Make sure module is wide enough for title
if (_title_visible && _label.property_text_width() + 8.0 > _width)
set_width(_label.property_text_width() + 8.0);
// Set height to contain ports and title
double height_base = 2;
if (_title_visible)
height_base += 2 + _label.property_text_height();
double h = height_base;
if (_ports.size() > 0)
h += _ports.size() * ((*_ports.begin())->height()+2.0);
if (!_title_visible && _ports.size() > 0)
h += 0.5;
set_height(h);
// Move ports to appropriate locations
double y;
int i = 0;
for (PortVector::iterator pi = _ports.begin(); pi != _ports.end(); ++pi, ++i) {
boost::shared_ptr<Port> p = (*pi);
y = height_base + (i * (p->height() + 2.0));
if (p->is_input()) {
p->set_width(widest_in);
p->property_x() = 0.0;
p->property_y() = y;
} else {
p->set_width(widest_out);
p->property_x() = _width - p->width() - 0.0;
p->property_y() = y;
}
}
// Center title
_label.property_x() = _width/2.0;
// Update connection locations if we've moved/resized
for (PortVector::iterator pi = _ports.begin(); pi != _ports.end(); ++pi, ++i) {
(*pi)->move_connections();
}
#endif
// Make things actually move to their new locations (?!)
move(0, 0);
}
void
Ellipse::select_tick()
{
_ellipse.property_dash() = _canvas.lock()->select_dash();
}
void
Ellipse::add_connection(boost::shared_ptr<Connection> c)
{
Connectable::add_connection(c);
raise_to_top();
}
void
Ellipse::set_border_color(uint32_t c)
{
_border_color = c;
_ellipse.property_outline_color_rgba() = _border_color;
}
void
Ellipse::set_default_base_color()
{
_color = ELLIPSE_FILL_COLOUR;
_ellipse.property_fill_color_rgba() = _color;
}
void
Ellipse::set_base_color(uint32_t c)
{
_color = c;
_ellipse.property_fill_color_rgba() = _color;
}
} // namespace FlowCanvas