Ports group; Code optimization; Begining to work at paning by dragging;
[gmodulargraph.git] / src / gtkgraph-demo.c
blobda20f759770eb8ec79e081c21187aa5978c240ab
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;
116 double wire_x, wire_y;
117 double direction; /* In deegreens. Also affects wire direction. */
118 } GModularGraph_Port;
120 #define G_MODULAR_GRAPH_PORT(p) ((GModularGraph_Port *) p)
122 /* Canvas data */
123 GtkWidget * canvas;
124 GooCanvasItem * root_item;
125 static double rounding = 3;
127 static GModularGraph_Module * module[2] = {NULL, NULL};
129 GModularGraph_Module * g_modular_graph_module_new (GooCanvasItem * parent,
130 char * caption, char * tooltip,
131 int placing, int x, int y)
133 GModularGraph_Module * module;
134 unsigned width = 50, height = 20;
136 /* Fundament */
137 module = calloc (1, sizeof (GModularGraph_Module));
138 module->group = goo_canvas_group_new (parent, "x", (double) x, "y", (double) y, "tooltip", tooltip, NULL);
140 /* Decoration */
141 module->outer_border = goo_canvas_rect_new (module->group,
142 - border_threshold / 2, - border_threshold / 2,
143 width + border_threshold, height + border_threshold,
144 "radius-x", rounding + border_threshold / 2, "radius-y", rounding + border_threshold / 2,
145 "stroke-color-rgba", 0x0000005f, NULL);
147 module->box = goo_canvas_rect_new (module->group,
148 border_threshold / 2, border_threshold / 2,
149 width - border_threshold, height - border_threshold,
150 "radius-x", rounding, "radius-y", rounding,
151 "stroke-color", "gray60", "fill-color", "gray50",
152 NULL);
154 module->inner_border = goo_canvas_rect_new (module->group,
155 border_threshold * 1.5, border_threshold * 1.5,
156 width - border_threshold * 3, height - border_threshold * 3,
157 "radius-x", rounding - border_threshold / 2, "radius-y", rounding - border_threshold / 2,
158 "stroke-color", "gray50", NULL);
160 g_signal_connect (G_OBJECT (module->box), "notify::width", G_CALLBACK (on_module_notify_width), module);
161 g_signal_connect (G_OBJECT (module->box), "notify::height", G_CALLBACK (on_module_notify_height), module);
163 /* Caption */
164 module->caption = goo_canvas_text_new (module->group, "",
165 rounding, 0, -1, GTK_ANCHOR_NORTH_WEST,
166 "font", "sans 8",
167 "fill-color", "white", NULL);
168 g_signal_connect (G_OBJECT (module->caption), "notify::text", G_CALLBACK (on_module_text_notify), module);
170 /* Dragging capabilities */
171 g_signal_connect (G_OBJECT (module->group), "button-press-event", G_CALLBACK (on_module_buttonPress_event), module);
172 g_signal_connect (G_OBJECT (module->group), "button-release-event", G_CALLBACK (on_module_buttonRelease_event), module);
174 /* Mouse over */
175 g_signal_connect (G_OBJECT (module->group), "enter-notify-event", G_CALLBACK (on_module_enterNotify_event), module);
176 g_signal_connect (G_OBJECT (module->group), "leave-notify-event", G_CALLBACK (on_module_leaveNotify_event), module);
178 g_object_set (G_OBJECT (module->caption), "text", caption, NULL);
179 return module;
182 void g_modular_graph_module_add_ports_group (GModularGraphPortsGroup ** ret, GModularGraph_Module * module, GModularGraphPortDirection direction)
184 GModularGraphPortsGroup * group;
186 group = calloc (1, sizeof(GModularGraphPortsGroup));
187 group->direction = direction;
188 group->container = goo_canvas_table_new (module->group, "x", 0.0, "y", 20.0, "row-spacing", 1.0, "column-spacing", 1.0,
189 /*"horz-grid-line-width", 1.0,*/ NULL);
190 module->ports = g_list_append (module->ports, group);
192 if (ret) * ret = group;
195 static void canvas_text_size (GooCanvasItem * item, double * w, double * h)
197 PangoRectangle rect;
199 goo_canvas_text_get_natural_extents (GOO_CANVAS_TEXT (item), NULL, & rect);
200 if (w) *w = (double) PANGO_PIXELS (rect.width);
201 if (h) *h = (double) PANGO_PIXELS (rect.height);
204 void g_modular_graph_ports_group_add_port (GModularGraphPortsGroup * group, GModularGraph_Port * port)
206 /* Add to list */
207 if (! g_list_find (group->ports, port))
208 group->ports = g_list_append (group->ports, port);
210 /* Update group width */
211 double w;
212 canvas_text_size (port->text, &w, NULL);
213 if (w > group->width)
214 group->width = w;
217 void g_modular_graph_module_add_port (GModularGraph_Port ** ret,
218 GModularGraph_Module * module,
219 int direction,
220 char * name, char * tooltip)
222 /* Create ports group */
223 GModularGraphPortsGroup * group = NULL;
224 if (module->ports)
226 GList * elem = module->ports;
227 while (elem->data)
228 if (G_MODULAR_GRAPH_PORTS_GROUP (elem->data)->direction == direction)
230 group = elem->data;
231 break;
234 if (! group)
236 g_modular_graph_module_add_ports_group (& group, module, direction);
239 GModularGraph_Port * port = calloc (1, sizeof (GModularGraph_Port));
241 /* Decoration */
242 port->box = goo_canvas_rect_new (group->container,
243 0.5, 0.5, 50, 15,
244 "tooltip", tooltip,
245 "radius-x", rounding,
246 "radius-y", rounding,
247 "line-width", border_threshold,
248 "stroke-color", "gray60",
249 "fill-color", "gray50",
250 NULL);
251 /* Caption */
252 port->text = goo_canvas_text_new (group->container, "",
253 rounding, 0, -1, GTK_ANCHOR_NORTH_WEST,
254 "tooltip", tooltip,
255 "font", "sans 8",
256 "fill-color", "white", NULL);
257 goo_canvas_item_set_child_properties (group->container, port->box, "row", group->p_count, "x-expand", TRUE, "x-fill", TRUE, NULL);
258 goo_canvas_item_set_child_properties (group->container, port->text, "row", group->p_count, "x-expand", TRUE, "x-fill", TRUE,
259 "left-padding", rounding, NULL);
260 g_signal_connect (G_OBJECT (port->text), "notify::text", G_CALLBACK (on_port_text_notify), port);
262 g_object_set (G_OBJECT (group->container), "width", 100.0, NULL);
263 g_object_set (G_OBJECT (port->text), "text", name, NULL);
265 group->p_count++;
266 if (ret) *ret = port;
269 int main( int argc, char ** argv )
271 GtkWidget * win;
272 GtkWidget * scroller;
274 gtk_init (& argc, & argv);
275 win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
276 g_signal_connect (G_OBJECT (win), "delete-event", G_CALLBACK (gtk_main_quit), NULL);
278 scroller = gtk_scrolled_window_new (NULL, NULL);
279 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller), GTK_SHADOW_IN);
280 gtk_container_add (GTK_CONTAINER (win), scroller);
282 /* Create canvas widget */
283 canvas = goo_canvas_new ();
284 gtk_widget_set_size_request (canvas, 600, 450);
285 goo_canvas_set_bounds (GOO_CANVAS (canvas), 0, 0, 1000, 1000);
286 g_object_set (G_OBJECT (canvas), "automatic-bounds", TRUE, NULL);
288 gtk_container_add (GTK_CONTAINER (scroller), canvas);
290 /* Root */
291 root_item = goo_canvas_get_root_item (GOO_CANVAS (canvas));
292 g_object_set (G_OBJECT (root_item), "antialias", CAIRO_ANTIALIAS_SUBPIXEL, NULL);
293 g_object_set (G_OBJECT (root_item), "line-join", CAIRO_LINE_JOIN_ROUND, NULL);
294 g_object_set (G_OBJECT (root_item), "line-cap", CAIRO_LINE_CAP_ROUND, NULL);
295 g_object_set (G_OBJECT (root_item), "line-width", border_threshold, NULL);
296 g_signal_connect (G_OBJECT (root_item), "button-press-enter", G_CALLBACK (on_root_buttonPress_event), NULL);
297 g_signal_connect (G_OBJECT (root_item), "button-press-enter", G_CALLBACK (on_root_buttonRelease_event), NULL);
299 module[0] = g_modular_graph_module_new (root_item, "Module 1", "Module 1 tip", 0, 50, 50);
300 module[1] = g_modular_graph_module_new (root_item, "Module 2", "Module 2 tip", 0, 200, 10);
301 g_modular_graph_module_add_port (NULL, module[1], G_MODULAR_GRAPH_PORT_DIRECTION_IN, "Port 1", "Port 1 tip");
302 g_modular_graph_module_add_port (NULL, module[1], G_MODULAR_GRAPH_PORT_DIRECTION_IN, "Port 2", "Port 2 tip");
303 g_modular_graph_module_add_port (NULL, module[1], G_MODULAR_GRAPH_PORT_DIRECTION_IN, "Port 3 new", "Port 3 tip");
305 /* 2. Connection */
306 char * wire_data = "M50,100 C100,100 50,200 100,200";
307 wire = goo_canvas_path_new (root_item,
308 wire_data,
309 "line-width", 3.0,
310 "stroke-color", "black", NULL);
311 wire = goo_canvas_path_new (root_item,
312 wire_data,
313 "line-width", 2.0,
314 "stroke-color", "violet", NULL);
315 g_signal_connect (G_OBJECT (wire), "enter-notify-event", G_CALLBACK (wire_entered), NULL);
316 g_signal_connect (G_OBJECT (wire), "leave-notify-event", G_CALLBACK (wire_left), NULL);
318 gtk_widget_set_has_tooltip (canvas, TRUE);
319 g_object_set (wire, "tooltip", "Wire Test", NULL);
321 gtk_widget_show_all (win);
322 gtk_main ();
323 return 0;
326 /* Helpers (nothing yet) */
328 /* Root callbacks */
330 static gboolean on_root_buttonPress_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data)
332 return FALSE;
335 static gboolean on_root_buttonRelease_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data)
337 return FALSE;
340 static gboolean on_root_dragged (GooCanvasItem * item, GooCanvasItem * target, GdkEventMotion * event, gpointer data)
342 return FALSE;
345 /* Module callbacks */
347 static gboolean on_module_enterNotify_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventCrossing * event, gpointer data)
349 char
350 * inner_border_color = "gray60",
351 * outer_border_color = "gray70";
352 g_object_set (G_MODULAR_GRAPH_MODULE (data)->inner_border, "stroke-color", inner_border_color, NULL);
353 g_object_set (G_MODULAR_GRAPH_MODULE (data)->box, "stroke-color", outer_border_color, NULL);
354 return FALSE;
357 static gboolean on_module_leaveNotify_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventCrossing * event, gpointer data)
359 char
360 * inner_border_color = "gray50",
361 * outer_border_color = "gray60";
362 g_object_set (G_MODULAR_GRAPH_MODULE (data)->inner_border, "stroke-color", inner_border_color, NULL);
363 g_object_set (G_MODULAR_GRAPH_MODULE (data)->box, "stroke-color", outer_border_color, NULL);
364 return FALSE;
367 static gboolean on_module_notify_width (GooCanvasItem * item, GParamSpec * event, gpointer data)
369 double w;
370 g_object_get (G_MODULAR_GRAPH_MODULE (data)->box, "width", & w, NULL);
371 g_object_set (G_MODULAR_GRAPH_MODULE (data)->inner_border, "width", w - border_threshold * 2, NULL);
372 g_object_set (G_MODULAR_GRAPH_MODULE (data)->outer_border, "width", w + border_threshold * 2, NULL);
373 return FALSE;
376 static gboolean on_module_notify_height (GooCanvasItem * item, GParamSpec * event, gpointer data)
378 double h;
379 g_object_get (G_MODULAR_GRAPH_MODULE (data)->box, "height", & h, NULL);
380 g_object_set (G_MODULAR_GRAPH_MODULE (data)->inner_border, "height", h - border_threshold * 2, NULL);
381 g_object_set (G_MODULAR_GRAPH_MODULE (data)->outer_border, "height", h + border_threshold * 2, NULL);
382 return FALSE;
385 #define DRAG_CB( mod ) (G_MODULAR_GRAPH_MODULE( mod )->drag_cb)
386 #define DRAG_HOLD_X( mod ) (G_MODULAR_GRAPH_MODULE( mod )->drag_hold_x)
387 #define DRAG_HOLD_Y( mod ) (G_MODULAR_GRAPH_MODULE( mod )->drag_hold_y)
389 static gboolean drag_redraw = TRUE;
390 static unsigned drag_redraw_timeout_id = 0;
392 static gboolean drag_redraw_timeout (gpointer data)
394 drag_redraw = TRUE;
395 return TRUE;
398 static
399 gboolean on_module_buttonPress_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data)
401 if (event->button == drag_button)
403 g_object_get (G_OBJECT (item),
404 "x", & DRAG_HOLD_X (data),
405 "y", & DRAG_HOLD_Y (data), NULL);
406 DRAG_HOLD_X (data) = event->x - DRAG_HOLD_X (data);
407 DRAG_HOLD_Y (data) = event->y - DRAG_HOLD_Y (data);
409 DRAG_CB (data) = g_signal_connect (G_OBJECT (root_item), "motion-notify-event", G_CALLBACK (on_module_dragged), data);
410 drag_redraw_timeout_id = g_timeout_add (30, drag_redraw_timeout, NULL);
412 return FALSE;
415 static
416 gboolean on_module_buttonRelease_event (GooCanvasItem * item, GooCanvasItem * target, GdkEventButton * event, gpointer data)
418 if (event->button == drag_button)
420 g_source_remove (drag_redraw_timeout_id);
421 drag_redraw_timeout_id = 0;
422 if (DRAG_CB (data))
423 g_signal_handler_disconnect (G_OBJECT (root_item), DRAG_CB (data));
425 return FALSE;
428 static
429 gboolean on_module_dragged (GooCanvasItem * item, GooCanvasItem * target, GdkEventMotion * event, gpointer data)
431 if (drag_redraw)
433 g_object_set (G_MODULAR_GRAPH_MODULE (data)->group, "x", event->x - DRAG_HOLD_X (data), NULL);
434 g_object_set (G_MODULAR_GRAPH_MODULE (data)->group, "y", event->y - DRAG_HOLD_Y (data), NULL);
435 drag_redraw = FALSE;
437 return FALSE;
440 static gboolean on_module_text_notify (GooCanvasItem * item, GParamSpec * event, gpointer data)
442 double w, h;
444 canvas_text_size (item, &w, &h);
445 w += rounding * 2;
447 g_object_set (G_MODULAR_GRAPH_MODULE (data)->box, "width", w, NULL);
448 g_object_set (G_MODULAR_GRAPH_MODULE (data)->box, "height", h, NULL);
450 return FALSE;
453 /* Port callbacks */
455 static gboolean on_port_text_notify (GooCanvasItem * item, GParamSpec * event, gpointer data)
457 double w, h;
459 canvas_text_size (item, &w, &h);
460 w += rounding * 2;
462 g_object_set (G_MODULAR_GRAPH_PORT (data)->box, "width", w, NULL);
463 g_object_set (G_MODULAR_GRAPH_PORT (data)->box, "height", h, NULL);
465 return FALSE;
468 /* Wire callbacks */
470 static
471 gboolean wire_entered (GooCanvasItem *item,
472 GooCanvasItem *target,
473 GdkEventCrossing *event,
474 gpointer data)
476 g_object_set (item, "stroke-color", "white", NULL);
477 return FALSE;
480 static
481 gboolean wire_left (GooCanvasItem *item,
482 GooCanvasItem *target,
483 GdkEventCrossing *event,
484 gpointer data)
486 g_object_set (item, "stroke-color", "violet", NULL);
487 return FALSE;