diff options
-rw-r--r-- | ganv/Canvas.hpp | 2 | ||||
-rw-r--r-- | ganv/canvas-base.h | 94 | ||||
-rw-r--r-- | ganv/canvas.h | 2 | ||||
-rw-r--r-- | ganv/ganv.h | 1 | ||||
-rw-r--r-- | ganv/group.h | 51 | ||||
-rw-r--r-- | ganv/node.h | 1 | ||||
-rw-r--r-- | src/Canvas.cpp | 6 | ||||
-rw-r--r-- | src/canvas-base.c | 637 | ||||
-rw-r--r-- | src/ganv-private.h | 7 | ||||
-rw-r--r-- | src/group.c | 490 | ||||
-rw-r--r-- | wscript | 1 |
11 files changed, 615 insertions, 677 deletions
diff --git a/ganv/Canvas.hpp b/ganv/Canvas.hpp index 0b5a95c..e017d70 100644 --- a/ganv/Canvas.hpp +++ b/ganv/Canvas.hpp @@ -135,7 +135,7 @@ public: GQuark wrapper_key(); - GanvGroup* root(); + GanvItem* root(); GdkCursor* move_cursor(); diff --git a/ganv/canvas-base.h b/ganv/canvas-base.h index a6abe5f..8f6d39c 100644 --- a/ganv/canvas-base.h +++ b/ganv/canvas-base.h @@ -36,8 +36,6 @@ typedef struct _GanvCanvasBase GanvCanvasBase; typedef struct _GanvCanvasBaseClass GanvCanvasBaseClass; typedef struct _GanvItem GanvItem; typedef struct _GanvItemClass GanvItemClass; -typedef struct _GanvGroup GanvGroup; -typedef struct _GanvGroupClass GanvGroupClass; /* GanvItem - base item class for canvas items @@ -61,17 +59,14 @@ enum { GANV_ITEM_ALWAYS_REDRAW = 1 << 3, GANV_ITEM_VISIBLE = 1 << 4, GANV_ITEM_NEED_UPDATE = 1 << 5, - GANV_ITEM_NEED_CLIP = 1 << 6, - GANV_ITEM_NEED_VIS = 1 << 7, + GANV_ITEM_NEED_VIS = 1 << 6, }; /* Update flags for items */ enum { GANV_CANVAS_BASE_UPDATE_REQUESTED = 1 << 0, GANV_CANVAS_BASE_UPDATE_AFFINE = 1 << 1, - GANV_CANVAS_BASE_UPDATE_CLIP = 1 << 2, - GANV_CANVAS_BASE_UPDATE_VISIBILITY = 1 << 3, - GANV_CANVAS_BASE_UPDATE_IS_VISIBLE = 1 << 4 /* Deprecated. FIXME: remove this */ + GANV_CANVAS_BASE_UPDATE_VISIBILITY = 1 << 2, }; #define GANV_TYPE_ITEM (ganv_item_get_type()) @@ -87,7 +82,7 @@ struct _GanvItem { /* Parent canvas for this item */ GanvCanvasBase* canvas; - /* Parent canvas group for this item (a GanvGroup) */ + /* Parent for this item */ GanvItem* parent; /* Position in parent-relative coordinates. */ @@ -100,6 +95,12 @@ struct _GanvItem { struct _GanvItemClass { GtkObjectClass parent_class; + /* Add a child to this item (optional) */ + void (* add)(GanvItem* item, GanvItem* child); + + /* Remove a child from this item (optional) */ + void (* remove)(GanvItem* item, GanvItem* child); + /* Tell the item to update itself. The flags are from the update flags * defined above. The item should update its internal state from its * queued state, and recompute and request its repaint area. The update @@ -127,10 +128,10 @@ struct _GanvItemClass { int x, int y, int width, int height); /* Calculate the distance from an item to the specified point. It also - * returns a canvas item which is the item itself in the case of the - * object being an actual leaf item, or a child in case of the object - * being a canvas group. (cx, cy) are the canvas pixel coordinates that - * correspond to the item-relative coordinates (x, y). + * returns a canvas item which is actual item the point is within, which + * may not be equal to @item if @item has children. (cx, cy) are the + * canvas pixel coordinates that correspond to the item-relative + * coordinates (x, y). */ double (* point)(GanvItem* item, double x, double y, int cx, int cy, GanvItem** actual_item); @@ -152,9 +153,8 @@ struct _GanvItemClass { GType ganv_item_get_type(void) G_GNUC_CONST; -/* Create a canvas item using the standard Gtk argument mechanism. The item is - * automatically inserted at the top of the specified canvas group. The last - * argument must be a NULL pointer. +/* Create a canvas item using the standard Gtk argument mechanism. The item + * automatically added to @parent. The last argument must be a NULL pointer. */ GanvItem* ganv_item_new(GanvItem* parent, GType type, const gchar* first_arg_name, ...); @@ -175,10 +175,10 @@ void ganv_item_set_valist(GanvItem* item, /* Move an item by the specified amount */ void ganv_item_move(GanvItem* item, double dx, double dy); -/* Raise an item to the top of its parent group's z-order. */ +/* Raise an item to the top of its parent's z-order. */ void ganv_item_raise_to_top(GanvItem* item); -/* Lower an item to the bottom of its parent group's z-order */ +/* Lower an item to the bottom of its parent's z-order */ void ganv_item_lower_to_bottom(GanvItem* item); /* Show an item (make it visible). If the item is already shown, it has no @@ -220,17 +220,6 @@ void ganv_item_i2w_affine(GanvItem* item, cairo_matrix_t* matrix); */ void ganv_item_i2c_affine(GanvItem* item, cairo_matrix_t* matrix); -/* Remove the item from its parent group and make the new group its parent. The - * item will be put on top of all the items in the new group. The item's - * coordinates relative to its new parent to *not* change -- this means that the - * item could potentially move on the screen. - * - * The item and the group must be in the same canvas. An item cannot be - * reparented to a group that is the item itself or that is an inferior of the - * item. - */ -void ganv_item_reparent(GanvItem* item, GanvGroup* new_group); - /* Used to send all of the keystroke events to a specific item as well as * GDK_FOCUS_CHANGE events. */ @@ -249,51 +238,6 @@ void ganv_item_get_bounds(GanvItem* item, void ganv_item_request_update(GanvItem* item); -/* GanvGroup - a group of canvas items - * - * A group is a node in the hierarchical tree of groups/items inside a canvas. - * Groups serve to give a logical structure to the items. - * - * Consider a circuit editor application that uses the canvas for its schematic - * display. Hierarchically, there would be canvas groups that contain all the - * components needed for an "adder", for example -- this includes some logic - * gates as well as wires. You can move stuff around in a convenient way by - * doing a ganv_item_move() of the hierarchical groups -- to move an - * adder, simply move the group that represents the adder. - * - * The following arguments are available: - * - * name type read/write description - * -------------------------------------------------------------------------------- - * x double RW X coordinate of group's origin - * y double RW Y coordinate of group's origin - */ - - -#define GANV_TYPE_GROUP (ganv_group_get_type()) -#define GANV_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GANV_TYPE_GROUP, GanvGroup)) -#define GANV_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GANV_TYPE_GROUP, GanvGroupClass)) -#define GANV_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GANV_TYPE_GROUP)) -#define GANV_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GANV_TYPE_GROUP)) -#define GANV_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GANV_TYPE_GROUP, GanvGroupClass)) - - -struct _GanvGroup { - GanvItem item; - - /* Children of the group */ - GList* item_list; - GList* item_list_end; -}; - -struct _GanvGroupClass { - GanvItemClass parent_class; -}; - - -GType ganv_group_get_type(void) G_GNUC_CONST; - - /*** GanvCanvasBase ***/ @@ -415,8 +359,8 @@ GType ganv_canvas_base_get_type(void) G_GNUC_CONST; */ GtkWidget* ganv_canvas_base_new(void); -/* Returns the root canvas item group of the canvas */ -GanvGroup* ganv_canvas_base_root(GanvCanvasBase* canvas); +/* Returns the root item of the canvas */ +GanvItem* ganv_canvas_base_root(GanvCanvasBase* canvas); /* Sets the limits of the scrolling region, in world coordinates */ void ganv_canvas_base_set_scroll_region(GanvCanvasBase* canvas, diff --git a/ganv/canvas.h b/ganv/canvas.h index 08ec2f9..ddd420b 100644 --- a/ganv/canvas.h +++ b/ganv/canvas.h @@ -70,7 +70,7 @@ ganv_canvas_resize(GanvCanvas* canvas, double width, double height); * ganv_canvas_get_root: * Return value: (transfer none): The root group of @canvas. */ -GanvGroup* +GanvItem* ganv_canvas_get_root(const GanvCanvas* canvas); void diff --git a/ganv/ganv.h b/ganv/ganv.h index 3b84935..5becce7 100644 --- a/ganv/ganv.h +++ b/ganv/ganv.h @@ -21,6 +21,7 @@ #include "ganv/circle.h" #include "ganv/edge.h" #include "ganv/ganv.h" +#include "ganv/group.h" #include "ganv/module.h" #include "ganv/node.h" #include "ganv/port.h" diff --git a/ganv/group.h b/ganv/group.h new file mode 100644 index 0000000..273bdea --- /dev/null +++ b/ganv/group.h @@ -0,0 +1,51 @@ +/* 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 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/>. + */ + +#ifndef GANV_GROUP_H +#define GANV_GROUP_H + +#include "ganv/canvas-base.h" + +/* Based on GnomeCanvasGroup, by Federico Mena <federico@nuclecu.unam.mx> + * and Raph Levien <raph@gimp.org> + * Copyright 1997-2000 Free Software Foundation + */ + +#define GANV_TYPE_GROUP (ganv_group_get_type()) +#define GANV_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GANV_TYPE_GROUP, GanvGroup)) +#define GANV_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GANV_TYPE_GROUP, GanvGroupClass)) +#define GANV_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GANV_TYPE_GROUP)) +#define GANV_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GANV_TYPE_GROUP)) +#define GANV_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GANV_TYPE_GROUP, GanvGroupClass)) + +typedef struct _GanvGroup GanvGroup; +typedef struct _GanvGroupClass GanvGroupClass; + +struct _GanvGroup { + GanvItem item; + + /* Children of the group */ + GList* item_list; + GList* item_list_end; +}; + +struct _GanvGroupClass { + GanvItemClass parent_class; +}; + + +GType ganv_group_get_type(void) G_GNUC_CONST; + +#endif /* GANV_GROUP_H */ diff --git a/ganv/node.h b/ganv/node.h index 7c14b53..c0e2302 100644 --- a/ganv/node.h +++ b/ganv/node.h @@ -19,6 +19,7 @@ #include "ganv/canvas-base.h" #include "ganv/types.h" #include "ganv/text.h" +#include "ganv/group.h" G_BEGIN_DECLS diff --git a/src/Canvas.cpp b/src/Canvas.cpp index db02ffe..4f081fa 100644 --- a/src/Canvas.cpp +++ b/src/Canvas.cpp @@ -166,7 +166,7 @@ struct GanvCanvasImpl { return ((GanvCanvasImpl*)impl)->on_event(ev); } - GanvGroup* root() { + GanvItem* root() { return ganv_canvas_base_root(GANV_CANVAS_BASE(_gcanvas)); } @@ -1670,7 +1670,7 @@ Canvas::for_each_selected_edge(EdgePtrFunction f, void* data) } } -GanvGroup* +GanvItem* Canvas::root() { return ganv_canvas_base_root(GANV_CANVAS_BASE(impl()->_gcanvas)); @@ -1900,7 +1900,7 @@ ganv_canvas_resize(GanvCanvas* canvas, double width, double height) canvas->impl->resize(width, height); } -GanvGroup* +GanvItem* ganv_canvas_get_root(const GanvCanvas* canvas) { return ganv_canvas_base_root(GANV_CANVAS_BASE(canvas->impl->_gcanvas)); diff --git a/src/canvas-base.c b/src/canvas-base.c index ef523f0..04cdf55 100644 --- a/src/canvas-base.c +++ b/src/canvas-base.c @@ -27,6 +27,7 @@ #include <cairo.h> #include "ganv/canvas-base.h" +#include "ganv/group.h" #include "./gettext.h" #include "./ganv-marshal.h" @@ -35,21 +36,12 @@ #define CANVAS_IDLE_PRIORITY (GDK_PRIORITY_REDRAW - 5) static void ganv_canvas_base_request_update(GanvCanvasBase* canvas); -static void group_add(GanvGroup* group, - GanvItem* item); -static void group_remove(GanvGroup* group, - GanvItem* item); static void add_idle(GanvCanvasBase* canvas); -/*** GanvItem ***/ - /* Some convenience stuff */ #define GCI_UPDATE_MASK (GANV_CANVAS_BASE_UPDATE_REQUESTED | GANV_CANVAS_BASE_UPDATE_AFFINE \ - | GANV_CANVAS_BASE_UPDATE_CLIP | GANV_CANVAS_BASE_UPDATE_VISIBILITY) -#define GCI_EPSILON 1e-18 -#define GCI_PRINT_MATRIX(s, a) g_print("%s %g %g %g %g %g %g\n", s, (a)[0], (a)[1], (a)[2], (a)[3], \ - (a)[4], (a)[5]) + | GANV_CANVAS_BASE_UPDATE_VISIBILITY) enum { ITEM_PROP_0, @@ -120,10 +112,16 @@ ganv_item_new(GanvItem* parent, GType type, const gchar* first_arg_name, ...) static void item_post_create_setup(GanvItem* item) { - group_add(GANV_GROUP(item->parent), item); - - ganv_canvas_base_request_redraw(item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1); - item->canvas->need_repick = TRUE; + GanvItemClass* parent_class = GANV_ITEM_GET_CLASS(item->parent); + if (parent_class->add) { + parent_class->add(item->parent, item); + ganv_canvas_base_request_redraw(item->canvas, + item->x1, item->y1, + item->x2 + 1, item->y2 + 1); + item->canvas->need_repick = TRUE; + } else { + g_error("item added to non-parent item\n"); + } } /* Set_property handler for canvas items */ @@ -255,7 +253,7 @@ ganv_item_dispose(GObject* object) } if (item->parent) { - group_remove(GANV_GROUP(item->parent), item); + GANV_ITEM_GET_CLASS(item->parent)->remove(item->parent, item); } G_OBJECT_CLASS(item_parent_class)->dispose(object); @@ -299,12 +297,22 @@ static void ganv_item_update(GanvItem* item, int flags) { GTK_OBJECT_UNSET_FLAGS(item, GANV_ITEM_NEED_UPDATE); - GTK_OBJECT_UNSET_FLAGS(item, GANV_ITEM_NEED_CLIP); GTK_OBJECT_UNSET_FLAGS(item, GANV_ITEM_NEED_VIS); } -/* - * This routine invokes the update method of the item +/* Point handler for canvas items */ +static double +ganv_item_point(GanvItem* item, + double x, double y, + int cx, int cy, + GanvItem** actual_item) +{ + *actual_item = NULL; + return G_MAXDOUBLE; +} + +/** + * Invoke the update method of the item * Please notice, that we take parent to canvas pixel matrix as argument * unlike virtual method ::update, whose argument is item 2 canvas pixel * matrix @@ -313,16 +321,11 @@ ganv_item_update(GanvItem* item, int flags) * General naming rule is FROM2TO, where FROM and TO are abbreviations * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel * I hope that this helps to keep track of what really happens - * */ - -static void +void ganv_item_invoke_update(GanvItem* item, int flags) { int child_flags = flags; - if (!(item->object.flags & GANV_ITEM_VISIBLE)) { - child_flags &= ~GANV_CANVAS_BASE_UPDATE_IS_VISIBLE; - } /* apply object flags to child flags */ @@ -332,10 +335,6 @@ ganv_item_invoke_update(GanvItem* item, int flags) child_flags |= GANV_CANVAS_BASE_UPDATE_REQUESTED; } - if (item->object.flags & GANV_ITEM_NEED_CLIP) { - child_flags |= GANV_CANVAS_BASE_UPDATE_CLIP; - } - if (item->object.flags & GANV_ITEM_NEED_VIS) { child_flags |= GANV_CANVAS_BASE_UPDATE_VISIBILITY; } @@ -348,25 +347,6 @@ ganv_item_invoke_update(GanvItem* item, int flags) } /** - * Invoke the point method of the item. - * The arguments x, y should be in the parent item local coordinates. - */ -static double -ganv_item_invoke_point(GanvItem* item, double x, double y, int cx, int cy, - GanvItem** actual_item) -{ - // Transform x and y to item local coordinates - x -= item->x; - y -= item->y; - - if (GANV_ITEM_GET_CLASS(item)->point) { - return GANV_ITEM_GET_CLASS(item)->point(item, x, y, cx, cy, actual_item); - } - - return G_MAXDOUBLE; -} - -/** * ganv_item_set: * @item: A canvas item. * @first_arg_name: The list of object argument name/value pairs used to configure the item. @@ -751,47 +731,6 @@ is_descendant(GanvItem* item, GanvItem* parent) } /** - * ganv_item_reparent: - * @item: A canvas item. - * @new_group: A canvas group. - * - * Changes the parent of the specified item to be the new group. The item keeps - * its group-relative coordinates as for its old parent, so the item may change - * its absolute position within the canvas. - **/ -void -ganv_item_reparent(GanvItem* item, GanvGroup* new_group) -{ - g_return_if_fail(GANV_IS_ITEM(item)); - g_return_if_fail(GANV_IS_GROUP(new_group)); - - /* Both items need to be in the same canvas */ - g_return_if_fail(item->canvas == GANV_ITEM(new_group)->canvas); - - /* The group cannot be an inferior of the item or be the item itself -- - * this also takes care of the case where the item is the root item of - * the canvas. */ - g_return_if_fail(!is_descendant(GANV_ITEM(new_group), item)); - - /* Everything is ok, now actually reparent the item */ - - g_object_ref(G_OBJECT(item)); /* protect it from the unref in group_remove */ - - redraw_if_visible(item); - - group_remove(GANV_GROUP(item->parent), item); - item->parent = GANV_ITEM(new_group); - group_add(new_group, item); - - /* Redraw and repick */ - - redraw_if_visible(item); - item->canvas->need_repick = TRUE; - - g_object_unref(G_OBJECT(item)); -} - -/** * ganv_item_grab_focus: * @item: A canvas item. * @@ -909,510 +848,6 @@ ganv_item_request_update(GanvItem* item) } } -/*** GanvGroup ***/ - - -enum { - GROUP_PROP_0, - GROUP_PROP_X, - GROUP_PROP_Y -}; - - -static void ganv_group_class_init(GanvGroupClass* class); -static void ganv_group_init(GanvGroup* group); -static void ganv_group_set_property(GObject* object, - guint param_id, - const GValue* value, - GParamSpec* pspec); -static void ganv_group_get_property(GObject* object, - guint param_id, - GValue* value, - GParamSpec* pspec); - -static void ganv_group_destroy(GtkObject* object); - -static void ganv_group_update(GanvItem* item, int flags); -static void ganv_group_realize(GanvItem* item); -static void ganv_group_unrealize(GanvItem* item); -static void ganv_group_map(GanvItem* item); -static void ganv_group_unmap(GanvItem* item); -static void ganv_group_draw(GanvItem* item, cairo_t* cr, - int x, int y, int width, int height); -static double ganv_group_point(GanvItem* item, double x, double y, - int cx, int cy, - GanvItem** actual_item); -static void ganv_group_bounds(GanvItem* item, double* x1, double* y1, - double* x2, double* y2); - - -static GanvItemClass* group_parent_class; - -G_DEFINE_TYPE(GanvGroup, ganv_group, GANV_TYPE_ITEM) - -/* Class initialization function for GanvGroupClass */ -static void -ganv_group_class_init(GanvGroupClass* class) -{ - GObjectClass* gobject_class; - GtkObjectClass* object_class; - GanvItemClass* item_class; - - gobject_class = (GObjectClass*)class; - object_class = (GtkObjectClass*)class; - item_class = (GanvItemClass*)class; - - group_parent_class = g_type_class_peek_parent(class); - - gobject_class->set_property = ganv_group_set_property; - gobject_class->get_property = ganv_group_get_property; - - g_object_class_install_property - (gobject_class, GROUP_PROP_X, - g_param_spec_double("x", - _("X"), - _("X"), - -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, - (G_PARAM_READABLE | G_PARAM_WRITABLE))); - g_object_class_install_property - (gobject_class, GROUP_PROP_Y, - g_param_spec_double("y", - _("Y"), - _("Y"), - -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, - (G_PARAM_READABLE | G_PARAM_WRITABLE))); - - object_class->destroy = ganv_group_destroy; - - item_class->update = ganv_group_update; - item_class->realize = ganv_group_realize; - item_class->unrealize = ganv_group_unrealize; - item_class->map = ganv_group_map; - item_class->unmap = ganv_group_unmap; - item_class->draw = ganv_group_draw; - item_class->point = ganv_group_point; - item_class->bounds = ganv_group_bounds; -} - -/* Object initialization function for GanvGroup */ -static void -ganv_group_init(GanvGroup* group) -{ -#if 0 - group->xpos = 0.0; - group->ypos = 0.0; -#endif -} - -/* Set_property handler for canvas groups */ -static void -ganv_group_set_property(GObject* gobject, guint param_id, - const GValue* value, GParamSpec* pspec) -{ - g_return_if_fail(GANV_IS_GROUP(gobject)); - - GanvItem* item = GANV_ITEM(gobject); - - switch (param_id) { - case GROUP_PROP_X: - item->x = g_value_get_double(value); - ganv_item_request_update(item); - break; - - case GROUP_PROP_Y: - item->y = g_value_get_double(value); - ganv_item_request_update(item); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, param_id, pspec); - break; - } -} - -/* Get_property handler for canvas groups */ -static void -ganv_group_get_property(GObject* gobject, guint param_id, - GValue* value, GParamSpec* pspec) -{ - GanvItem* item; - - g_return_if_fail(GANV_IS_GROUP(gobject)); - - item = GANV_ITEM(gobject); - - switch (param_id) { - case GROUP_PROP_X: - g_value_set_double(value, item->x); - break; - - case GROUP_PROP_Y: - g_value_set_double(value, item->y); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, param_id, pspec); - break; - } -} - -/* Destroy handler for canvas groups */ -static void -ganv_group_destroy(GtkObject* object) -{ - GanvGroup* group; - - g_return_if_fail(GANV_IS_GROUP(object)); - - group = GANV_GROUP(object); - - while (group->item_list) { - // child is unref'ed by the child's group_remove(). - gtk_object_destroy(GTK_OBJECT(group->item_list->data)); - } - if (GTK_OBJECT_CLASS(group_parent_class)->destroy) { - (*GTK_OBJECT_CLASS(group_parent_class)->destroy)(object); - } -} - -/* Update handler for canvas groups */ -static void -ganv_group_update(GanvItem* item, int flags) -{ - GanvGroup* group; - GList* list; - GanvItem* i; - - group = GANV_GROUP(item); - - (*group_parent_class->update)(item, flags); - - double min_x = 0.0; - double min_y = 0.0; - double max_x = 0.0; - double max_y = 0.0; - - for (list = group->item_list; list; list = list->next) { - i = list->data; - - ganv_item_invoke_update(i, flags); - - min_x = fmin(min_x, fmin(i->x1, i->x2)); - min_y = fmin(min_y, fmin(i->y1, i->y2)); - max_x = fmax(max_x, fmax(i->x1, i->x2)); - max_y = fmax(max_y, fmax(i->y2, i->y2)); - } - item->x1 = min_x; - item->y1 = min_y; - item->x2 = max_x; - item->y2 = max_y; -} - -/* Realize handler for canvas groups */ -static void -ganv_group_realize(GanvItem* item) -{ - GanvGroup* group; - GList* list; - GanvItem* i; - - group = GANV_GROUP(item); - - for (list = group->item_list; list; list = list->next) { - i = list->data; - - if (!(i->object.flags & GANV_ITEM_REALIZED)) { - (*GANV_ITEM_GET_CLASS(i)->realize)(i); - } - } - - (*group_parent_class->realize)(item); -} - -/* Unrealize handler for canvas groups */ -static void -ganv_group_unrealize(GanvItem* item) -{ - GanvGroup* group; - GList* list; - GanvItem* i; - - group = GANV_GROUP(item); - - for (list = group->item_list; list; list = list->next) { - i = list->data; - - if (i->object.flags & GANV_ITEM_REALIZED) { - (*GANV_ITEM_GET_CLASS(i)->unrealize)(i); - } - } - - (*group_parent_class->unrealize)(item); -} - -/* Map handler for canvas groups */ -static void -ganv_group_map(GanvItem* item) -{ - GanvGroup* group; - GList* list; - GanvItem* i; - - group = GANV_GROUP(item); - - for (list = group->item_list; list; list = list->next) { - i = list->data; - - if (!(i->object.flags & GANV_ITEM_MAPPED)) { - (*GANV_ITEM_GET_CLASS(i)->map)(i); - } - } - - (*group_parent_class->map)(item); -} - -/* Unmap handler for canvas groups */ -static void -ganv_group_unmap(GanvItem* item) -{ - GanvGroup* group; - GList* list; - GanvItem* i; - - group = GANV_GROUP(item); - - for (list = group->item_list; list; list = list->next) { - i = list->data; - - if (i->object.flags & GANV_ITEM_MAPPED) { - (*GANV_ITEM_GET_CLASS(i)->unmap)(i); - } - } - - (*group_parent_class->unmap)(item); -} - -/* Draw handler for canvas groups */ -static void -ganv_group_draw(GanvItem* item, cairo_t* cr, - int x, int y, int width, int height) -{ - GanvGroup* group; - GList* list; - GanvItem* child = NULL; - - group = GANV_GROUP(item); - - for (list = group->item_list; list; list = list->next) { - child = list->data; - - if (((child->object.flags & GANV_ITEM_VISIBLE) - && ((child->x1 < (x + width)) - && (child->y1 < (y + height)) - && (child->x2 > x) - && (child->y2 > y))) - || ((GTK_OBJECT_FLAGS(child) & GANV_ITEM_ALWAYS_REDRAW) - && (child->x1 < child->canvas->redraw_x2) - && (child->y1 < child->canvas->redraw_y2) - && (child->x2 > child->canvas->redraw_x1) - && (child->y2 > child->canvas->redraw_y2))) { - if (GANV_ITEM_GET_CLASS(child)->draw) { - (*GANV_ITEM_GET_CLASS(child)->draw)( - child, cr, x, y, width, height); - } - } - } -} - -/* Point handler for canvas groups */ -static double -ganv_group_point(GanvItem* item, double x, double y, int cx, int cy, - GanvItem** actual_item) -{ - GanvGroup* group; - GList* list; - GanvItem* child, * point_item; - int x1, y1, x2, y2; - double gx, gy; - double dist, best; - int has_point; - - group = GANV_GROUP(item); - - x1 = cx - item->canvas->close_enough; - y1 = cy - item->canvas->close_enough; - x2 = cx + item->canvas->close_enough; - y2 = cy + item->canvas->close_enough; - - best = 0.0; - *actual_item = NULL; - - gx = x; - gy = y; - - dist = 0.0; /* keep gcc happy */ - - for (list = group->item_list; list; list = list->next) { - child = list->data; - - if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1)) { - continue; - } - - point_item = NULL; /* cater for incomplete item implementations */ - - if ((child->object.flags & GANV_ITEM_VISIBLE) - && GANV_ITEM_GET_CLASS(child)->point) { - dist = ganv_item_invoke_point(child, gx, gy, cx, cy, &point_item); - has_point = TRUE; - } else { - has_point = FALSE; - } - - if (has_point - && point_item - && ((int)(dist * item->canvas->pixels_per_unit + 0.5) - <= item->canvas->close_enough)) { - best = dist; - *actual_item = point_item; - } - } - - return best; -} - -/* Bounds handler for canvas groups */ -static void -ganv_group_bounds(GanvItem* item, double* x1, double* y1, double* x2, double* y2) -{ - GanvGroup* group; - GanvItem* child; - GList* list; - double tx1, ty1, tx2, ty2; - double minx, miny, maxx, maxy; - int set; - - group = GANV_GROUP(item); - - /* Get the bounds of the first visible item */ - - child = NULL; /* Unnecessary but eliminates a warning. */ - - set = FALSE; - - for (list = group->item_list; list; list = list->next) { - child = list->data; - - if (child->object.flags & GANV_ITEM_VISIBLE) { - set = TRUE; - ganv_item_get_bounds(child, &minx, &miny, &maxx, &maxy); - break; - } - } - - /* If there were no visible items, return an empty bounding box */ - - if (!set) { - *x1 = *y1 = *x2 = *y2 = 0.0; - return; - } - - /* Now we can grow the bounds using the rest of the items */ - - list = list->next; - - for (; list; list = list->next) { - child = list->data; - - if (!(child->object.flags & GANV_ITEM_VISIBLE)) { - continue; - } - - ganv_item_get_bounds(child, &tx1, &ty1, &tx2, &ty2); - - if (tx1 < minx) { - minx = tx1; - } - - if (ty1 < miny) { - miny = ty1; - } - - if (tx2 > maxx) { - maxx = tx2; - } - - if (ty2 > maxy) { - maxy = ty2; - } - } - - *x1 = minx; - *y1 = miny; - *x2 = maxx; - *y2 = maxy; -} - -/* Adds an item to a group */ -static void -group_add(GanvGroup* group, GanvItem* item) -{ - g_object_ref_sink(G_OBJECT(item)); - - if (!group->item_list) { - group->item_list = g_list_append(group->item_list, item); - group->item_list_end = group->item_list; - } else { - group->item_list_end = g_list_append(group->item_list_end, item)->next; - } - - if (group->item.object.flags & GANV_ITEM_REALIZED) { - (*GANV_ITEM_GET_CLASS(item)->realize)(item); - } - - if (group->item.object.flags & GANV_ITEM_MAPPED) { - (*GANV_ITEM_GET_CLASS(item)->map)(item); - } - - g_object_notify(G_OBJECT(item), "parent"); -} - -/* Removes an item from a group */ -static void -group_remove(GanvGroup* group, GanvItem* item) -{ - GList* children; - - g_return_if_fail(GANV_IS_GROUP(group)); - g_return_if_fail(GANV_IS_ITEM(item)); - - for (children = group->item_list; children; children = children->next) { - if (children->data == item) { - if (item->object.flags & GANV_ITEM_MAPPED) { - (*GANV_ITEM_GET_CLASS(item)->unmap)(item); - } - - if (item->object.flags & GANV_ITEM_REALIZED) { - (*GANV_ITEM_GET_CLASS(item)->unrealize)(item); - } - - /* Unparent the child */ - - item->parent = NULL; - g_object_unref(G_OBJECT(item)); - - /* Remove it from the list */ - - if (children == group->item_list_end) { - group->item_list_end = children->prev; - } - - group->item_list = g_list_remove_link(group->item_list, children); - g_list_free(children); - break; - } - } -} /*** GanvCanvasBase ***/ @@ -2149,8 +1584,11 @@ pick_current_item(GanvCanvasBase* canvas, GdkEvent* event) /* find the closest item */ if (canvas->root->object.flags & GANV_ITEM_VISIBLE) { - ganv_item_invoke_point(canvas->root, x, y, cx, cy, - &canvas->new_current_item); + GANV_ITEM_GET_CLASS(canvas->root)->point( + canvas->root, + x - canvas->root->x, y - canvas->root->y, + cx, cy, + &canvas->new_current_item); } else { canvas->new_current_item = NULL; } @@ -2640,12 +2078,12 @@ add_idle(GanvCanvasBase* canvas) * * Return value: The root group of the specified canvas. **/ -GanvGroup* +GanvItem* ganv_canvas_base_root(GanvCanvasBase* canvas) { g_return_val_if_fail(GANV_IS_CANVAS_BASE(canvas), NULL); - return GANV_GROUP(canvas->root); + return canvas->root; } /** @@ -2921,7 +2359,11 @@ ganv_canvas_base_get_item_at(GanvCanvasBase* canvas, double x, double y) ganv_canvas_base_w2c(canvas, x, y, &cx, &cy); - dist = ganv_item_invoke_point(canvas->root, x, y, cx, cy, &item); + dist = GANV_ITEM_GET_CLASS(canvas->root)->point( + canvas->root, + x - canvas->root->x, y - canvas->root->y, + cx, cy, + &item); if ((int)(dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough) { return item; } else { @@ -3227,4 +2669,5 @@ ganv_item_class_init(GanvItemClass* class) class->map = ganv_item_map; class->unmap = ganv_item_unmap; class->update = ganv_item_update; + class->point = ganv_item_point; } diff --git a/src/ganv-private.h b/src/ganv-private.h index 8e9af59..1c25716 100644 --- a/src/ganv-private.h +++ b/src/ganv-private.h @@ -23,6 +23,7 @@ extern "C" { #include <cairo.h> #include "ganv/types.h" +#include "ganv/text.h" extern guint signal_moved; @@ -198,6 +199,12 @@ ganv_canvas_port_event(GanvCanvas* canvas, GanvPort* port, GdkEvent* event); +void +ganv_item_invoke_update(GanvItem* item, int flags); + +void +ganv_canvas_base_request_update(GanvCanvasBase* canvas); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/group.c b/src/group.c new file mode 100644 index 0000000..1ebb42c --- /dev/null +++ b/src/group.c @@ -0,0 +1,490 @@ +/* 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 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/>. + */ + +/* Based on GnomeCanvasGroup, by Federico Mena <federico@nuclecu.unam.mx> + * and Raph Levien <raph@gimp.org> + * Copyright 1997-2000 Free Software Foundation + */ + +#include <math.h> + +#include "ganv/canvas-base.h" +#include "ganv/group.h" + +#include "./gettext.h" +#include "./ganv-private.h" + +enum { + GROUP_PROP_0, + GROUP_PROP_X, + GROUP_PROP_Y +}; + +G_DEFINE_TYPE(GanvGroup, ganv_group, GANV_TYPE_ITEM) + +static GanvItemClass* group_parent_class; + +static void +ganv_group_init(GanvGroup* group) +{ +} + +static void +ganv_group_set_property(GObject* gobject, guint param_id, + const GValue* value, GParamSpec* pspec) +{ + g_return_if_fail(GANV_IS_GROUP(gobject)); + + GanvItem* item = GANV_ITEM(gobject); + + switch (param_id) { + case GROUP_PROP_X: + item->x = g_value_get_double(value); + ganv_item_request_update(item); + break; + + case GROUP_PROP_Y: + item->y = g_value_get_double(value); + ganv_item_request_update(item); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, param_id, pspec); + break; + } +} + +static void +ganv_group_get_property(GObject* gobject, guint param_id, + GValue* value, GParamSpec* pspec) +{ + GanvItem* item; + + g_return_if_fail(GANV_IS_GROUP(gobject)); + + item = GANV_ITEM(gobject); + + switch (param_id) { + case GROUP_PROP_X: + g_value_set_double(value, item->x); + break; + + case GROUP_PROP_Y: + g_value_set_double(value, item->y); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, param_id, pspec); + break; + } +} + +static void +ganv_group_destroy(GtkObject* object) +{ + GanvGroup* group; + + g_return_if_fail(GANV_IS_GROUP(object)); + + group = GANV_GROUP(object); + + while (group->item_list) { + // child is unref'ed by the child's group_remove(). + gtk_object_destroy(GTK_OBJECT(group->item_list->data)); + } + if (GTK_OBJECT_CLASS(group_parent_class)->destroy) { + (*GTK_OBJECT_CLASS(group_parent_class)->destroy)(object); + } +} + +static void +ganv_group_update(GanvItem* item, int flags) +{ + GanvGroup* group; + GList* list; + GanvItem* i; + + group = GANV_GROUP(item); + + (*group_parent_class->update)(item, flags); + + double min_x = 0.0; + double min_y = 0.0; + double max_x = 0.0; + double max_y = 0.0; + + for (list = group->item_list; list; list = list->next) { + i = list->data; + + ganv_item_invoke_update(i, flags); + + min_x = fmin(min_x, fmin(i->x1, i->x2)); + min_y = fmin(min_y, fmin(i->y1, i->y2)); + max_x = fmax(max_x, fmax(i->x1, i->x2)); + max_y = fmax(max_y, fmax(i->y2, i->y2)); + } + item->x1 = min_x; + item->y1 = min_y; + item->x2 = max_x; + item->y2 = max_y; +} + +static void +ganv_group_realize(GanvItem* item) +{ + GanvGroup* group; + GList* list; + GanvItem* i; + + group = GANV_GROUP(item); + + for (list = group->item_list; list; list = list->next) { + i = list->data; + + if (!(i->object.flags & GANV_ITEM_REALIZED)) { + (*GANV_ITEM_GET_CLASS(i)->realize)(i); + } + } + + (*group_parent_class->realize)(item); +} + +static void +ganv_group_unrealize(GanvItem* item) +{ + GanvGroup* group; + GList* list; + GanvItem* i; + + group = GANV_GROUP(item); + + for (list = group->item_list; list; list = list->next) { + i = list->data; + + if (i->object.flags & GANV_ITEM_REALIZED) { + (*GANV_ITEM_GET_CLASS(i)->unrealize)(i); + } + } + + (*group_parent_class->unrealize)(item); +} + +static void +ganv_group_map(GanvItem* item) +{ + GanvGroup* group; + GList* list; + GanvItem* i; + + group = GANV_GROUP(item); + + for (list = group->item_list; list; list = list->next) { + i = list->data; + + if (!(i->object.flags & GANV_ITEM_MAPPED)) { + (*GANV_ITEM_GET_CLASS(i)->map)(i); + } + } + + (*group_parent_class->map)(item); +} + +static void +ganv_group_unmap(GanvItem* item) +{ + GanvGroup* group; + GList* list; + GanvItem* i; + + group = GANV_GROUP(item); + + for (list = group->item_list; list; list = list->next) { + i = list->data; + + if (i->object.flags & GANV_ITEM_MAPPED) { + (*GANV_ITEM_GET_CLASS(i)->unmap)(i); + } + } + + (*group_parent_class->unmap)(item); +} + +static void +ganv_group_draw(GanvItem* item, cairo_t* cr, + int x, int y, int width, int height) +{ + GanvGroup* group; + GList* list; + GanvItem* child = NULL; + + group = GANV_GROUP(item); + + for (list = group->item_list; list; list = list->next) { + child = list->data; + + if (((child->object.flags & GANV_ITEM_VISIBLE) + && ((child->x1 < (x + width)) + && (child->y1 < (y + height)) + && (child->x2 > x) + && (child->y2 > y))) + || ((GTK_OBJECT_FLAGS(child) & GANV_ITEM_ALWAYS_REDRAW) + && (child->x1 < child->canvas->redraw_x2) + && (child->y1 < child->canvas->redraw_y2) + && (child->x2 > child->canvas->redraw_x1) + && (child->y2 > child->canvas->redraw_y2))) { + if (GANV_ITEM_GET_CLASS(child)->draw) { + (*GANV_ITEM_GET_CLASS(child)->draw)( + child, cr, x, y, width, height); + } + } + } +} + +static double +ganv_group_point(GanvItem* item, double x, double y, int cx, int cy, + GanvItem** actual_item) +{ + GanvGroup* group; + GList* list; + GanvItem* child, * point_item; + int x1, y1, x2, y2; + double gx, gy; + double dist, best; + int has_point; + + group = GANV_GROUP(item); + + x1 = cx - item->canvas->close_enough; + y1 = cy - item->canvas->close_enough; + x2 = cx + item->canvas->close_enough; + y2 = cy + item->canvas->close_enough; + + best = 0.0; + *actual_item = NULL; + + gx = x; + gy = y; + + dist = 0.0; /* keep gcc happy */ + + for (list = group->item_list; list; list = list->next) { + child = list->data; + + if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1)) { + continue; + } + + point_item = NULL; /* cater for incomplete item implementations */ + + if ((child->object.flags & GANV_ITEM_VISIBLE) + && GANV_ITEM_GET_CLASS(child)->point) { + dist = GANV_ITEM_GET_CLASS(child)->point( + child, + gx - child->x, gy - child->y, + cx, cy, + &point_item); + has_point = TRUE; + } else { + has_point = FALSE; + } + + if (has_point + && point_item + && ((int)(dist * item->canvas->pixels_per_unit + 0.5) + <= item->canvas->close_enough)) { + best = dist; + *actual_item = point_item; + } + } + + return best; +} + +static void +ganv_group_bounds(GanvItem* item, double* x1, double* y1, double* x2, double* y2) +{ + GanvGroup* group; + GanvItem* child; + GList* list; + double tx1, ty1, tx2, ty2; + double minx, miny, maxx, maxy; + int set; + + group = GANV_GROUP(item); + + /* Get the bounds of the first visible item */ + + child = NULL; /* Unnecessary but eliminates a warning. */ + + set = FALSE; + + for (list = group->item_list; list; list = list->next) { + child = list->data; + + if (child->object.flags & GANV_ITEM_VISIBLE) { + set = TRUE; + ganv_item_get_bounds(child, &minx, &miny, &maxx, &maxy); + break; + } + } + + /* If there were no visible items, return an empty bounding box */ + + if (!set) { + *x1 = *y1 = *x2 = *y2 = 0.0; + return; + } + + /* Now we can grow the bounds using the rest of the items */ + + list = list->next; + + for (; list; list = list->next) { + child = list->data; + + if (!(child->object.flags & GANV_ITEM_VISIBLE)) { + continue; + } + + ganv_item_get_bounds(child, &tx1, &ty1, &tx2, &ty2); + + if (tx1 < minx) { + minx = tx1; + } + + if (ty1 < miny) { + miny = ty1; + } + + if (tx2 > maxx) { + maxx = tx2; + } + + if (ty2 > maxy) { + maxy = ty2; + } + } + + *x1 = minx; + *y1 = miny; + *x2 = maxx; + *y2 = maxy; +} + +static void +ganv_group_add(GanvItem* parent, GanvItem* item) +{ + GanvGroup* group = GANV_GROUP(parent); + g_object_ref_sink(G_OBJECT(item)); + + if (!group->item_list) { + group->item_list = g_list_append(group->item_list, item); + group->item_list_end = group->item_list; + } else { + group->item_list_end = g_list_append(group->item_list_end, item)->next; + } + + if (group->item.object.flags & GANV_ITEM_REALIZED) { + (*GANV_ITEM_GET_CLASS(item)->realize)(item); + } + + if (group->item.object.flags & GANV_ITEM_MAPPED) { + (*GANV_ITEM_GET_CLASS(item)->map)(item); + } + + g_object_notify(G_OBJECT(item), "parent"); +} + +static void +ganv_group_remove(GanvItem* parent, GanvItem* item) +{ + GanvGroup* group = GANV_GROUP(parent); + GList* children; + + g_return_if_fail(GANV_IS_GROUP(group)); + g_return_if_fail(GANV_IS_ITEM(item)); + + for (children = group->item_list; children; children = children->next) { + if (children->data == item) { + if (item->object.flags & GANV_ITEM_MAPPED) { + (*GANV_ITEM_GET_CLASS(item)->unmap)(item); + } + + if (item->object.flags & GANV_ITEM_REALIZED) { + (*GANV_ITEM_GET_CLASS(item)->unrealize)(item); + } + + /* Unparent the child */ + + item->parent = NULL; + g_object_unref(G_OBJECT(item)); + + /* Remove it from the list */ + + if (children == group->item_list_end) { + group->item_list_end = children->prev; + } + + group->item_list = g_list_remove_link(group->item_list, children); + g_list_free(children); + break; + } + } +} + +static void +ganv_group_class_init(GanvGroupClass* class) +{ + GObjectClass* gobject_class; + GtkObjectClass* object_class; + GanvItemClass* item_class; + + gobject_class = (GObjectClass*)class; + object_class = (GtkObjectClass*)class; + item_class = (GanvItemClass*)class; + + group_parent_class = g_type_class_peek_parent(class); + + gobject_class->set_property = ganv_group_set_property; + gobject_class->get_property = ganv_group_get_property; + + g_object_class_install_property + (gobject_class, GROUP_PROP_X, + g_param_spec_double("x", + _("X"), + _("X"), + -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, + (G_PARAM_READABLE | G_PARAM_WRITABLE))); + g_object_class_install_property + (gobject_class, GROUP_PROP_Y, + g_param_spec_double("y", + _("Y"), + _("Y"), + -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, + (G_PARAM_READABLE | G_PARAM_WRITABLE))); + + object_class->destroy = ganv_group_destroy; + + item_class->add = ganv_group_add; + item_class->remove = ganv_group_remove; + item_class->update = ganv_group_update; + item_class->realize = ganv_group_realize; + item_class->unrealize = ganv_group_unrealize; + item_class->map = ganv_group_map; + item_class->unmap = ganv_group_unmap; + item_class->draw = ganv_group_draw; + item_class->point = ganv_group_point; + item_class->bounds = ganv_group_bounds; +} @@ -80,6 +80,7 @@ ganv_source = [ 'src/circle.c', 'src/edge.c', 'src/ganv-marshal.c', + 'src/group.c', 'src/module.c', 'src/node.c', 'src/port.c', |