From 93b157843473065b73cbaa99e998f57d39f189ad Mon Sep 17 00:00:00 2001 From: Cedric Bosdonnat Date: Thu, 26 Nov 2009 22:05:12 +0100 Subject: Bug #361156 - contacts-map plugin Add a map showing the location of contacts when possible. It's disabled at the moment. --- plugins/contacts-map/Makefile.am | 31 +++ plugins/contacts-map/contacts-map.c | 216 +++++++++++++++++++++ plugins/contacts-map/geo-utils.c | 102 ++++++++++ plugins/contacts-map/geo-utils.h | 28 +++ .../contacts-map/org-gnome-contacts-map.eplug.xml | 19 ++ 5 files changed, 396 insertions(+) create mode 100644 plugins/contacts-map/Makefile.am create mode 100644 plugins/contacts-map/contacts-map.c create mode 100644 plugins/contacts-map/geo-utils.c create mode 100644 plugins/contacts-map/geo-utils.h create mode 100644 plugins/contacts-map/org-gnome-contacts-map.eplug.xml (limited to 'plugins') diff --git a/plugins/contacts-map/Makefile.am b/plugins/contacts-map/Makefile.am new file mode 100644 index 0000000000..91ff22c0bc --- /dev/null +++ b/plugins/contacts-map/Makefile.am @@ -0,0 +1,31 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/widgets \ + $(EVOLUTION_ADDRESSBOOK_CFLAGS) \ + $(CHAMPLAIN_CFLAGS) \ + $(GEOCLUE_CFLAGS) + +@EVO_PLUGIN_RULE@ + +plugin_DATA = org-gnome-contacts-map.eplug +plugin_LTLIBRARIES = liborg-gnome-contacts-map.la + +liborg_gnome_contacts_map_la_SOURCES = \ + contacts-map.c \ + geo-utils.c \ + geo-utils.h + +liborg_gnome_contacts_map_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) +liborg_gnome_contacts_map_la_LIBADD = \ + $(top_builddir)/e-util/libeutil.la \ + $(top_builddir)/shell/libeshell.la \ + $(CHAMPLAIN_LIBS) \ + $(GEOCLUE_LIBS) \ + $(EVOLUTION_ADDRESSBOOK_LIBS) + +EXTRA_DIST = org-gnome-contacts-maps.eplug.xml + +BUILT_SOURCES = $(plugin_DATA) +CLEANFILES = $(BUILT_SOURCES) + +-include $(top_srcdir)/git.mk diff --git a/plugins/contacts-map/contacts-map.c b/plugins/contacts-map/contacts-map.c new file mode 100644 index 0000000000..8961233587 --- /dev/null +++ b/plugins/contacts-map/contacts-map.c @@ -0,0 +1,216 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + * + * Authors: + * Cedric Bosdonnat + * + * Copyright (C) 2009 Cedric Bosdonnat (http://cedric.bosdonnat.free.fr) + * + */ +#include "geo-utils.h" + +#include + +#include +#include + +#include +#include +#include + + +/* Plugin entry points */ + +gboolean addressbook_map_init (GtkUIManager *ui_manager, EShellView *shell_view); +void action_show_ebook_map (GtkAction *action, EShellView *shell_view); +void show_map_general (ESourceSelector *selector); + +/* Implementations */ + +gboolean +addressbook_map_init (GtkUIManager *ui_manager, EShellView *shell_view) +{ + EShell *shell; + EShellSettings *shell_settings; + EShellWindow *shell_window; + GtkActionGroup *action_group; + GtkAction *action; + GIcon *icon; + const gchar *tooltip; + const gchar *name; + const gchar *label; + + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + name = "contacts-map"; + label = _("Contacts map"); + tooltip = _("Show a map of all the contacts"); + action = gtk_action_new (name, NULL, tooltip, NULL); + icon = g_themed_icon_new ("gnome-globe"); + gtk_action_set_gicon (action, icon); + gtk_action_set_label (action, label); + + name = "contacts"; + action_group = e_shell_window_get_action_group (shell_window, name); + gtk_action_group_add_action (action_group, action); + + g_signal_connect ( + action, "activate", + G_CALLBACK (action_show_ebook_map), shell_view); + + g_object_unref (action); + + return TRUE; +} + + +void +action_show_ebook_map (GtkAction *action, EShellView *shell_view) +{ + EShellSidebar *shell_sidebar; + ESourceSelector *selector = NULL; + + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + g_object_get (shell_sidebar, "selector", &selector, NULL); + g_return_if_fail (selector != NULL); + + show_map_general (selector); + + g_object_unref (selector); +} + + +void +show_map_general (ESourceSelector *selector) +{ + EBook *book; + ESource *primary_source; + EBookQuery *query; + GList *contacts, *tmp; + gchar *uri; + + GeoclueGeocode *geocoder; + GeocluePositionFields fields; + GeoclueAccuracy *accuracy; + + gdouble lat = 0; + gdouble lng = 0; + + GtkWidget *map_widget; + ChamplainView *view; + ChamplainLayer *layer; + + gdouble *min_lat = NULL; + gdouble *max_lat = NULL; + gdouble *min_lng = NULL; + gdouble *max_lng = NULL; + + primary_source = (ESource*)e_source_selector_peek_primary_selection (selector); + uri = e_source_get_uri (primary_source); + book = e_book_new_from_uri (uri, NULL); + + if (!book || !e_book_open (book, TRUE, NULL)) + { + g_warning ("Couldn't load addressbook %s", uri); + return; + } + + /* Get all the contacts with an address */ + query = e_book_query_field_exists (E_CONTACT_ADDRESS); + e_book_get_contacts (book, query, &contacts, NULL); + e_book_query_unref (query); + + init_map (&view, &map_widget); + layer = champlain_selection_layer_new (); + + geocoder = NULL; + geocoder = get_geocoder (); + if (geocoder != NULL) { + for (tmp = contacts; tmp; tmp = tmp->next) { + GError *error = NULL; + EContact *contact; + EContactAddress *addr; + GHashTable *details; + + contact = tmp->data; + + /* Get the lat & lng and add the marker asynchronously */ + addr = e_contact_get (contact, E_CONTACT_ADDRESS_HOME); + details = (GHashTable*) get_geoclue_from_address (addr); + fields = geoclue_geocode_address_to_position (geocoder, details, + &lat, &lng, NULL, &accuracy, &error); + + if (!error && + (fields & GEOCLUE_POSITION_FIELDS_LATITUDE) != 0 && + (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) != 0) { + /* Add the marker to the map */ + add_marker (layer, lat, lng, contact); + if (!min_lat) { + min_lat = g_malloc (sizeof (gdouble)); + *min_lat = lat; + } + if (!max_lat) { + max_lat = g_malloc (sizeof(gdouble)); + *max_lat = lat; + } + if (!min_lng) { + min_lng = g_malloc (sizeof (gdouble)); + *min_lng = lng; + } + if (!max_lng) { + max_lng = malloc (sizeof (gdouble)); + *max_lng = lng; + } + + /* Store the min/max lat/lng */ + get_min_max (min_lat, max_lat, + min_lng, max_lng, lat, lng); + } else if (error) { + g_warning ("Error while geocoding: %s\n", error->message); + g_error_free (error); + } + + g_hash_table_destroy (details); + g_object_unref (contact); + } + } + + champlain_view_add_layer (view, layer); + champlain_layer_show (layer); + champlain_layer_show_all_markers (CHAMPLAIN_LAYER (layer)); + + create_map_window (map_widget, _("Contacts map")); + + /* Do not ensure something visible is we have nothing */ + if (min_lat && min_lng && max_lat && max_lng) + champlain_view_ensure_visible (view, + *min_lat, *min_lng, + *max_lat, *max_lng, FALSE); + + g_free (min_lat); + g_free (max_lat); + g_free (min_lng); + g_free (max_lng); + + g_object_unref (geocoder); + + if (contacts != NULL) + g_list_free (contacts); + + g_object_unref (book); + g_free (uri); +} diff --git a/plugins/contacts-map/geo-utils.c b/plugins/contacts-map/geo-utils.c new file mode 100644 index 0000000000..3f7473cab4 --- /dev/null +++ b/plugins/contacts-map/geo-utils.c @@ -0,0 +1,102 @@ +#include "geo-utils.h" + +static gboolean is_clutter_initialized = FALSE; + +void +get_min_max (gdouble *min_lat, gdouble *max_lat, + gdouble *min_lng, gdouble *max_lng, + gdouble lat, gdouble lng) +{ + if (lat < *min_lat) + *min_lat = lat; + else if (lat > *max_lat) + *max_lat = lat; + + if (lng < *min_lng) + *min_lng = lng; + else if (lng > *max_lng) + *max_lng = lng; +} + +void +add_marker (ChamplainLayer *layer, gdouble lat, gdouble lng, EContact *contact) +{ + ClutterActor *marker; + + gchar *contact_name = e_contact_get (contact, E_CONTACT_FULL_NAME); + marker = champlain_marker_new_with_text (contact_name, "Serif 8", NULL, NULL); + g_free (contact_name); + + champlain_marker_set_use_markup (CHAMPLAIN_MARKER (marker), FALSE); + champlain_base_marker_set_position (CHAMPLAIN_BASE_MARKER (marker), lat, lng); + + champlain_layer_add_marker (layer, CHAMPLAIN_BASE_MARKER(marker)); +} + +GeoclueGeocode* +get_geocoder (void) +{ + GeoclueGeocode *geocoder = NULL; + + /* Create new GeoclueGeocode */ + geocoder = geoclue_geocode_new ("org.freedesktop.Geoclue.Providers.Yahoo", + "/org/freedesktop/Geoclue/Providers/Yahoo"); + + return geocoder; +} + +GHashTable * +get_geoclue_from_address (const EContactAddress* addr) +{ + GHashTable *address = geoclue_address_details_new (); + + g_hash_table_insert (address, g_strdup (GEOCLUE_ADDRESS_KEY_POSTALCODE), g_strdup ((*addr).code)); + g_hash_table_insert (address, g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRY), g_strdup ((*addr).country)); + g_hash_table_insert (address, g_strdup (GEOCLUE_ADDRESS_KEY_LOCALITY), g_strdup ((*addr).locality)); + g_hash_table_insert (address, g_strdup (GEOCLUE_ADDRESS_KEY_STREET), g_strdup ((*addr).street)); + + return address; +} + +void +init_map (ChamplainView **view, GtkWidget **widget) +{ + if (!is_clutter_initialized) { + gtk_clutter_init (NULL, NULL); + is_clutter_initialized = TRUE; + } + + *widget = gtk_champlain_embed_new (); + *view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (*widget)); + + champlain_view_set_show_license (*view, FALSE); + + g_object_set (G_OBJECT (*view), "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC, + "zoom-level", 9, NULL); +} + +void +create_map_window (GtkWidget *map_widget, const gchar *title) +{ + GtkWidget *window, *viewport; + + /* create the main, top level, window */ + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + /* give the window a 10px wide border */ + gtk_container_set_border_width (GTK_CONTAINER (window), 10); + + /* give it the title */ + gtk_window_set_title (GTK_WINDOW (window), title ); + + gtk_widget_set_size_request (map_widget, 300, 300); + + viewport = gtk_frame_new (NULL); + gtk_container_add (GTK_CONTAINER (viewport), map_widget); + + /* and insert it into the main window */ + gtk_container_add (GTK_CONTAINER (window), viewport); + + /* make sure that everything, window and label, are visible */ + gtk_widget_show_all (window); +} diff --git a/plugins/contacts-map/geo-utils.h b/plugins/contacts-map/geo-utils.h new file mode 100644 index 0000000000..65248f4893 --- /dev/null +++ b/plugins/contacts-map/geo-utils.h @@ -0,0 +1,28 @@ +#include +#include + +#include +#include + +#include +#include +#include +#include + +void +get_min_max (gdouble *min_lat, gdouble *max_lat, + gdouble *min_lng, gdouble *max_lng, + gdouble lat, gdouble lng); + +GeoclueGeocode *get_geocoder (void); + +void add_marker ( + ChamplainLayer *layer, + gdouble lat, gdouble lng, + EContact *contact); + +GHashTable *get_geoclue_from_address (const EContactAddress* addr); + +void init_map (ChamplainView **view, GtkWidget **widget); + +void create_map_window (GtkWidget *map_widget, const gchar *title); diff --git a/plugins/contacts-map/org-gnome-contacts-map.eplug.xml b/plugins/contacts-map/org-gnome-contacts-map.eplug.xml new file mode 100644 index 0000000000..e695979491 --- /dev/null +++ b/plugins/contacts-map/org-gnome-contacts-map.eplug.xml @@ -0,0 +1,19 @@ + + + + + <_description>Add a map showing the location of contacts when possible. + + + + + + + + + + + + + -- cgit