/* dia-canvas-group.c * Copyright (C) 2000-2003 James Henstridge, Arjan Molenaar * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "dia-canvas-group.h" #include "dia-handle.h" #include "diamarshal.h" #include #include "dia-canvas-i18n.h" static void dia_canvas_group_init (DiaCanvasGroup *canvas_group); static void dia_canvas_group_class_init (DiaCanvasGroupClass *klass); static void dia_canvas_group_groupable_init (DiaCanvasGroupableIface *groupable); static void dia_canvas_group_dispose (GObject *object); static void dia_canvas_group_update (DiaCanvasItem *item, gdouble affine[6]); static gdouble dia_canvas_group_point (DiaCanvasItem *item, gdouble x, gdouble y); /* Interface functions: */ static void dia_canvas_group_groupable_add (DiaCanvasGroupable *group, DiaCanvasItem *item); static void dia_canvas_group_groupable_remove (DiaCanvasGroupable *group, DiaCanvasItem *item); static gboolean dia_canvas_group_groupable_get_iter (DiaCanvasGroupable *group, DiaCanvasIter *iter); static gboolean dia_canvas_group_groupable_next (DiaCanvasGroupable *group, DiaCanvasIter *iter); static DiaCanvasItem* dia_canvas_group_groupable_value (DiaCanvasGroupable *group, DiaCanvasIter *iter); static gint dia_canvas_group_groupable_length (DiaCanvasGroupable *group); static gint dia_canvas_group_groupable_pos (DiaCanvasGroupable *group, DiaCanvasItem *item); static DiaCanvasItemClass *parent_class = NULL; /* Quark for the old item's index. */ static GQuark q_z_order = 0; GType dia_canvas_group_get_type (void) { static GType object_type = 0; if (!object_type) { static const GTypeInfo object_info = { sizeof (DiaCanvasGroupClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) dia_canvas_group_class_init, (GClassFinalizeFunc) NULL, (gconstpointer) NULL, /* class_data */ sizeof (DiaCanvasGroup), (guint16) 0, /* n_preallocs */ (GInstanceInitFunc) dia_canvas_group_init, }; static const GInterfaceInfo groupable_info = { (GInterfaceInitFunc) dia_canvas_group_groupable_init, NULL, NULL, }; object_type = g_type_register_static (DIA_TYPE_CANVAS_ITEM, "DiaCanvasGroup", &object_info, 0); g_type_add_interface_static (object_type, DIA_TYPE_CANVAS_GROUPABLE, &groupable_info); q_z_order = g_quark_from_static_string ("DiaCanvasGroup::z_order"); } return object_type; } static void dia_canvas_group_class_init (DiaCanvasGroupClass *klass) { GObjectClass *object_class; DiaCanvasItemClass *item_class; object_class = (GObjectClass*) klass; item_class = DIA_CANVAS_ITEM_CLASS(klass); parent_class = g_type_class_peek_parent (klass); object_class->dispose = dia_canvas_group_dispose; item_class->update = dia_canvas_group_update; item_class->point = dia_canvas_group_point; } static void dia_canvas_group_groupable_init (DiaCanvasGroupableIface *groupable) { groupable->add = dia_canvas_group_groupable_add; groupable->remove = dia_canvas_group_groupable_remove; groupable->get_iter = dia_canvas_group_groupable_get_iter; groupable->next = dia_canvas_group_groupable_next; groupable->value = dia_canvas_group_groupable_value; groupable->length = dia_canvas_group_groupable_length; groupable->pos = dia_canvas_group_groupable_pos; } static void dia_canvas_group_init (DiaCanvasGroup *group) { group->children = NULL; DIA_UNSET_FLAGS ((DiaCanvasItem*)group, DIA_INTERACTIVE); } static void dia_canvas_group_dispose (GObject *object) { DiaCanvasGroup *group = (DiaCanvasGroup*) object; /* unref all children */ while (group->children) dia_canvas_groupable_remove (DIA_CANVAS_GROUPABLE (group), group->children->data); G_OBJECT_CLASS (parent_class)->dispose (object); } static void dia_canvas_group_update (DiaCanvasItem *item, gdouble affine[6]) { DiaCanvasGroup *group; DiaRectangle new_bounds = { 0.0, 0.0, 0.0, 0.0 }; gboolean set = FALSE; static guint update_signal_id = 0; GList *tmp; g_return_if_fail(item != NULL); g_return_if_fail(DIA_IS_CANVAS_GROUP(item)); //g_message (G_STRLOC); parent_class->update (item, affine); group = DIA_CANVAS_GROUP(item); if (!update_signal_id) update_signal_id = g_signal_lookup("update", DIA_TYPE_CANVAS_ITEM); for (tmp = group->children; tmp != NULL; tmp = tmp->next) { DiaCanvasItem *child = tmp->data; dia_canvas_item_update_child (item, child, affine); if (DIA_CANVAS_ITEM_VISIBLE (child)) { gdouble x1, x2, y1, y2; dia_canvas_item_bb_affine (child, child->affine, &x1, &y1, &x2, &y2); if (!set) { new_bounds.left = x1; new_bounds.top = y1; new_bounds.right = x2; new_bounds.bottom = y2; set = TRUE; } else { new_bounds.left = MIN(x1, new_bounds.left); new_bounds.top = MIN(y1, new_bounds.top); new_bounds.right = MAX(x2, new_bounds.right); new_bounds.bottom = MAX(y2, new_bounds.bottom); } //g_message (G_STRLOC": item: (%f, %f) (%f, %f)", // x1, y1, x2, y2); //g_message (G_STRLOC": bond: (%f, %f) (%f, %f)", // new_bounds.left, new_bounds.top, // new_bounds.right, new_bounds.bottom); set = TRUE; } } if (!set) { item->bounds.left = item->bounds.top = item->bounds.right = item->bounds.bottom = 0.0; } else { item->bounds = new_bounds; } //g_message (G_STRLOC": (%f, %f) (%f, %f)", item->bounds.left, // item->bounds.top, item->bounds.right, item->bounds.bottom); //g_message (G_STRLOC": bond: (%f, %f) (%f, %f)", // new_bounds.left, new_bounds.top, // new_bounds.right, new_bounds.bottom); } static gdouble dia_canvas_group_point (DiaCanvasItem *item, gdouble x, gdouble y) { if ((x > item->bounds.left) && (x < item->bounds.right) && (y > item->bounds.top) && (y < item->bounds.bottom)) return 0.0; else return G_MAXDOUBLE; } static void dia_canvas_group_groupable_add (DiaCanvasGroupable *group, DiaCanvasItem *item) { g_assert (DIA_IS_CANVAS_ITEM (item)); DIA_CANVAS_GROUP (group)->children = g_list_append (DIA_CANVAS_GROUP (group)->children, item); g_object_ref (item); dia_canvas_item_set_child_of (item, DIA_CANVAS_ITEM (group)); /* TODO: Remove this as soon as we can set properties on interfaces. */ if (DIA_CANVAS_ITEM (group)->canvas) { guint index = GPOINTER_TO_SIZE(g_object_get_qdata (G_OBJECT (item), q_z_order)); if (index > 0) { guint length = g_list_length (DIA_CANVAS_GROUP (group)->children); g_object_set_qdata (G_OBJECT (item), q_z_order, NULL); dia_canvas_group_lower_item (DIA_CANVAS_GROUP (group), item, length - index); } } g_object_set_qdata (G_OBJECT (item), q_z_order, NULL); } static void dia_canvas_group_groupable_remove (DiaCanvasGroupable *group, DiaCanvasItem *item) { guint index; /* Save the current position so the removal can be undone. */ index = g_list_index (DIA_CANVAS_GROUP (group)->children, item) + 1; g_object_set_qdata (G_OBJECT (item), q_z_order, GSIZE_TO_POINTER(index)); DIA_CANVAS_GROUP (group)->children = g_list_remove (DIA_CANVAS_GROUP (group)->children, item); g_object_unref (item); dia_canvas_item_set_child_of (item, NULL); } static gboolean dia_canvas_group_groupable_get_iter (DiaCanvasGroupable *group, DiaCanvasIter *iter) { g_return_val_if_fail (DIA_IS_CANVAS_GROUP (group), FALSE); iter->data[0] = DIA_CANVAS_GROUP (group)->children; return iter->data[0] ? TRUE : FALSE; } static gboolean dia_canvas_group_groupable_next (DiaCanvasGroupable *group, DiaCanvasIter *iter) { g_return_val_if_fail (DIA_IS_CANVAS_GROUP (group), FALSE); iter->data[0] = g_list_next (iter->data[0]); return iter->data[0] ? TRUE : FALSE; } static DiaCanvasItem* dia_canvas_group_groupable_value (DiaCanvasGroupable *group, DiaCanvasIter *iter) { g_return_val_if_fail (DIA_IS_CANVAS_GROUP (group), NULL); return iter->data[0] ? ((GList*) iter->data[0])->data : NULL; } static gint dia_canvas_group_groupable_length (DiaCanvasGroupable *group) { g_return_val_if_fail (DIA_IS_CANVAS_GROUP (group), -1); return g_list_length (DIA_CANVAS_GROUP (group)->children); } static gint dia_canvas_group_groupable_pos (DiaCanvasGroupable *group, DiaCanvasItem *item) { g_return_val_if_fail (DIA_IS_CANVAS_GROUP (group), -1); return g_list_index (DIA_CANVAS_GROUP (group)->children, item); } /* * Public functions */ /** * dia_canvas_group_create_item: * @group: * @type: * @first_arg_name: * @...: * * Create a new #DiaCanvasItem of @type as a child of the @group. * You can add properties that have to be set on the newly created item. The * last value should be %NULL. Note that the property 'parent' has no effect. * * Return value: The newly created canvas item. Note that this is a borrowed * reference, you should reference it yourself if you want to keep a * reference to the newly created item. **/ DiaCanvasItem* dia_canvas_group_create_item (DiaCanvasGroup *group, GType type, const gchar* first_arg_name, ...) { DiaCanvasItem *item = NULL; gboolean old_allow_undo = FALSE; va_list va_args; g_return_val_if_fail (DIA_IS_CANVAS_GROUP (group), NULL); g_return_val_if_fail (g_type_is_a (type, DIA_TYPE_CANVAS_ITEM), NULL); if (DIA_CANVAS_ITEM (group)->canvas) { old_allow_undo = DIA_CANVAS_ITEM (group)->canvas->allow_undo; DIA_CANVAS_ITEM (group)->canvas->allow_undo = FALSE; } if (first_arg_name) { va_start (va_args, first_arg_name); item = (DiaCanvasItem*) g_object_new_valist (type, first_arg_name, va_args); va_end (va_args); } else item = g_object_new (type, NULL); if (DIA_CANVAS_ITEM (group)->canvas) DIA_CANVAS_ITEM (group)->canvas->allow_undo = old_allow_undo; if (!item->parent) dia_canvas_groupable_add (DIA_CANVAS_GROUPABLE (group), item); /* We unref the item since the 'add' method did ref it. If it was * not ref'ed we destroy it this way... */ if (G_OBJECT (item)->ref_count == 1) { g_object_unref (item); return NULL; } g_object_unref (item); return item; } static void z_order (DiaCanvasGroup *group, DiaCanvasItem *item, gint pos) { guint old_pos; /* item->parent may be NULL, since it is set after the "add" signal */ g_assert (g_list_index (group->children, item) >= 0); if (pos == 0) return; old_pos = g_list_index (group->children, item); group->children = g_list_remove (group->children, item); group->children = g_list_insert (group->children, item, old_pos + pos); g_signal_emit_by_name (G_OBJECT (item), "z_order", pos); } /** * dia_canvas_group_raise_item: * @group: * @item: * @pos: * * Bring an object @pos positions to the front. **/ void dia_canvas_group_raise_item (DiaCanvasGroup *group, DiaCanvasItem *item, gint pos) { g_return_if_fail (DIA_IS_CANVAS_GROUP (group)); g_return_if_fail (DIA_IS_CANVAS_ITEM (item)); /* In case of an undo action, item->parent may be NULL */ //g_return_if_fail (item->parent == (DiaCanvasItem*) group); g_return_if_fail (g_list_index (group->children, item) >= 0); g_return_if_fail (pos >= 0); z_order (group, item, pos); } /** * dia_canvas_group_lower_item: * @group: * @item: * @pos: * * Bring @item @pos position to the back. **/ void dia_canvas_group_lower_item (DiaCanvasGroup *group, DiaCanvasItem *item, gint pos) { g_return_if_fail (DIA_IS_CANVAS_GROUP (group)); g_return_if_fail (DIA_IS_CANVAS_ITEM (item)); /* In case of an undo action, item->parent may be NULL */ //g_return_if_fail (item->parent == (DiaCanvasItem*) group); g_return_if_fail (g_list_index (group->children, item) >= 0); g_return_if_fail (pos >= 0); z_order (group, item, -pos); } /** * dia_canvas_group_foreach: * @group: * @func: * @data: * * DEPRICATED * * Call the function @func for every child of @group. You can pass extra data * via the @data value. * * Return value: **/ gint dia_canvas_group_foreach (DiaCanvasGroup *group, DiaCanvasItemForeachFunc func, gpointer data) { DiaCanvasItem *item; gint result = FALSE; DiaCanvasIter iter; g_return_val_if_fail (DIA_IS_CANVAS_ITEM (group), FALSE); g_return_val_if_fail (func != NULL, FALSE); if (func ((DiaCanvasItem*) group, data)) return TRUE; if (!DIA_IS_CANVAS_GROUPABLE (group)) return FALSE; if (dia_canvas_groupable_get_iter ((DiaCanvasGroupable*) group, &iter)) do { item = dia_canvas_groupable_value ((DiaCanvasGroupable*) group, &iter); result |= dia_canvas_group_foreach ((DiaCanvasGroup*) item, func, data); } while (dia_canvas_groupable_next ((DiaCanvasGroupable*) group, &iter)); return result; }