summaryrefslogtreecommitdiffstats
path: root/src/port.c
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2011-12-06 21:01:38 +0000
committerDavid Robillard <d@drobilla.net>2011-12-06 21:01:38 +0000
commit0731f12beaa0cfc0de56dc05ca3814143fd394a5 (patch)
treed8a98ee48badba378172d3a1c46fba2f2e266d37 /src/port.c
downloadganv-0731f12beaa0cfc0de56dc05ca3814143fd394a5.tar.gz
ganv-0731f12beaa0cfc0de56dc05ca3814143fd394a5.tar.bz2
ganv-0731f12beaa0cfc0de56dc05ca3814143fd394a5.zip
FlowCanvas's successor is hereby dubbed Ganv.
git-svn-id: http://svn.drobilla.net/lad/trunk/ganv@3820 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/port.c')
-rw-r--r--src/port.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/src/port.c b/src/port.c
new file mode 100644
index 0000000..7316846
--- /dev/null
+++ b/src/port.c
@@ -0,0 +1,420 @@
+/* This file is part of Ganv.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ganv 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 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Ganv 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 Ganv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <math.h>
+
+#include "ganv/canvas.h"
+#include "ganv/port.h"
+#include "ganv/module.h"
+
+#include "./boilerplate.h"
+#include "./ganv-private.h"
+#include "./gettext.h"
+
+static const double PORT_LABEL_HPAD = 4.0;
+static const double PORT_LABEL_VPAD = 1.0;
+
+G_DEFINE_TYPE(GanvPort, ganv_port, GANV_TYPE_BOX)
+
+static GanvBoxClass* parent_class;
+
+enum {
+ PROP_0,
+ PROP_IS_INPUT
+};
+
+static void
+ganv_port_init(GanvPort* port)
+{
+ port->is_input = TRUE;
+}
+
+static void
+ganv_port_destroy(GtkObject* object)
+{
+ g_return_if_fail(object != NULL);
+ g_return_if_fail(GANV_IS_PORT(object));
+
+ GnomeCanvasItem* item = GNOME_CANVAS_ITEM(object);
+ GanvPort* port = GANV_PORT(object);
+ GanvCanvas* canvas = GANV_CANVAS(item->canvas);
+ if (port->is_input) {
+ ganv_canvas_for_each_edge_to(canvas,
+ &port->box.node,
+ ganv_edge_remove);
+ } else {
+ ganv_canvas_for_each_edge_from(canvas,
+ &port->box.node,
+ ganv_edge_remove);
+ }
+
+ if (GTK_OBJECT_CLASS(parent_class)->destroy) {
+ (*GTK_OBJECT_CLASS(parent_class)->destroy)(object);
+ }
+}
+
+static void
+ganv_port_set_property(GObject* object,
+ guint prop_id,
+ const GValue* value,
+ GParamSpec* pspec)
+{
+ g_return_if_fail(object != NULL);
+ g_return_if_fail(GANV_IS_PORT(object));
+
+ GanvPort* port = GANV_PORT(object);
+
+ switch (prop_id) {
+ SET_CASE(IS_INPUT, boolean, port->is_input);
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ganv_port_get_property(GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec)
+{
+ g_return_if_fail(object != NULL);
+ g_return_if_fail(GANV_IS_PORT(object));
+
+ GanvPort* port = GANV_PORT(object);
+
+ switch (prop_id) {
+ GET_CASE(IS_INPUT, boolean, port->is_input);
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ganv_port_tail_vector(const GanvNode* self,
+ const GanvNode* head,
+ double* x,
+ double* y,
+ double* dx,
+ double* dy)
+{
+ GanvPort* port = GANV_PORT(self);
+
+ double px, py;
+ g_object_get(G_OBJECT(self), "x", &px, "y", &py, NULL);
+
+ *x = px + ganv_box_get_width(&port->box);
+ *y = py + ganv_box_get_height(&port->box) / 2.0;
+ *dx = 1.0;
+ *dy = 0.0;
+
+ gnome_canvas_item_i2w(GNOME_CANVAS_ITEM(self)->parent, x, y);
+}
+
+static void
+ganv_port_head_vector(const GanvNode* self,
+ const GanvNode* tail,
+ double* x,
+ double* y,
+ double* dx,
+ double* dy)
+{
+ GanvPort* port = GANV_PORT(self);
+
+ double px, py;
+ g_object_get(G_OBJECT(self), "x", &px, "y", &py, NULL);
+
+ *x = px;
+ *y = py + ganv_box_get_height(&port->box) / 2.0;
+ *dx = -1.0;
+ *dy = 0.0;
+
+ gnome_canvas_item_i2w(GNOME_CANVAS_ITEM(self)->parent, x, y);
+}
+
+static void
+ganv_port_resize(GanvNode* self)
+{
+ GanvPort* port = GANV_PORT(self);
+ GanvNode* node = GANV_NODE(self);
+
+ double label_w, label_h;
+ g_object_get(node->label,
+ "width", &label_w,
+ "height", &label_h,
+ NULL);
+
+ ganv_box_set_width(&port->box, label_w + (PORT_LABEL_HPAD * 2.0));
+ ganv_box_set_height(&port->box, label_h + (PORT_LABEL_VPAD * 2.0));
+
+ gnome_canvas_item_set(GNOME_CANVAS_ITEM(node->label),
+ "x", PORT_LABEL_HPAD,
+ "y", PORT_LABEL_VPAD,
+ NULL);
+
+ if (parent_class->parent_class.resize) {
+ parent_class->parent_class.resize(self);
+ }
+}
+
+static void
+ganv_port_set_width(GanvBox* box,
+ double width)
+{
+ GanvPort* port = GANV_PORT(box);
+ parent_class->set_width(box, width);
+ if (port->control) {
+ ganv_port_set_control_value(port, port->control->value);
+ }
+}
+
+static void
+ganv_port_set_height(GanvBox* box,
+ double height)
+{
+ GanvPort* port = GANV_PORT(box);
+ parent_class->set_height(box, height);
+ if (port->control) {
+ double control_y1;
+ g_object_get(port->control->rect, "y1", &control_y1, NULL);
+ gnome_canvas_item_set(GNOME_CANVAS_ITEM(port->control->rect),
+ "y2", control_y1 + height,
+ NULL);
+ }
+}
+
+static gboolean
+on_event(GanvNode* node, GdkEvent* event)
+{
+ GnomeCanvasItem* item = GNOME_CANVAS_ITEM(node);
+ GanvCanvas* canvas = GANV_CANVAS(item->canvas);
+
+ return ganv_canvas_port_event(canvas, GANV_PORT(node), event);
+}
+
+static void
+ganv_port_class_init(GanvPortClass* class)
+{
+ GObjectClass* gobject_class = (GObjectClass*)class;
+ GtkObjectClass* object_class = (GtkObjectClass*)class;
+ GanvNodeClass* node_class = (GanvNodeClass*)class;
+ GanvBoxClass* box_class = (GanvBoxClass*)class;
+
+ parent_class = GANV_BOX_CLASS(g_type_class_peek_parent(class));
+
+ gobject_class->set_property = ganv_port_set_property;
+ gobject_class->get_property = ganv_port_get_property;
+
+ g_object_class_install_property(
+ gobject_class, PROP_IS_INPUT, g_param_spec_boolean(
+ "is-input",
+ _("is input"),
+ _("whether this port is an input (or an output, if false)"),
+ 0,
+ G_PARAM_READWRITE));
+
+ object_class->destroy = ganv_port_destroy;
+
+ node_class->on_event = on_event;
+ node_class->tail_vector = ganv_port_tail_vector;
+ node_class->head_vector = ganv_port_head_vector;
+ node_class->resize = ganv_port_resize;
+
+ box_class->set_width = ganv_port_set_width;
+ box_class->set_height = ganv_port_set_height;
+}
+
+GanvPort*
+ganv_port_new(GanvModule* module,
+ gboolean is_input,
+ const char* first_prop_name, ...)
+{
+ GanvPort* port = GANV_PORT(
+ g_object_new(ganv_port_get_type(), NULL));
+
+ GnomeCanvasItem* item = GNOME_CANVAS_ITEM(port);
+ va_list args;
+ va_start(args, first_prop_name);
+ gnome_canvas_item_construct(item,
+ GNOME_CANVAS_GROUP(module),
+ first_prop_name, args);
+ va_end(args);
+
+ port->is_input = is_input;
+
+ GanvBox* box = GANV_BOX(port);
+ box->radius_tl = (is_input ? 0.0 : 4.0);
+ box->radius_tr = (is_input ? 4.0 : 0.0);
+ box->radius_br = (is_input ? 4.0 : 0.0);
+ box->radius_bl = (is_input ? 0.0 : 4.0);
+
+ GanvNode* node = GANV_NODE(port);
+ node->can_tail = !is_input;
+ node->can_head = is_input;
+ node->draggable = FALSE;
+
+ GanvCanvas* canvas = GANV_CANVAS(item->canvas);
+ if (!node->label) {
+ const double depth = ganv_module_get_empty_port_depth(module);
+ const double breadth = ganv_module_get_empty_port_breadth(module);
+ if (canvas->direction == GANV_HORIZONTAL) {
+ ganv_box_set_width(box, depth);
+ ganv_box_set_height(box, breadth);
+ } else {
+ ganv_box_set_width(box, breadth);
+ ganv_box_set_height(box, depth);
+ }
+ }
+
+ ganv_module_add_port(module, port);
+ return port;
+}
+
+void
+ganv_port_show_control(GanvPort* port)
+{
+ port->control = (GanvPortControl*)malloc(sizeof(GanvPortControl));
+ port->control->value = 0.0f;
+ port->control->min = 0.0f;
+ port->control->max = 0.0f;
+ port->control->is_toggle = FALSE;
+ port->control->rect = GANV_BOX(gnome_canvas_item_new(
+ GNOME_CANVAS_GROUP(port),
+ ganv_box_get_type(),
+ "x1", 0.0,
+ "y1", 0.0,
+ "x2", 0.0,
+ "y2", ganv_box_get_height(&port->box),
+ "fill-color", 0xFFFFFF80,
+ "border-width", 0.0,
+ NULL));
+ gnome_canvas_item_show(GNOME_CANVAS_ITEM(port->control->rect));
+}
+
+void
+ganv_port_hide_control(GanvPort* port)
+{
+ gtk_object_destroy(GTK_OBJECT(port->control->rect));
+ free(port->control);
+ port->control = NULL;
+}
+
+void
+ganv_port_set_control_is_toggle(GanvPort* port,
+ gboolean is_toggle)
+{
+ if (port->control) {
+ port->control->is_toggle = is_toggle;
+ ganv_port_set_control_value(port, port->control->value);
+ }
+}
+
+void
+ganv_port_set_control_value(GanvPort* port,
+ float value)
+{
+ if (!port->control) {
+ return;
+ }
+
+ if (port->control->is_toggle) {
+ if (value != 0.0) {
+ value = port->control->max;
+ } else {
+ value = port->control->min;
+ }
+ }
+
+ if (value < port->control->min) {
+ port->control->min = value;
+ }
+ if (value > port->control->max) {
+ port->control->max = value;
+ }
+
+ if (port->control->max == port->control->min) {
+ port->control->max = port->control->min + 1.0;
+ }
+
+ const int inf = isinf(value);
+ if (inf == -1) {
+ value = port->control->min;
+ } else if (inf == 1) {
+ value = port->control->max;
+ }
+
+ const double w = (value - port->control->min)
+ / (port->control->max - port->control->min)
+ * ganv_box_get_width(&port->box);
+
+ if (isnan(w)) {
+ return;
+ }
+
+ ganv_box_set_width(port->control->rect, MAX(0.0, w - 1.0));
+#if 0
+ if (signal && _control->value == value)
+ signal = false;
+#endif
+
+ port->control->value = value;
+}
+
+void
+ganv_port_set_control_min(GanvPort* port,
+ float min)
+{
+ if (port->control) {
+ port->control->min = min;
+ ganv_port_set_control_value(port, port->control->value);
+ }
+}
+
+void
+ganv_port_set_control_max(GanvPort* port,
+ float max)
+{
+ if (port->control) {
+ port->control->max = max;
+ ganv_port_set_control_value(port, port->control->value);
+ }
+}
+
+double
+ganv_port_get_natural_width(const GanvPort* port)
+{
+ /*
+ Canvas* const canvas = _module->canvas();
+ if (canvas->direction() == Canvas::VERTICAL) {
+ return _module->empty_port_breadth();
+ } else*/
+ if (port->box.node.label) {
+ double label_w;
+ g_object_get(port->box.node.label, "width", &label_w, NULL);
+ return label_w + (PORT_LABEL_HPAD * 2.0);
+ } else {
+ //return _module->empty_port_depth();
+ return 4.0;
+ }
+}
+
+GanvModule*
+ganv_port_get_module(const GanvPort* port)
+{
+ return GANV_MODULE(GNOME_CANVAS_ITEM(port)->parent);
+}