summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ganv/Canvas.hpp2
-rw-r--r--ganv/canvas-base.h94
-rw-r--r--ganv/canvas.h2
-rw-r--r--ganv/ganv.h1
-rw-r--r--ganv/group.h51
-rw-r--r--ganv/node.h1
-rw-r--r--src/Canvas.cpp6
-rw-r--r--src/canvas-base.c637
-rw-r--r--src/ganv-private.h7
-rw-r--r--src/group.c490
-rw-r--r--wscript1
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;
+}
diff --git a/wscript b/wscript
index fb5bb69..2b2caa5 100644
--- a/wscript
+++ b/wscript
@@ -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',