1 /* This file is part of FlowCanvas. Copyright (C) 2005 Dave Robillard.
3 * FlowCanvas is free software; you can redistribute it and/or modify it under the
4 * terms of the GNU General Public License as published by the Free Software
5 * Foundation; either version 2 of the License, or (at your option) any later
8 * FlowCanvas is distributed in the hope that it will be useful, but WITHOUT ANY
9 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
12 * You should have received a copy of the GNU General Public License along
13 * with this program; if not, write to the Free Software Foundation, Inc.,
14 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 #include "flowcanvas/Module.h"
19 #include "flowcanvas/FlowCanvas.h"
28 namespace LibFlowCanvas {
30 static const int MODULE_FILL_COLOUR = 0x122A3CFF;
31 static const int MODULE_OUTLINE_COLOUR = 0x8899AAFF;
32 static const int MODULE_TITLE_COLOUR = 0xDDEEFFFF;
34 Module::Module(FlowCanvas* patch_bay, const string& name, double x, double y)
35 : Gnome::Canvas::Group(*patch_bay->root(), x, y),
38 m_patch_bay(patch_bay),
39 m_module_box(*this, 0, 0, 0, 0), // w, h set later
40 m_canvas_title(*this, 0, 6, name) // x set later
42 assert(m_patch_bay != NULL);
44 m_module_box.property_fill_color_rgba() = MODULE_FILL_COLOUR;
46 m_module_box.property_outline_color_rgba() = MODULE_OUTLINE_COLOUR;
47 m_module_box.property_join_style() = Gdk::JOIN_ROUND;
50 m_canvas_title.property_size_set() = true;
51 m_canvas_title.property_size() = 10000;
52 m_canvas_title.property_weight_set() = true;
53 m_canvas_title.property_weight() = 400;
54 m_canvas_title.property_fill_color_rgba() = MODULE_TITLE_COLOUR;
56 width(m_canvas_title.property_text_width() + 6.0);
57 height(m_canvas_title.property_text_height() + 2.0);
58 m_canvas_title.property_x() = m_width/2.0;
60 signal_event().connect(sigc::mem_fun(this, &Module::module_event));
67 for (list<Module*>::iterator i = m_patch_bay->selected_modules().begin();
68 i != m_patch_bay->selected_modules().end(); ++i)
71 m_patch_bay->selected_modules().erase(i);
76 for (PortList::iterator p = m_ports.begin(); p != m_ports.end(); ++p)
80 /** Set the border width of the module.
82 * Do NOT directly set the width_units property on the rect, use this function.
85 Module::border_width(double w)
88 m_module_box.property_width_units() = w;
93 Module::module_event(GdkEvent* event)
95 assert(event != NULL);
98 static double drag_start_x, drag_start_y;
99 double module_x, module_y; // FIXME: bad name, actually mouse click loc
100 static bool dragging = false;
103 module_x = event->button.x;
104 module_y = event->button.y;
106 property_parent().get_value()->w2i(module_x, module_y);
108 switch (event->type) {
110 case GDK_2BUTTON_PRESS:
115 case GDK_BUTTON_PRESS:
116 if (event->button.button == 1) {
119 // Set these so we can tell on a button release if a drag actually
120 // happened (if not, it's just a click)
123 grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK,
124 Gdk::Cursor(Gdk::FLEUR),
127 } else if (event->button.button == 2) {
130 } else if (event->button.button == 3) {
131 show_menu(&event->button);
137 case GDK_MOTION_NOTIFY:
138 if ((dragging && (event->motion.state & GDK_BUTTON1_MASK))) {
139 double new_x = module_x;
140 double new_y = module_y;
142 // Move any other selected modules if we're selected
144 for (list<Module*>::iterator i = m_patch_bay->selected_modules().begin();
145 i != m_patch_bay->selected_modules().end(); ++i)
147 (*i)->move(new_x - x, new_y - y);
150 move(new_x - x, new_y - y);
160 case GDK_BUTTON_RELEASE:
162 ungrab(event->button.time);
164 if (module_x != drag_start_x || module_y != drag_start_y) {// dragged
166 } else { // just a click
168 m_patch_bay->unselect_module(this);
171 if ( !(event->button.state & GDK_CONTROL_MASK))
172 m_patch_bay->clear_selection();
173 m_patch_bay->select_module(this);
182 case GDK_ENTER_NOTIFY:
185 for (PortList::iterator p = m_ports.begin(); p != m_ports.end(); ++p)
186 (*p)->raise_connections();
189 case GDK_LEAVE_NOTIFY:
202 Module::zoom(float z)
204 m_canvas_title.property_size() = static_cast<int>(roundf(10000.0f * z));
205 for (PortList::iterator p = m_ports.begin(); p != m_ports.end(); ++p)
211 Module::hilite(bool b)
214 m_module_box.property_fill_color_rgba() = 0x223553FF;
216 m_module_box.property_fill_color_rgba() = MODULE_FILL_COLOUR;
222 Module::selected(bool selected)
224 m_selected = selected;
226 m_module_box.property_fill_color_rgba() = 0x223553FF;
227 m_module_box.property_outline_color_rgba() = 0xEEEEFFFF;
228 m_module_box.property_dash() = m_patch_bay->select_dash();
229 m_canvas_title.property_fill_color_rgba() = 0xEEEEFFFF;
230 //for (PortList::iterator p = m_ports.begin(); p != m_ports.end(); ++p)
231 // (*p)->rect()->property_outline_color_rgba() = 0xEEEEFFFF;
233 m_module_box.property_fill_color_rgba() = MODULE_FILL_COLOUR;
234 m_module_box.property_outline_color_rgba() = MODULE_OUTLINE_COLOUR;
235 m_module_box.property_dash() = NULL;
236 m_canvas_title.property_fill_color_rgba() = MODULE_TITLE_COLOUR;
237 //for (PortList::iterator p = m_ports.begin(); p != m_ports.end(); ++p)
238 // (*p)->rect()->property_outline_color_rgba() = 0x8899AAFF;
244 Module::is_within(const Gnome::Canvas::Rect* const rect)
246 const double x1 = rect->property_x1();
247 const double y1 = rect->property_y1();
248 const double x2 = rect->property_x2();
249 const double y2 = rect->property_y2();
251 if (x1 < x2 && y1 < y2) {
252 return (property_x() > x1
254 && property_x() + width() < x2
255 && property_y() + height() < y2);
256 } else if (x2 < x1 && y2 < y1) {
257 return (property_x() > x2
259 && property_x() + width() < x1
260 && property_y() + height() < y1);
261 } else if (x1 < x2 && y2 < y1) {
262 return (property_x() > x1
264 && property_x() + width() < x2
265 && property_y() + height() < y1);
266 } else if (x2 < x1 && y1 < y2) {
267 return (property_x() > x2
269 && property_x() + width() < x1
270 && property_y() + height() < y2);
278 Module::remove_port(const string& port_name, bool resize_to_fit)
280 for (PortList::iterator i = m_ports.begin(); i != m_ports.end(); ++i) {
281 if ((*i)->name() == port_name) {
283 i = m_ports.erase(i);
293 Module::width(double w)
296 m_module_box.property_x2() = m_module_box.property_x1() + w;
301 Module::height(double h)
304 m_module_box.property_y2() = m_module_box.property_y1() + h;
308 /** Overloaded Group::move to update connection paths and keep module on the canvas */
310 Module::move(double dx, double dy)
312 double new_x = property_x() + dx;
313 double new_y = property_y() + dy;
315 if (new_x < 0) dx = property_x() * -1;
316 else if (new_x + m_width > m_patch_bay->width()) dx = m_patch_bay->width() - property_x() - m_width;
318 if (new_y < 0) dy = property_y() * -1;
319 else if (new_y + m_height > m_patch_bay->height()) dy = m_patch_bay->height() - property_y() - m_height;
321 Gnome::Canvas::Group::move(dx, dy);
323 // Deal with moving the connection lines
324 for (PortList::iterator p = ports().begin(); p != ports().end(); ++p)
325 (*p)->move_connections();
329 /** Move to the specified absolute coordinate on the canvas */
331 Module::move_to(double x, double y)
335 if (x + m_width > m_patch_bay->width()) x = m_patch_bay->width() - m_width;
336 if (y + m_height > m_patch_bay->height()) y = m_patch_bay->height() - m_height;
338 // Man, not many things left to try to get the damn things to move! :)
342 // Deal with moving the connection lines
343 for (PortList::iterator p = ports().begin(); p != ports().end(); ++p)
344 (*p)->move_connections();
349 Module::name(const string& n)
352 m_canvas_title.property_text() = m_name;
358 Module::add_port(const string& port_name, bool is_input, int colour, bool resize_to_fit)
360 Port* port = new Port(this, port_name, is_input, colour);
362 port->signal_event().connect(
363 sigc::bind<Port*>(sigc::mem_fun(m_patch_bay, &FlowCanvas::port_event), port));
365 add_port(port, resize_to_fit);
370 Module::add_port(Port* port, bool resize_to_fit)
372 m_ports.push_back(port);
379 /** Resize the module to fit its contents best.
384 double widest_in = 0.0;
385 double widest_out = 0.0;
389 // Find widest in/out ports
390 for (PortList::iterator i = m_ports.begin(); i != m_ports.end(); ++i) {
392 if (p->is_input() && p->width() > widest_in)
393 widest_in = p->width();
394 else if (p->is_output() && p->width() > widest_out)
395 widest_out = p->width();
398 // Make sure module is wide enough for ports
399 if (widest_in > widest_out)
400 width(widest_in + 5.0 + border_width()*2.0);
402 width(widest_out + 5.0 + border_width()*2.0);
404 // Make sure module is wide enough for title
405 if (m_canvas_title.property_text_width() + 6.0 > m_width)
406 width(m_canvas_title.property_text_width() + 6.0);
408 // Set height to contain ports and title
409 double height_base = m_canvas_title.property_text_height() + 2;
410 double h = height_base;
411 if (m_ports.size() > 0)
412 h += m_ports.size() * ((*m_ports.begin())->height()+2.0);
415 // Move ports to appropriate locations
419 for (PortList::iterator pi = m_ports.begin(); pi != m_ports.end(); ++pi, ++i) {
422 y = height_base + (i * (p->height() + 2.0));
425 p->property_x() = 1.0;//border_width();
428 p->width(widest_out);
429 p->property_x() = m_width - p->width() - 1.0;//p->border_width();
435 m_canvas_title.property_x() = m_width/2.0;
437 // Update connection locations if we've moved/resized
438 for (PortList::iterator pi = m_ports.begin(); pi != m_ports.end(); ++pi, ++i) {
439 (*pi)->move_connections();
442 // Make things actually move to their new locations (?!)
447 /** Port offset, for connection drawing. See doc/port_offsets.dia */
449 Module::port_connection_point_offset(Port* port)
451 assert(port->module() == this);
452 assert(ports().size() > 0);
454 return (port->connection_coords().get_y()
455 - m_ports.front()->connection_coords().get_y());
459 /** Range of port offsets, for connection drawing. See doc/port_offsets.dia */
461 Module::port_connection_points_range()
463 assert(m_ports.size() > 0);
465 double ret = fabs(m_ports.back()->connection_coords().get_y()
466 - m_ports.front()->connection_coords().get_y());
468 return (ret < 1.0) ? 1.0 : ret;
472 } // namespace LibFlowCanvas