Fix ports group search code
[gmodulargraph.git] / src / gtkgraph-demo.c
blobabe56d0eced293360228fb37dd8b661711a180e8
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * gtkgraph-demo.c
4 * Copyright (C) Nikita Zlobin 2011 <nick87720z@gmail.com>
5 *
6 * gtkgraph is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * gtkgraph is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <goocanvas.h>
21 #include <math.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
26 static gboolean on_root_buttonPress_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data);
27 static gboolean on_root_buttonRelease_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data);
28 static gboolean on_root_dragged (GooCanvasItem * item, GooCanvasItem * target, GdkEventMotion * event, gpointer data);
30 static gboolean on_module_enterNotify_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventCrossing * event, gpointer data);
31 static gboolean on_module_leaveNotify_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventCrossing * event, gpointer data);
33 static gboolean on_module_notify_width (GooCanvasItem * item, GParamSpec * event, gpointer data);
34 static gboolean on_module_notify_height (GooCanvasItem * item, GParamSpec * event, gpointer data);
36 static gboolean on_module_buttonPress_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data);
37 static gboolean on_module_buttonRelease_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data);
38 static gboolean on_module_dragged (GooCanvasItem * item, GooCanvasItem * target, GdkEventMotion * event, gpointer data);
40 static gboolean on_module_text_notify (GooCanvasItem * item, GParamSpec * event, gpointer data);
41 static gboolean on_port_text_notify (GooCanvasItem * item, GParamSpec * event, gpointer data);
43 static gboolean wire_entered (GooCanvasItem * item, GooCanvasItem * target, GdkEventCrossing * event, gpointer data);
44 static gboolean wire_left (GooCanvasItem * item, GooCanvasItem * target, GdkEventCrossing * event, gpointer data);
46 GooCanvasItem * wire;
47 double border_threshold = 1.0;
49 #define drag_button 1
51 typedef
52 struct {
53 struct {
54 unsigned color;
55 float rounding;
56 struct {
57 unsigned threshold;
58 unsigned border_color;
59 } border;
60 struct {
61 unsigned shadow_color;
62 float shadow_size;
63 } shadow;
64 struct {
65 unsigned alignment;
66 char * font;
67 } caption;
68 } moduleBox;
69 } GModularGraphStyle;
71 typedef
72 struct {
73 GooCanvasItem * box; /* Background */
74 GooCanvasItem * outer_border;
75 GooCanvasItem * inner_border;
77 GtkOrientation * orientation;
79 GooCanvasItem * caption;
80 GooCanvasItem * group; /* Just a logical container, without any look */
82 /* List of ports groups */
83 GList * ports;
85 /* Dragging data */
86 double drag_hold_x, drag_hold_y;
87 unsigned drag_cb;
88 } GModularGraph_Module;
90 #define G_MODULAR_GRAPH_MODULE(p) ((GModularGraph_Module *) p)
92 typedef enum {
93 G_MODULAR_GRAPH_PORT_DIRECTION_IN = 1,
94 G_MODULAR_GRAPH_PORT_DIRECTION_OUT
95 } GModularGraphPortDirection;
97 typedef
98 struct {
99 GList * ports;
100 GModularGraphPortDirection direction;
102 unsigned width;
103 unsigned p_count;
104 GooCanvasItem * container;
105 } GModularGraphPortsGroup;
107 #define G_MODULAR_GRAPH_PORTS_GROUP(p) ((GModularGraphPortsGroup *) p)
109 typedef
110 struct {
111 GooCanvasItem * box;
112 GooCanvasItem * text;
113 GooCanvasItem * group;
114 GModularGraphPortsGroup * ports_group;
115 double min_width;
117 double wire_x, wire_y;
118 double direction; /* In deegreens. Also affects wire direction. */
119 } GModularGraph_Port;
121 #define G_MODULAR_GRAPH_PORT(p) ((GModularGraph_Port *) p)
123 /* Canvas data */
124 GtkWidget * canvas;
125 GooCanvasItem * root_item;
126 static double rounding = 3;
128 static GModularGraph_Module * module[2] = {NULL, NULL};
130 static void canvas_text_size (GooCanvasItem * item, double * w, double * h)
132 PangoRectangle rect;
134 goo_canvas_text_get_natural_extents (GOO_CANVAS_TEXT (item), NULL, & rect);
135 if (w) *w = (double) PANGO_PIXELS (rect.width);
136 if (h) *h = (double) PANGO_PIXELS (rect.height);
139 static inline void
140 text_wanted_size (GooCanvasItem * item, double * w, double * h)
142 canvas_text_size (item, w, h);
143 if (w) *w += rounding * 2;
146 static void module_update_size (GModularGraph_Module * module)
148 GList * sib = module->ports;
149 double w = 0, h = 0;
150 double cX = 0, cY = 0, cW = 0, cH = 0;
151 GooCanvasBounds bounds;
153 while (1)
155 GModularGraphPortsGroup * group = G_MODULAR_GRAPH_PORTS_GROUP (sib->data);
157 g_object_get (group->container, "x", & cX, "y", & cY, NULL);
158 goo_canvas_item_get_bounds (group->container, & bounds);
159 cW = bounds.x2 - bounds.x1 + cX;
160 cH = bounds.y2 - bounds.y1 + cY;
162 if (cW > w) w = cW;
163 if (cH > h) h = cH;
165 if (! sib->next) break;
166 sib = sib->next;
168 double text_w, text_h;
169 text_wanted_size (module->caption, & text_w, & text_h);
170 if (w < text_w) w = text_w;
171 h += rounding;
172 g_object_set (module->box, "width", w, "height", h, NULL);
175 GModularGraph_Module * g_modular_graph_module_new (GooCanvasItem * parent,
176 char * caption, char * tooltip,
177 int placing, int x, int y)
179 GModularGraph_Module * module;
180 unsigned width = 50, height = 20;
182 /* Fundament */
183 module = calloc (1, sizeof (GModularGraph_Module));
184 module->group = goo_canvas_group_new (parent, "x", (double) x, "y", (double) y, "tooltip", tooltip, NULL);
186 /* Decoration */
187 module->outer_border = goo_canvas_rect_new (module->group,
188 - border_threshold / 2, - border_threshold / 2,
189 width + border_threshold, height + border_threshold,
190 "radius-x", rounding + border_threshold / 2, "radius-y", rounding + border_threshold / 2,
191 "stroke-color-rgba", 0x0000005f, NULL);
193 module->box = goo_canvas_rect_new (module->group,
194 border_threshold / 2, border_threshold / 2,
195 width - border_threshold, height - border_threshold,
196 "radius-x", rounding, "radius-y", rounding,
197 "stroke-color", "gray60", "fill-color", "gray30",
198 NULL);
200 module->inner_border = goo_canvas_rect_new (module->group,
201 border_threshold * 1.5, border_threshold * 1.5,
202 width - border_threshold * 3, height - border_threshold * 3,
203 "radius-x", rounding - border_threshold / 2, "radius-y", rounding - border_threshold / 2,
204 "stroke-color", "gray40", NULL);
206 g_signal_connect (G_OBJECT (module->box), "notify::width", G_CALLBACK (on_module_notify_width), module);
207 g_signal_connect (G_OBJECT (module->box), "notify::height", G_CALLBACK (on_module_notify_height), module);
209 /* Caption */
210 module->caption = goo_canvas_text_new (module->group, "",
211 rounding, 0, -1, GTK_ANCHOR_NORTH_WEST,
212 "font", "sans 8",
213 "fill-color", "white", NULL);
214 g_signal_connect (G_OBJECT (module->caption), "notify::text", G_CALLBACK (on_module_text_notify), module);
216 /* Dragging capabilities */
217 g_signal_connect (G_OBJECT (module->group), "button-press-event", G_CALLBACK (on_module_buttonPress_event), module);
218 g_signal_connect (G_OBJECT (module->group), "button-release-event", G_CALLBACK (on_module_buttonRelease_event), module);
220 /* Mouse over */
221 g_signal_connect (G_OBJECT (module->group), "enter-notify-event", G_CALLBACK (on_module_enterNotify_event), module);
222 g_signal_connect (G_OBJECT (module->group), "leave-notify-event", G_CALLBACK (on_module_leaveNotify_event), module);
224 g_object_set (G_OBJECT (module->caption), "text", caption, NULL);
225 return module;
228 void g_modular_graph_module_add_ports_group (GModularGraphPortsGroup ** ret, GModularGraph_Module * module, GModularGraphPortDirection direction)
230 GModularGraphPortsGroup * group;
232 group = calloc (1, sizeof(GModularGraphPortsGroup));
233 group->direction = direction;
234 group->container = goo_canvas_table_new (module->group, "x", 0.0, "y", 20.0, "row-spacing", 1.0, "column-spacing", 0.0,
235 //"clip-path", "M 0 0 h 100 v 20 h -20 Z",
236 //"horz-grid-line-width", 1.0, "vert-grid-line-width", 1.0,
237 "stroke-color", "yellow", NULL);
238 module->ports = g_list_append (module->ports, group);
240 if (ret) * ret = group;
243 static double ports_group_update_width (GModularGraphPortsGroup * group)
245 double min_width = 0;
246 GList * sib = group->ports;
248 while (1)
250 if (G_MODULAR_GRAPH_PORT (sib->data)->min_width > min_width)
251 min_width = G_MODULAR_GRAPH_PORT (sib->data)->min_width;
253 if (! sib->next) break;
254 sib = sib->next;
256 sib = group->ports;
257 while (1)
259 g_object_set (G_MODULAR_GRAPH_PORT (sib->data)->box, "width", min_width, NULL);
260 if (! sib->next) break;
261 sib = sib->next;
263 return min_width;
266 void g_modular_graph_module_add_port (GModularGraph_Port ** ret,
267 GModularGraph_Module * module,
268 int direction,
269 char * name, char * tooltip)
271 /* Create ports group */
272 GModularGraphPortsGroup * group = NULL;
273 if (module->ports)
275 GList * elem = module->ports;
276 while (1)
278 if (G_MODULAR_GRAPH_PORTS_GROUP (elem->data)->direction == direction)
280 group = elem->data;
281 break;
283 if (! elem->next) break;
284 elem = elem->next;
287 if (! group)
289 g_modular_graph_module_add_ports_group (& group, module, direction);
292 GModularGraph_Port * port = calloc (1, sizeof (GModularGraph_Port));
294 /* Decoration */
295 port->box = goo_canvas_rect_new (group->container,
296 0.5, 0.5, 50, 5,
297 "tooltip", tooltip,
298 "radius-x", rounding,
299 "radius-y", rounding,
300 "line-width", border_threshold,
301 "stroke-color", "gray70",
302 "fill-color", "gray60",
303 NULL);
304 /* Caption */
305 port->text = goo_canvas_text_new (group->container, "",
306 rounding, 0, -1, GTK_ANCHOR_NORTH_WEST,
307 "tooltip", tooltip,
308 "font", "sans 8",
309 "fill-color", "gray90", NULL);
310 goo_canvas_item_set_child_properties (group->container, port->box, "row", group->p_count, "x-expand", TRUE, "x-fill", TRUE, NULL);
311 goo_canvas_item_set_child_properties (group->container, port->text, "row", group->p_count, "x-expand", TRUE, "x-fill", TRUE,
312 "left-padding", rounding, "x-align", 0.5, NULL);
314 g_signal_connect (G_OBJECT (port->text), "notify::text", G_CALLBACK (on_port_text_notify), port);
316 /* Add port to group */
317 port->ports_group = group;
318 if (! g_list_find (group->ports, port))
319 group->ports = g_list_append (group->ports, port);
321 /* Complete initialization */
322 g_object_set (G_OBJECT (port->text), "text", name, NULL);
323 module_update_size (module);
325 group->p_count++;
326 if (ret) *ret = port;
329 int main( int argc, char ** argv )
331 GtkWidget * win;
332 GtkWidget * scroller;
334 gtk_init (& argc, & argv);
335 win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
336 g_signal_connect (G_OBJECT (win), "delete-event", G_CALLBACK (gtk_main_quit), NULL);
338 scroller = gtk_scrolled_window_new (NULL, NULL);
339 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller), GTK_SHADOW_IN);
340 gtk_container_add (GTK_CONTAINER (win), scroller);
342 /* Create canvas widget */
343 canvas = goo_canvas_new ();
344 gtk_widget_set_size_request (canvas, 600, 450);
345 goo_canvas_set_bounds (GOO_CANVAS (canvas), 0, 0, 1000, 1000);
346 g_object_set (G_OBJECT (canvas), "automatic-bounds", TRUE, NULL);
348 gtk_container_add (GTK_CONTAINER (scroller), canvas);
350 /* Root */
351 root_item = goo_canvas_get_root_item (GOO_CANVAS (canvas));
352 g_object_set (G_OBJECT (root_item), "antialias", CAIRO_ANTIALIAS_SUBPIXEL, NULL);
353 g_object_set (G_OBJECT (root_item), "line-join", CAIRO_LINE_JOIN_ROUND, NULL);
354 g_object_set (G_OBJECT (root_item), "line-cap", CAIRO_LINE_CAP_ROUND, NULL);
355 g_object_set (G_OBJECT (root_item), "line-width", border_threshold, NULL);
356 g_signal_connect (G_OBJECT (root_item), "button-press-enter", G_CALLBACK (on_root_buttonPress_event), NULL);
357 g_signal_connect (G_OBJECT (root_item), "button-press-enter", G_CALLBACK (on_root_buttonRelease_event), NULL);
359 module[0] = g_modular_graph_module_new (root_item, "Module 1", "Module 1 tip", 0, 50, 50);
360 module[1] = g_modular_graph_module_new (root_item, "Module 2", "Module 2 tip", 0, 200, 10);
361 g_modular_graph_module_add_port (NULL, module[1], G_MODULAR_GRAPH_PORT_DIRECTION_IN, "Port 1", "Port 1 tip");
362 g_modular_graph_module_add_port (NULL, module[1], G_MODULAR_GRAPH_PORT_DIRECTION_IN, "Port 2", "Port 2 tip");
363 g_modular_graph_module_add_port (NULL, module[1], G_MODULAR_GRAPH_PORT_DIRECTION_IN, "Port┃", "Port 3 tip");
365 /* 2. Connection */
366 char * wire_data = "M50,100 C100,100 50,200 100,200";
367 wire = goo_canvas_path_new (root_item,
368 wire_data,
369 "line-width", 3.0,
370 "stroke-color", "black", NULL);
371 wire = goo_canvas_path_new (root_item,
372 wire_data,
373 "line-width", 2.0,
374 "stroke-color", "violet", NULL);
375 g_signal_connect (G_OBJECT (wire), "enter-notify-event", G_CALLBACK (wire_entered), NULL);
376 g_signal_connect (G_OBJECT (wire), "leave-notify-event", G_CALLBACK (wire_left), NULL);
378 gtk_widget_set_has_tooltip (canvas, TRUE);
379 g_object_set (wire, "tooltip", "Wire Test", NULL);
381 gtk_widget_show_all (win);
382 gtk_main ();
383 return 0;
386 /* Helpers (nothing yet) */
388 /* Root callbacks */
390 static gboolean on_root_buttonPress_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data)
392 return FALSE;
395 static gboolean on_root_buttonRelease_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data)
397 return FALSE;
400 static gboolean on_root_dragged (GooCanvasItem * item, GooCanvasItem * target, GdkEventMotion * event, gpointer data)
402 return FALSE;
405 /* Module callbacks */
407 static gboolean on_module_enterNotify_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventCrossing * event, gpointer data)
409 char
410 * inner_border_color = "gray50",
411 * outer_border_color = "gray70";
412 g_object_set (G_MODULAR_GRAPH_MODULE (data)->inner_border, "stroke-color", inner_border_color, NULL);
413 g_object_set (G_MODULAR_GRAPH_MODULE (data)->box, "stroke-color", outer_border_color, NULL);
414 return FALSE;
417 static gboolean on_module_leaveNotify_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventCrossing * event, gpointer data)
419 char
420 * inner_border_color = "gray40",
421 * outer_border_color = "gray60";
422 g_object_set (G_MODULAR_GRAPH_MODULE (data)->inner_border, "stroke-color", inner_border_color, NULL);
423 g_object_set (G_MODULAR_GRAPH_MODULE (data)->box, "stroke-color", outer_border_color, NULL);
424 return FALSE;
427 static gboolean on_module_notify_width (GooCanvasItem * item, GParamSpec * event, gpointer data)
429 double w;
430 g_object_get (G_MODULAR_GRAPH_MODULE (data)->box, "width", & w, NULL);
431 g_object_set (G_MODULAR_GRAPH_MODULE (data)->inner_border, "width", w - border_threshold * 2, NULL);
432 g_object_set (G_MODULAR_GRAPH_MODULE (data)->outer_border, "width", w + border_threshold * 2, NULL);
433 return FALSE;
436 static gboolean on_module_notify_height (GooCanvasItem * item, GParamSpec * event, gpointer data)
438 double h;
439 g_object_get (G_MODULAR_GRAPH_MODULE (data)->box, "height", & h, NULL);
440 g_object_set (G_MODULAR_GRAPH_MODULE (data)->inner_border, "height", h - border_threshold * 2, NULL);
441 g_object_set (G_MODULAR_GRAPH_MODULE (data)->outer_border, "height", h + border_threshold * 2, NULL);
442 return FALSE;
445 #define DRAG_CB( mod ) (G_MODULAR_GRAPH_MODULE( mod )->drag_cb)
446 #define DRAG_HOLD_X( mod ) (G_MODULAR_GRAPH_MODULE( mod )->drag_hold_x)
447 #define DRAG_HOLD_Y( mod ) (G_MODULAR_GRAPH_MODULE( mod )->drag_hold_y)
449 static gboolean drag_redraw = TRUE;
450 static unsigned drag_redraw_timeout_id = 0;
452 static gboolean drag_redraw_timeout (gpointer data)
454 drag_redraw = TRUE;
455 return TRUE;
458 static
459 gboolean on_module_buttonPress_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data)
461 if (event->button == drag_button)
463 g_object_get (G_OBJECT (item),
464 "x", & DRAG_HOLD_X (data),
465 "y", & DRAG_HOLD_Y (data), NULL);
466 DRAG_HOLD_X (data) = event->x - DRAG_HOLD_X (data);
467 DRAG_HOLD_Y (data) = event->y - DRAG_HOLD_Y (data);
469 DRAG_CB (data) = g_signal_connect (G_OBJECT (root_item), "motion-notify-event", G_CALLBACK (on_module_dragged), data);
470 drag_redraw_timeout_id = g_timeout_add (30, drag_redraw_timeout, NULL);
472 return FALSE;
475 static
476 gboolean on_module_buttonRelease_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data)
478 if (event->button == drag_button)
480 g_source_remove (drag_redraw_timeout_id);
481 drag_redraw_timeout_id = 0;
482 if (DRAG_CB (data))
483 g_signal_handler_disconnect (G_OBJECT (root_item), DRAG_CB (data));
485 return FALSE;
488 static
489 gboolean on_module_dragged (GooCanvasItem * item, GooCanvasItem * target, GdkEventMotion * event, gpointer data)
491 if (drag_redraw)
493 g_object_set (G_MODULAR_GRAPH_MODULE (data)->group, "x", event->x - DRAG_HOLD_X (data), NULL);
494 g_object_set (G_MODULAR_GRAPH_MODULE (data)->group, "y", event->y - DRAG_HOLD_Y (data), NULL);
495 drag_redraw = FALSE;
497 return FALSE;
500 static gboolean on_module_text_notify (GooCanvasItem * item, GParamSpec * event, gpointer data)
502 double w, h;
503 text_wanted_size (item, &w, &h);
505 g_object_set (G_MODULAR_GRAPH_MODULE (data)->box, "width", w, NULL);
506 g_object_set (G_MODULAR_GRAPH_MODULE (data)->box, "height", h, NULL);
508 return FALSE;
511 /* Port callbacks */
513 static gboolean on_port_text_notify (GooCanvasItem * item, GParamSpec * event, gpointer data)
515 double w, h;
516 text_wanted_size (item, &w, &h);
517 if (G_MODULAR_GRAPH_PORT (data)->min_width == w) return FALSE;
519 G_MODULAR_GRAPH_PORT (data)->min_width = w;
520 ports_group_update_width (G_MODULAR_GRAPH_PORT (data)->ports_group);
521 g_object_set (G_MODULAR_GRAPH_PORT (data)->box, "height", h, NULL);
523 return FALSE;
526 /* Wire callbacks */
528 static
529 gboolean wire_entered (GooCanvasItem *item,
530 GooCanvasItem *target,
531 GdkEventCrossing *event,
532 gpointer data)
534 g_object_set (item, "stroke-color", "white", NULL);
535 return FALSE;
538 static
539 gboolean wire_left (GooCanvasItem *item,
540 GooCanvasItem *target,
541 GdkEventCrossing *event,
542 gpointer data)
544 g_object_set (item, "stroke-color", "violet", NULL);
545 return FALSE;