/* diversity-viewport.c - DiversityViewport, a glance at the glorious world * * Copyright 2007-2008 OpenMoko, Inc. * Authored by Chia-I Wu * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "diversity-marshal.h" enum { ENABLED, OBJECT_ADDED, OBJECT_REMOVED, OBJECT_CLEARED, FILTER_CHANGED, LAST_SIGNAL }; #define DIVERSITY_VIEWPORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), DIVERSITY_TYPE_VIEWPORT, DiversityViewportPrivate)) typedef struct _DiversityViewportPrivate DiversityViewportPrivate; typedef struct _DiversityViewportRule DiversityViewportRule; struct _DiversityViewportRule { DiversityObjectType type; gint mask; gint value; }; struct _DiversityViewportPrivate { GSList *objects; gboolean enabled; DiversityViewportFilter filter; gpointer filter_data; DiversityViewportRule rule; }; static guint viewport_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE(DiversityViewport, diversity_viewport, DIVERSITY_TYPE_OBJECT); static void on_object_geometry_changed(DiversityObject *obj, gdouble lon, gdouble lat, gdouble width, gdouble height, gpointer data); static void diversity_viewport_dispose(GObject *obj) { DiversityViewport *view = DIVERSITY_VIEWPORT(obj); DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); GSList *tmp_list; tmp_list = priv->objects; while (tmp_list) { g_signal_handlers_disconnect_by_func(tmp_list->data, on_object_geometry_changed, view); g_object_unref(tmp_list->data); tmp_list = tmp_list->next; } ((GObjectClass *) diversity_viewport_parent_class)->dispose(obj); } static void diversity_viewport_finalize(GObject *obj) { DiversityViewport *view = DIVERSITY_VIEWPORT(obj); DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); g_slist_free(priv->objects); priv->objects = NULL; ((GObjectClass *) diversity_viewport_parent_class)->finalize(obj); } static void diversity_viewport_filter_changed(DiversityViewport *view) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); GSList *tmp_list; if (!priv->filter) return; tmp_list = priv->objects; while (tmp_list) { DiversityObject *obj = tmp_list->data; tmp_list = tmp_list->next; if (!priv->filter(view, obj, priv->filter_data)) diversity_viewport_remove_object(view, obj); } } static void diversity_viewport_class_init(DiversityViewportClass *klass) { GObjectClass *o_class = (GObjectClass *) klass; o_class->dispose = diversity_viewport_dispose; o_class->finalize = diversity_viewport_finalize; klass->filter_changed = diversity_viewport_filter_changed; g_type_class_add_private(klass, sizeof(DiversityViewportPrivate)); viewport_signals[ENABLED] = g_signal_new("enabled", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(DiversityViewportClass, enabled), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); viewport_signals[OBJECT_ADDED] = g_signal_new("object-added", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(DiversityViewportClass, object_added), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); viewport_signals[OBJECT_REMOVED] = g_signal_new("object-removed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(DiversityViewportClass, object_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); viewport_signals[OBJECT_CLEARED] = g_signal_new("object-cleared", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(DiversityViewportClass, object_cleared), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); viewport_signals[FILTER_CHANGED] = g_signal_new("filter-changed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(DiversityViewportClass, filter_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void diversity_viewport_init(DiversityViewport *view) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); DIVERSITY_OBJECT(view)->type = DIVERSITY_OBJECT_TYPE_VIEWPORT; priv->objects = NULL; priv->enabled = FALSE; priv->filter = NULL; } static void on_viewport_geometry_changed(DiversityViewport *view, gdouble lon, gdouble lat, gdouble width, gdouble height, gpointer data) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); GSList *tmp_list = priv->objects; while (tmp_list) { DiversityObject *obj = tmp_list->data; tmp_list = tmp_list->next; if (!diversity_object_intersect((DiversityObject *) view, obj)) diversity_viewport_remove_object(view, obj); } } static void on_object_geometry_changed(DiversityObject *obj, gdouble lon, gdouble lat, gdouble width, gdouble height, gpointer data) { DiversityViewport *view = data; if (!diversity_object_intersect((DiversityObject *) view, obj)) diversity_viewport_remove_object(view, obj); } DiversityViewport *diversity_viewport_new(gdouble lon1, gdouble lat1, gdouble lon2, gdouble lat2) { DiversityObject *view; if (lon1 > lon2 || lat1 > lat2) return NULL; view = g_object_new(DIVERSITY_TYPE_VIEWPORT, NULL); diversity_object_geometry_set(view, lon1, lat1, lon2 - lon1, lat2 - lat1); g_signal_connect(view, "geometry_changed", G_CALLBACK(on_viewport_geometry_changed), NULL); return (DiversityViewport *) view; } gboolean diversity_viewport_add_object(DiversityViewport *view, DiversityObject *obj) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); if (!priv->enabled) return FALSE; if (priv->filter && !priv->filter(view, obj, priv->filter_data)) return FALSE; if (!diversity_object_intersect((DiversityObject *) view, obj)) return FALSE; if (g_slist_find(priv->objects, obj)) return FALSE; g_signal_connect(obj, "geometry_changed", G_CALLBACK(on_object_geometry_changed), view); priv->objects = g_slist_prepend(priv->objects, obj); g_object_ref(obj); diversity_viewport_get_objects(view); g_signal_emit(view, viewport_signals[OBJECT_ADDED], 0, obj); return TRUE; } void diversity_viewport_remove_object(DiversityViewport *view, DiversityObject *obj) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); GSList *tmp; tmp = g_slist_find(priv->objects, obj); if (!tmp) return; g_signal_handlers_disconnect_by_func(obj, on_object_geometry_changed, view); priv->objects = g_slist_delete_link(priv->objects, tmp); g_object_unref(obj); g_signal_emit(view, viewport_signals[OBJECT_REMOVED], 0, obj); } void diversity_viewport_set_filter(DiversityViewport *view, DiversityViewportFilter filter, gpointer data) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); priv->filter = filter; priv->filter_data = data; g_signal_emit(view, viewport_signals[FILTER_CHANGED], 0); } static gboolean rule_filter(DiversityViewport *view, DiversityObject *obj, gpointer data) { DiversityViewportRule *rule = data; guint flags; flags = diversity_object_get_flags(obj); return (obj->type == rule->type && (flags & rule->mask) == rule->value); } void diversity_viewport_set_rule(DiversityViewport *view, DiversityObjectType type, gint mask, gint value) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); priv->rule.type = type; priv->rule.mask = mask; priv->rule.value = value & mask; diversity_viewport_set_filter(view, rule_filter, &priv->rule); } void diversity_viewport_clear_objects(DiversityViewport *view) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); GSList *tmp_list = priv->objects; while (tmp_list) { g_signal_handlers_disconnect_by_func(tmp_list->data, on_object_geometry_changed, view); g_object_unref(tmp_list->data); tmp_list = tmp_list->next; } g_slist_free(priv->objects); priv->objects = NULL; g_signal_emit(view, viewport_signals[OBJECT_CLEARED], 0); } guint diversity_viewport_count_objects(DiversityViewport *view) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); return g_slist_length(priv->objects); } const GSList *diversity_viewport_get_objects(DiversityViewport *view) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); return priv->objects; } void diversity_viewport_set_enabled(DiversityViewport *view, gboolean enabled) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); if (priv->enabled != enabled) { priv->enabled = enabled; g_signal_emit(view, viewport_signals[ENABLED], 0, enabled); if (!enabled) diversity_viewport_clear_objects(view); } } gboolean diversity_viewport_get_enabled(DiversityViewport *view) { DiversityViewportPrivate *priv = DIVERSITY_VIEWPORT_GET_PRIVATE(view); return priv->enabled; }