1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) Nikita Zlobin 2011 <nick87720z@gmail.com>
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>
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
);
47 double border_threshold
= 1.0;
58 unsigned border_color
;
61 unsigned shadow_color
;
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 */
86 double drag_hold_x
, drag_hold_y
;
88 } GModularGraph_Module
;
90 #define G_MODULAR_GRAPH_MODULE(p) ((GModularGraph_Module *) p)
93 G_MODULAR_GRAPH_PORT_DIRECTION_IN
= 1,
94 G_MODULAR_GRAPH_PORT_DIRECTION_OUT
95 } GModularGraphPortDirection
;
100 GModularGraphPortDirection direction
;
104 GooCanvasItem
* container
;
105 } GModularGraphPortsGroup
;
107 #define G_MODULAR_GRAPH_PORTS_GROUP(p) ((GModularGraphPortsGroup *) p)
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)
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;
137 module
= calloc (1, sizeof (GModularGraph_Module
));
138 module
->group
= goo_canvas_group_new (parent
, "x", (double) x
, "y", (double) y
, "tooltip", tooltip
, NULL
);
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",
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
);
164 module
->caption
= goo_canvas_text_new (module
->group
, "",
165 rounding
, 0, -1, GTK_ANCHOR_NORTH_WEST
,
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
);
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
);
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
)
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
)
207 if (! g_list_find (group
->ports
, port
))
208 group
->ports
= g_list_append (group
->ports
, port
);
210 /* Update group width */
212 canvas_text_size (port
->text
, &w
, NULL
);
213 if (w
> group
->width
)
217 void g_modular_graph_module_add_port (GModularGraph_Port
** ret
,
218 GModularGraph_Module
* module
,
220 char * name
, char * tooltip
)
222 /* Create ports group */
223 GModularGraphPortsGroup
* group
= NULL
;
226 GList
* elem
= module
->ports
;
228 if (G_MODULAR_GRAPH_PORTS_GROUP (elem
->data
)->direction
== direction
)
236 g_modular_graph_module_add_ports_group (& group
, module
, direction
);
239 GModularGraph_Port
* port
= calloc (1, sizeof (GModularGraph_Port
));
242 port
->box
= goo_canvas_rect_new (group
->container
,
245 "radius-x", rounding
,
246 "radius-y", rounding
,
247 "line-width", border_threshold
,
248 "stroke-color", "gray60",
249 "fill-color", "gray50",
252 port
->text
= goo_canvas_text_new (group
->container
, "",
253 rounding
, 0, -1, GTK_ANCHOR_NORTH_WEST
,
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
);
266 if (ret
) *ret
= port
;
269 int main( int argc
, char ** argv
)
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
);
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");
306 char * wire_data
= "M50,100 C100,100 50,200 100,200";
307 wire
= goo_canvas_path_new (root_item
,
310 "stroke-color", "black", NULL
);
311 wire
= goo_canvas_path_new (root_item
,
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
);
326 /* Helpers (nothing yet) */
330 static gboolean
on_root_buttonPress_event (GooCanvasItem
* item
, GooCanvasItem
* target
, GdkEventButton
* event
, gpointer data
)
335 static gboolean
on_root_buttonRelease_event (GooCanvasItem
* item
, GooCanvasItem
* target
, GdkEventButton
* event
, gpointer data
)
340 static gboolean
on_root_dragged (GooCanvasItem
* item
, GooCanvasItem
* target
, GdkEventMotion
* event
, gpointer data
)
345 /* Module callbacks */
347 static gboolean
on_module_enterNotify_event (GooCanvasItem
* item
, GooCanvasItem
* target
, GdkEventCrossing
* event
, gpointer data
)
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
);
357 static gboolean
on_module_leaveNotify_event (GooCanvasItem
* item
, GooCanvasItem
* target
, GdkEventCrossing
* event
, gpointer data
)
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
);
367 static gboolean
on_module_notify_width (GooCanvasItem
* item
, GParamSpec
* event
, gpointer data
)
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
);
376 static gboolean
on_module_notify_height (GooCanvasItem
* item
, GParamSpec
* event
, gpointer data
)
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
);
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
)
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
);
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;
423 g_signal_handler_disconnect (G_OBJECT (root_item
), DRAG_CB (data
));
429 gboolean
on_module_dragged (GooCanvasItem
* item
, GooCanvasItem
* target
, GdkEventMotion
* event
, gpointer data
)
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
);
440 static gboolean
on_module_text_notify (GooCanvasItem
* item
, GParamSpec
* event
, gpointer data
)
444 canvas_text_size (item
, &w
, &h
);
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
);
455 static gboolean
on_port_text_notify (GooCanvasItem
* item
, GParamSpec
* event
, gpointer data
)
459 canvas_text_size (item
, &w
, &h
);
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
);
471 gboolean
wire_entered (GooCanvasItem
*item
,
472 GooCanvasItem
*target
,
473 GdkEventCrossing
*event
,
476 g_object_set (item
, "stroke-color", "white", NULL
);
481 gboolean
wire_left (GooCanvasItem
*item
,
482 GooCanvasItem
*target
,
483 GdkEventCrossing
*event
,
486 g_object_set (item
, "stroke-color", "violet", NULL
);