diff options
author | David Robillard <d@drobilla.net> | 2011-12-13 22:55:42 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2011-12-13 22:55:42 +0000 |
commit | 677f884681b567377304b3d5c55a4eb02899f2de (patch) | |
tree | bd38af36ef6d52d9a97342b2ba0658113041bc10 /src | |
parent | 635cf8f91b2405dec530ac813d02a7440c3c836b (diff) | |
download | ganv-677f884681b567377304b3d5c55a4eb02899f2de.tar.gz ganv-677f884681b567377304b3d5c55a4eb02899f2de.tar.bz2 ganv-677f884681b567377304b3d5c55a4eb02899f2de.zip |
Move group implementation to separate files.
git-svn-id: http://svn.drobilla.net/lad/trunk/ganv@3870 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src')
-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 |
4 files changed, 540 insertions, 600 deletions
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; +} |