diff options
Diffstat (limited to 'addressbook')
-rw-r--r-- | addressbook/gui/contact-editor/Makefile.am | 2 | ||||
-rw-r--r-- | addressbook/gui/contact-list-editor/Makefile.am | 2 | ||||
-rw-r--r-- | addressbook/gui/merging/Makefile.am | 1 | ||||
-rw-r--r-- | addressbook/gui/widgets/Makefile.am | 12 | ||||
-rw-r--r-- | addressbook/gui/widgets/e-contact-map-window.c | 500 | ||||
-rw-r--r-- | addressbook/gui/widgets/e-contact-map-window.h | 81 | ||||
-rw-r--r-- | addressbook/gui/widgets/e-contact-map.c | 408 | ||||
-rw-r--r-- | addressbook/gui/widgets/e-contact-map.h | 106 | ||||
-rw-r--r-- | addressbook/gui/widgets/e-contact-marker.c | 624 | ||||
-rw-r--r-- | addressbook/gui/widgets/e-contact-marker.h | 84 | ||||
-rw-r--r-- | addressbook/gui/widgets/eab-contact-display.c | 4 | ||||
-rw-r--r-- | addressbook/importers/Makefile.am | 2 | ||||
-rw-r--r-- | addressbook/printing/Makefile.am | 2 |
13 files changed, 1814 insertions, 14 deletions
diff --git a/addressbook/gui/contact-editor/Makefile.am b/addressbook/gui/contact-editor/Makefile.am index ddd4592bf9..7664e3db50 100644 --- a/addressbook/gui/contact-editor/Makefile.am +++ b/addressbook/gui/contact-editor/Makefile.am @@ -11,7 +11,6 @@ libecontacteditor_la_CPPFLAGS = \ -DG_LOG_DOMAIN=\"contact-editor\" \ $(EVOLUTION_DATA_SERVER_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ - $(CHAMPLAIN_CFLAGS) \ $(GTKHTML_CFLAGS) libecontacteditor_la_SOURCES = \ @@ -35,7 +34,6 @@ libecontacteditor_la_LIBADD = \ $(EVOLUTION_ADDRESSBOOK_LIBS) \ $(EVOLUTION_DATA_SERVER_LIBS) \ $(GNOME_PLATFORM_LIBS) \ - $(CHAMPLAIN_LIBS) \ $(GTKHTML_LIBS) ui_DATA = \ diff --git a/addressbook/gui/contact-list-editor/Makefile.am b/addressbook/gui/contact-list-editor/Makefile.am index 0479683406..e73b89559b 100644 --- a/addressbook/gui/contact-list-editor/Makefile.am +++ b/addressbook/gui/contact-list-editor/Makefile.am @@ -11,7 +11,6 @@ libecontactlisteditor_la_CPPFLAGS = \ -DG_LOG_DOMAIN=\"contact-list-editor\" \ $(EVOLUTION_DATA_SERVER_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ - $(CHAMPLAIN_CFLAGS) \ $(GTKHTML_CFLAGS) libecontactlisteditor_la_SOURCES = \ @@ -29,7 +28,6 @@ libecontactlisteditor_la_LIBADD = \ $(top_builddir)/shell/libeshell.la \ $(EVOLUTION_DATA_SERVER_LIBS) \ $(GNOME_PLATFORM_LIBS) \ - $(CHAMPLAIN_LIBS) \ $(GTKHTML_LIBS) ui_DATA = contact-list-editor.ui diff --git a/addressbook/gui/merging/Makefile.am b/addressbook/gui/merging/Makefile.am index a6a1522c7a..319c1221fc 100644 --- a/addressbook/gui/merging/Makefile.am +++ b/addressbook/gui/merging/Makefile.am @@ -8,7 +8,6 @@ libeabbookmerging_la_CPPFLAGS = \ -I$(top_srcdir)/addressbook \ $(EVOLUTION_DATA_SERVER_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ - $(CHAMPLAIN_CFLAGS) \ $(GTKHTML_CFLAGS) libeabbookmerging_la_SOURCES = \ diff --git a/addressbook/gui/widgets/Makefile.am b/addressbook/gui/widgets/Makefile.am index e7ca15e386..4881613c79 100644 --- a/addressbook/gui/widgets/Makefile.am +++ b/addressbook/gui/widgets/Makefile.am @@ -18,8 +18,9 @@ libeabwidgets_la_CPPFLAGS = \ -I$(top_builddir)/shell \ $(EVOLUTION_DATA_SERVER_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ + $(GTKHTML_CFLAGS) \ $(CHAMPLAIN_CFLAGS) \ - $(GTKHTML_CFLAGS) + $(GEO_CFLAGS) eabincludedir = $(privincludedir)/addressbook/gui/widgets @@ -34,6 +35,12 @@ libeabwidgets_la_SOURCES = \ eab-contact-formatter.h \ eab-gui-util.c \ eab-gui-util.h \ + e-contact-map.c \ + e-contact-map.h \ + e-contact-map-window.c \ + e-contact-map-window.h \ + e-contact-marker.c \ + e-contact-marker.h \ e-minicard.c \ e-minicard.h \ e-minicard-label.c \ @@ -70,8 +77,9 @@ libeabwidgets_la_LIBADD = \ $(top_builddir)/e-util/libeutil.la \ $(EVOLUTION_DATA_SERVER_LIBS) \ $(GNOME_PLATFORM_LIBS) \ + $(GTKHTML_LIBS) \ $(CHAMPLAIN_LIBS) \ - $(GTKHTML_LIBS) + $(GEO_LIBS) dist-hook: cd $(distdir); rm -f $(BUILT_SOURCES) diff --git a/addressbook/gui/widgets/e-contact-map-window.c b/addressbook/gui/widgets/e-contact-map-window.c new file mode 100644 index 0000000000..2e3aec5bcb --- /dev/null +++ b/addressbook/gui/widgets/e-contact-map-window.c @@ -0,0 +1,500 @@ +/* + * e-contact-map-window.c + * + * 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 <http://www.gnu.org/licenses/> + * + * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef WITH_CONTACT_MAPS + +#include "e-contact-map.h" +#include "e-contact-map-window.h" +#include "e-contact-marker.h" + +#include <champlain/champlain.h> + +#include <string.h> + +#include <glib/gi18n.h> +#include <glib-object.h> + +#define E_CONTACT_MAP_WINDOW_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CONTACT_MAP_WINDOW, EContactMapWindowPrivate)) + +G_DEFINE_TYPE (EContactMapWindow, e_contact_map_window, GTK_TYPE_WINDOW) + +struct _EContactMapWindowPrivate { + EContactMap *map; + + GtkWidget *zoom_in_btn; + GtkWidget *zoom_out_btn; + + GtkWidget *search_entry; + GtkListStore *completion_model; + + GHashTable *hash_table; /* Hash table contact-name -> marker */ + + GtkWidget *spinner; + gint tasks_cnt; +}; + +enum { + SHOW_CONTACT_EDITOR, + LAST_SIGNAL +}; + +static gint signals[LAST_SIGNAL] = {0}; + +static void +marker_doubleclick_cb (ClutterActor *actor, + gpointer user_data) +{ + EContactMapWindow *window = user_data; + EContactMarker *marker; + const gchar *contact_uid; + + marker = E_CONTACT_MARKER (actor); + contact_uid = e_contact_marker_get_contact_uid (marker); + + g_signal_emit (window, signals[SHOW_CONTACT_EDITOR], 0, contact_uid); +} + +static void +book_contacts_received_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + EContactMapWindow *window = user_data; + EBookClient *client = E_BOOK_CLIENT (source_object); + GSList *contacts = NULL, *p; + GError *error = NULL; + + if (!e_book_client_get_contacts_finish (client, result, &contacts, &error)) + contacts = NULL; + + if (error != NULL) { + g_warning ( + "%s: Failed to get contacts: %s", + G_STRFUNC, error->message); + g_error_free (error); + } + + for (p = contacts; p; p = p->next) + e_contact_map_add_contact ( + window->priv->map, (EContact *) p->data); + + e_client_util_free_object_slist (contacts); + g_object_unref (client); +} + +static void +contact_map_window_zoom_in_cb (GtkButton *button, + gpointer user_data) +{ + EContactMapWindow *window = user_data; + ChamplainView *view; + + view = e_contact_map_get_view (window->priv->map); + + champlain_view_zoom_in (view); +} + +static void +contact_map_window_zoom_out_cb (GtkButton *button, + gpointer user_data) +{ + EContactMapWindow *window = user_data; + ChamplainView *view; + + view = e_contact_map_get_view (window->priv->map); + + champlain_view_zoom_out (view); +} +static void +zoom_level_changed_cb (ChamplainView *view, + GParamSpec *pspec, + gpointer user_data) +{ + EContactMapWindow *window = user_data; + gint zoom_level = champlain_view_get_zoom_level (view); + + gtk_widget_set_sensitive ( + window->priv->zoom_in_btn, + (zoom_level < champlain_view_get_max_zoom_level (view))); + + gtk_widget_set_sensitive ( + window->priv->zoom_out_btn, + (zoom_level > champlain_view_get_min_zoom_level (view))); +} + +/** + * Add contact to hash_table only when EContactMap tells us + * that the contact has really been added to map. + */ +static void +map_contact_added_cb (EContactMap *map, + ClutterActor *marker, + gpointer user_data) +{ + EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv; + const gchar *name; + GtkTreeIter iter; + + name = champlain_label_get_text (CHAMPLAIN_LABEL (marker)); + + g_hash_table_insert ( + priv->hash_table, + g_strdup (name), marker); + + gtk_list_store_append (priv->completion_model, &iter); + gtk_list_store_set ( + priv->completion_model, &iter, + 0, name, -1); + + g_signal_connect ( + marker, "double-clicked", + G_CALLBACK (marker_doubleclick_cb), user_data); + + priv->tasks_cnt--; + if (priv->tasks_cnt == 0) { + gtk_spinner_stop (GTK_SPINNER (priv->spinner)); + gtk_widget_hide (priv->spinner); + } +} + +static void +map_contact_removed_cb (EContactMap *map, + const gchar *name, + gpointer user_data) +{ + EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv; + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL (priv->completion_model); + + g_hash_table_remove (priv->hash_table, name); + + if (gtk_tree_model_get_iter_first (model, &iter)) { + do { + gchar *name_str; + gtk_tree_model_get (model, &iter, 0, &name_str, -1); + if (g_ascii_strcasecmp (name_str, name) == 0) { + g_free (name_str); + gtk_list_store_remove (priv->completion_model, &iter); + break; + } + g_free (name_str); + } while (gtk_tree_model_iter_next (model, &iter)); + } +} + +static void +map_contact_geocoding_started_cb (EContactMap *map, + ClutterActor *marker, + gpointer user_data) +{ + EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv; + + gtk_spinner_start (GTK_SPINNER (priv->spinner)); + gtk_widget_show (priv->spinner); + + priv->tasks_cnt++; +} + +static void +map_contact_geocoding_failed_cb (EContactMap *map, + const gchar *name, + gpointer user_data) +{ + EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv; + + priv->tasks_cnt--; + + if (priv->tasks_cnt == 0) { + gtk_spinner_stop (GTK_SPINNER (priv->spinner)); + gtk_widget_hide (priv->spinner); + } +} + +static void +contact_map_window_find_contact_cb (GtkButton *button, + gpointer user_data) +{ + EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv; + ClutterActor *marker; + + marker = g_hash_table_lookup ( + priv->hash_table, + gtk_entry_get_text (GTK_ENTRY (priv->search_entry))); + + if (marker) + e_contact_map_zoom_on_marker (priv->map, marker); +} + +static gboolean +contact_map_window_entry_key_pressed_cb (GtkWidget *entry, + GdkEventKey *event, + gpointer user_data) +{ + if (event->keyval == GDK_KEY_Return) + contact_map_window_find_contact_cb (NULL, user_data); + + return FALSE; +} + +static gboolean +entry_completion_match_selected_cb (GtkEntryCompletion *widget, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + GValue name_val = {0}; + EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv; + const gchar *name; + + gtk_tree_model_get_value (model, iter, 0, &name_val); + g_return_val_if_fail (G_VALUE_HOLDS_STRING (&name_val), FALSE); + + name = g_value_get_string (&name_val); + gtk_entry_set_text (GTK_ENTRY (priv->search_entry), name); + + contact_map_window_find_contact_cb (NULL, user_data); + + return TRUE; +} + +static void +contact_map_window_finalize (GObject *object) +{ + EContactMapWindowPrivate *priv; + + priv = E_CONTACT_MAP_WINDOW (object)->priv; + + if (priv->hash_table) { + g_hash_table_destroy (priv->hash_table); + priv->hash_table = NULL; + } + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_contact_map_window_parent_class)->finalize (object); +} + +static void +contact_map_window_dispose (GObject *object) +{ + EContactMapWindowPrivate *priv; + + priv = E_CONTACT_MAP_WINDOW (object)->priv; + + if (priv->map) { + gtk_widget_destroy (GTK_WIDGET (priv->map)); + priv->map = NULL; + } + + if (priv->completion_model) { + g_object_unref (priv->completion_model); + priv->completion_model = NULL; + } + + G_OBJECT_CLASS (e_contact_map_window_parent_class)->dispose (object); +} + +static void +e_contact_map_window_class_init (EContactMapWindowClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EContactMapWindowPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = contact_map_window_finalize; + object_class->dispose = contact_map_window_dispose; + + signals[SHOW_CONTACT_EDITOR] = g_signal_new ( + "show-contact-editor", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EContactMapWindowClass, show_contact_editor), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); +} + +static void +e_contact_map_window_init (EContactMapWindow *window) +{ + EContactMapWindowPrivate *priv; + GtkWidget *map; + GtkWidget *button, *entry; + GtkWidget *hbox, *vbox, *viewport; + GtkEntryCompletion *entry_completion; + GtkListStore *completion_model; + ChamplainView *view; + GHashTable *hash_table; + + priv = E_CONTACT_MAP_WINDOW_GET_PRIVATE (window); + window->priv = priv; + + priv->tasks_cnt = 0; + + hash_table = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); + priv->hash_table = hash_table; + + gtk_window_set_title (GTK_WINDOW (window), _("Contacts Map")); + gtk_container_set_border_width (GTK_CONTAINER (window), 12); + gtk_widget_set_size_request (GTK_WIDGET (window), 800, 600); + + /* The map view itself */ + map = e_contact_map_new (); + view = e_contact_map_get_view (E_CONTACT_MAP (map)); + champlain_view_set_zoom_level (view, 2); + priv->map = E_CONTACT_MAP (map); + g_signal_connect ( + view, "notify::zoom-level", + G_CALLBACK (zoom_level_changed_cb), window); + g_signal_connect ( + map, "contact-added", + G_CALLBACK (map_contact_added_cb), window); + g_signal_connect ( + map, "contact-removed", + G_CALLBACK (map_contact_removed_cb), window); + g_signal_connect ( + map, "geocoding-started", + G_CALLBACK (map_contact_geocoding_started_cb), window); + g_signal_connect ( + map, "geocoding-failed", + G_CALLBACK (map_contact_geocoding_failed_cb), window); + + /* HBox container */ + hbox = gtk_hbox_new (FALSE, 7); + + /* Spinner */ + button = gtk_spinner_new (); + gtk_container_add (GTK_CONTAINER (hbox), button); + gtk_widget_hide (button); + priv->spinner = button; + + /* Zoom-in button */ + button = gtk_button_new_from_stock (GTK_STOCK_ZOOM_IN); + g_signal_connect ( + button, "clicked", + G_CALLBACK (contact_map_window_zoom_in_cb), window); + priv->zoom_in_btn = button; + gtk_container_add (GTK_CONTAINER (hbox), button); + + /* Zoom-out button */ + button = gtk_button_new_from_stock (GTK_STOCK_ZOOM_OUT); + g_signal_connect ( + button, "clicked", + G_CALLBACK (contact_map_window_zoom_out_cb), window); + priv->zoom_out_btn = button; + gtk_container_add (GTK_CONTAINER (hbox), button); + + /* Completion model */ + completion_model = gtk_list_store_new (1, G_TYPE_STRING); + priv->completion_model = completion_model; + + /* Entry completion */ + entry_completion = gtk_entry_completion_new (); + gtk_entry_completion_set_model ( + entry_completion, GTK_TREE_MODEL (completion_model)); + gtk_entry_completion_set_text_column (entry_completion, 0); + g_signal_connect ( + entry_completion, "match-selected", + G_CALLBACK (entry_completion_match_selected_cb), window); + + /* Search entry */ + entry = gtk_entry_new (); + gtk_entry_set_completion (GTK_ENTRY (entry), entry_completion); + g_signal_connect ( + entry, "key-press-event", + G_CALLBACK (contact_map_window_entry_key_pressed_cb), window); + window->priv->search_entry = entry; + gtk_container_add (GTK_CONTAINER (hbox), entry); + + /* Search button */ + button = gtk_button_new_from_stock (GTK_STOCK_FIND); + g_signal_connect ( + button, "clicked", + G_CALLBACK (contact_map_window_find_contact_cb), window); + gtk_container_add (GTK_CONTAINER (hbox), button); + + viewport = gtk_frame_new (NULL); + gtk_container_add (GTK_CONTAINER (viewport), map); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (vbox), viewport); + gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + gtk_container_add (GTK_CONTAINER (window), vbox); + + gtk_widget_show_all (vbox); + gtk_widget_hide (priv->spinner); +} + +EContactMapWindow * +e_contact_map_window_new (void) +{ + return g_object_new ( + E_TYPE_CONTACT_MAP_WINDOW, NULL); +} + +/** + * Gets all contacts from @book and puts them + * on the map view + */ +void +e_contact_map_window_load_addressbook (EContactMapWindow *map, + EBookClient *book_client) +{ + EBookQuery *book_query; + gchar *query_string; + + g_return_if_fail (E_IS_CONTACT_MAP_WINDOW (map)); + g_return_if_fail (E_IS_BOOK_CLIENT (book_client)); + + /* Reference book, so that it does not get deleted before the callback is + * involved. The book is unrefed in the callback */ + g_object_ref (book_client); + + book_query = e_book_query_field_exists (E_CONTACT_ADDRESS); + query_string = e_book_query_to_string (book_query); + e_book_query_unref (book_query); + + e_book_client_get_contacts ( + book_client, query_string, NULL, + book_contacts_received_cb, map); + + g_free (query_string); +} + +EContactMap * +e_contact_map_window_get_map (EContactMapWindow *window) +{ + g_return_val_if_fail (E_IS_CONTACT_MAP_WINDOW (window), NULL); + + return window->priv->map; +} + +#endif /* WITH_CONTACT_MAPS */ diff --git a/addressbook/gui/widgets/e-contact-map-window.h b/addressbook/gui/widgets/e-contact-map-window.h new file mode 100644 index 0000000000..aa7bff4208 --- /dev/null +++ b/addressbook/gui/widgets/e-contact-map-window.h @@ -0,0 +1,81 @@ +/* + * e-contact-map-window.h + * + * 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 <http://www.gnu.org/licenses/> + * + * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com> + * + */ + +#ifndef E_CONTACT_MAP_WINDOW_H +#define E_CONTACT_MAP_WINDOW_H + +#ifdef WITH_CONTACT_MAPS + +#include <gtk/gtk.h> + +#include <libebook/libebook.h> + +#include "e-contact-map.h" + +/* Standard GObject macros */ +#define E_TYPE_CONTACT_MAP_WINDOW \ + (e_contact_map_window_get_type ()) +#define E_CONTACT_MAP_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CONTACT_MAP_WINDOW, EContactMapWindow)) +#define E_CONTACT_MAP_WINDOW_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CONTACT_MAP_WINDOW, EContactMapWindowClass)) +#define E_IS_CONTACT_MAP_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CONTACT_MAP_WINDOW)) +#define E_IS_CONTACT_MAP_WINDOW_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CONTACT_MAP_WINDOW)) +#define E_CONTACT_MAP_WINDOW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CONTACT_MAP_WINDOW, EContactMapWindowClass)) + +G_BEGIN_DECLS + +typedef struct _EContactMapWindow EContactMapWindow; +typedef struct _EContactMapWindowClass EContactMapWindowClass; +typedef struct _EContactMapWindowPrivate EContactMapWindowPrivate; + +struct _EContactMapWindow { + GtkWindow parent; + EContactMapWindowPrivate *priv; +}; + +struct _EContactMapWindowClass { + GtkWindowClass parent_class; + + void (*show_contact_editor) (EContactMapWindow *window, + const gchar *contact_uid); +}; + +GType e_contact_map_window_get_type (void) G_GNUC_CONST; +EContactMapWindow * e_contact_map_window_new (void); + +void e_contact_map_window_load_addressbook (EContactMapWindow *window, + EBookClient *book); + +EContactMap * e_contact_map_window_get_map (EContactMapWindow *window); + +G_END_DECLS + +#endif /* WITH_CONTACT_MAPS */ + +#endif /* E_CONTACT_MAP_WINDOW_H */ diff --git a/addressbook/gui/widgets/e-contact-map.c b/addressbook/gui/widgets/e-contact-map.c new file mode 100644 index 0000000000..57e7e55b97 --- /dev/null +++ b/addressbook/gui/widgets/e-contact-map.c @@ -0,0 +1,408 @@ +/* + * e-contact-map.c + * + * 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 <http://www.gnu.org/licenses/> + * + * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef WITH_CONTACT_MAPS + +#include "e-contact-map.h" + +#include <champlain/champlain.h> +#include <champlain-gtk/champlain-gtk.h> +#include <geoclue/geoclue-address.h> +#include <geoclue/geoclue-position.h> +#include <geocode-glib.h> + +#include <clutter/clutter.h> + +#include <string.h> +#include <glib/gi18n.h> +#include <math.h> + +#include "e-util/e-util.h" + +#include "e-contact-marker.h" + +#define E_CONTACT_MAP_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CONTACT_MAP, EContactMapPrivate)) + +typedef struct _AsyncContext AsyncContext; + +struct _EContactMapPrivate { + GHashTable *markers; /* Hash table contact-name -> marker */ + + ChamplainMarkerLayer *marker_layer; +}; + +struct _AsyncContext { + EContactMap *map; + EContactMarker *marker; +}; + +enum { + CONTACT_ADDED, + CONTACT_REMOVED, + GEOCODING_STARTED, + GEOCODING_FAILED, + LAST_SIGNAL +}; + +static gint signals[LAST_SIGNAL] = {0}; + +G_DEFINE_TYPE (EContactMap, e_contact_map, GTK_CHAMPLAIN_TYPE_EMBED) + +static void +async_context_free (AsyncContext *async_context) +{ + if (async_context->map != NULL) + g_object_unref (async_context->map); + + g_slice_free (AsyncContext, async_context); +} + +static void +contact_map_address_resolved_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GHashTable *resolved = NULL; + gpointer marker_ptr; + const gchar *name; + gdouble latitude, longitude; + AsyncContext *async_context = user_data; + ChamplainMarkerLayer *marker_layer; + ChamplainMarker *marker; + GError *error = NULL; + + g_return_if_fail (async_context != NULL); + g_return_if_fail (E_IS_CONTACT_MAP (async_context->map)); + g_return_if_fail (E_IS_CONTACT_MARKER (async_context->marker)); + + marker = CHAMPLAIN_MARKER (async_context->marker); + marker_layer = async_context->map->priv->marker_layer; + + /* If the marker_layer does not exist anymore, the map has + * probably been destroyed before this callback was launched. + * It's not a failure, just silently clean up what was left + * behind and pretend nothing happened. */ + + if (!CHAMPLAIN_IS_MARKER_LAYER (marker_layer)) + goto exit; + + resolved = geocode_object_resolve_finish ( + GEOCODE_OBJECT (source), result, &error); + + if (resolved == NULL || + !geocode_object_get_coords (resolved, &longitude, &latitude)) { + const gchar *name; + if (error) + g_error_free (error); + name = champlain_label_get_text (CHAMPLAIN_LABEL (marker)); + g_signal_emit ( + async_context->map, + signals[GEOCODING_FAILED], 0, name); + goto exit; + } + + /* Move the marker to resolved position */ + champlain_location_set_location ( + CHAMPLAIN_LOCATION (marker), latitude, longitude); + champlain_marker_layer_add_marker (marker_layer, marker); + champlain_marker_set_selected (marker, FALSE); + + /* Store the marker in the hash table. Use it's label as key */ + name = champlain_label_get_text (CHAMPLAIN_LABEL (marker)); + marker_ptr = g_hash_table_lookup ( + async_context->map->priv->markers, name); + if (marker_ptr != NULL) { + g_hash_table_remove (async_context->map->priv->markers, name); + champlain_marker_layer_remove_marker (marker_layer, marker_ptr); + } + g_hash_table_insert ( + async_context->map->priv->markers, + g_strdup (name), marker); + + g_signal_emit ( + async_context->map, + signals[CONTACT_ADDED], 0, marker); + +exit: + async_context_free (async_context); + + if (resolved != NULL) + g_hash_table_unref (resolved); +} + +static void +resolve_marker_position (EContactMap *map, + EContactMarker *marker, + EContactAddress *address) +{ + GeocodeObject *geocoder; + AsyncContext *async_context; + const gchar *key; + + g_return_if_fail (E_IS_CONTACT_MAP (map)); + g_return_if_fail (address != NULL); + + geocoder = geocode_object_new (); + + key = GEOCODE_OBJECT_FIELD_POSTAL; + geocode_object_add (geocoder, key, address->code); + + key = GEOCODE_OBJECT_FIELD_COUNTRY; + geocode_object_add (geocoder, key, address->country); + + key = GEOCODE_OBJECT_FIELD_STATE; + geocode_object_add (geocoder, key, address->region); + + key = GEOCODE_OBJECT_FIELD_CITY; + geocode_object_add (geocoder, key, address->locality); + + key = GEOCODE_OBJECT_FIELD_STREET; + geocode_object_add (geocoder, key, address->street); + + async_context = g_slice_new0 (AsyncContext); + async_context->map = g_object_ref (map); + async_context->marker = marker; + + geocode_object_resolve_async ( + geocoder, NULL, + contact_map_address_resolved_cb, + async_context); + + g_object_unref (geocoder); + + g_signal_emit (map, signals[GEOCODING_STARTED], 0, marker); +} + +static void +contact_map_finalize (GObject *object) +{ + EContactMapPrivate *priv; + + priv = E_CONTACT_MAP (object)->priv; + + if (priv->markers) { + g_hash_table_destroy (priv->markers); + priv->markers = NULL; + } + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_contact_map_parent_class)->finalize (object); +} + +static void +e_contact_map_class_init (EContactMapClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EContactMapPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = contact_map_finalize; + + signals[CONTACT_ADDED] = g_signal_new ( + "contact-added", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EContactMapClass, contact_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + signals[CONTACT_REMOVED] = g_signal_new ( + "contact-removed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EContactMapClass, contact_removed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + + signals[GEOCODING_STARTED] = g_signal_new ( + "geocoding-started", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EContactMapClass, geocoding_started), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + signals[GEOCODING_FAILED] = g_signal_new ( + "geocoding-failed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EContactMapClass, geocoding_failed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); +} + +static void +e_contact_map_init (EContactMap *map) +{ + GHashTable *hash_table; + ChamplainMarkerLayer *layer; + ChamplainView *view; + + map->priv = E_CONTACT_MAP_GET_PRIVATE (map); + + hash_table = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, NULL); + + map->priv->markers = hash_table; + + view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (map)); + /* This feature is somehow broken sometimes, so disable it for now */ + champlain_view_set_zoom_on_double_click (view, FALSE); + layer = champlain_marker_layer_new_full (CHAMPLAIN_SELECTION_SINGLE); + champlain_view_add_layer (view, CHAMPLAIN_LAYER (layer)); + map->priv->marker_layer = layer; +} + +GtkWidget * +e_contact_map_new (void) +{ + return g_object_new ( + E_TYPE_CONTACT_MAP,NULL); +} + +void +e_contact_map_add_contact (EContactMap *map, + EContact *contact) +{ + EContactAddress *address; + EContactPhoto *photo; + const gchar *contact_uid; + gchar *name; + + g_return_if_fail (map && E_IS_CONTACT_MAP (map)); + g_return_if_fail (contact && E_IS_CONTACT (contact)); + + photo = e_contact_get (contact, E_CONTACT_PHOTO); + contact_uid = e_contact_get_const (contact, E_CONTACT_UID); + + address = e_contact_get (contact, E_CONTACT_ADDRESS_HOME); + if (address) { + name = g_strconcat (e_contact_get_const (contact, E_CONTACT_FILE_AS), " (", _("Home"), ")", NULL); + e_contact_map_add_marker (map, name, contact_uid, address, photo); + g_free (name); + e_contact_address_free (address); + } + + address = e_contact_get (contact, E_CONTACT_ADDRESS_WORK); + if (address) { + name = g_strconcat (e_contact_get_const (contact, E_CONTACT_FILE_AS), " (", _("Work"), ")", NULL); + e_contact_map_add_marker (map, name, contact_uid, address, photo); + g_free (name); + e_contact_address_free (address); + } + + if (photo) + e_contact_photo_free (photo); +} + +void +e_contact_map_add_marker (EContactMap *map, + const gchar *name, + const gchar *contact_uid, + EContactAddress *address, + EContactPhoto *photo) +{ + EContactMarker *marker; + + g_return_if_fail (map && E_IS_CONTACT_MAP (map)); + g_return_if_fail (name && *name); + g_return_if_fail (contact_uid && *contact_uid); + g_return_if_fail (address); + + marker = E_CONTACT_MARKER (e_contact_marker_new (name, contact_uid, photo)); + + resolve_marker_position (map, marker, address); +} + +/** + * The \name parameter must match the label of the + * marker (for example "John Smith (work)") + */ +void +e_contact_map_remove_contact (EContactMap *map, + const gchar *name) +{ + ChamplainMarker *marker; + + g_return_if_fail (map && E_IS_CONTACT_MAP (map)); + g_return_if_fail (name && *name); + + marker = g_hash_table_lookup (map->priv->markers, name); + + champlain_marker_layer_remove_marker (map->priv->marker_layer, marker); + + g_hash_table_remove (map->priv->markers, name); + + g_signal_emit (map, signals[CONTACT_REMOVED], 0, name); +} + +void +e_contact_map_remove_marker (EContactMap *map, + ClutterActor *marker) +{ + const gchar *name; + + g_return_if_fail (map && E_IS_CONTACT_MAP (map)); + g_return_if_fail (marker && CLUTTER_IS_ACTOR (marker)); + + name = champlain_label_get_text (CHAMPLAIN_LABEL (marker)); + + e_contact_map_remove_contact (map, name); +} + +void +e_contact_map_zoom_on_marker (EContactMap *map, + ClutterActor *marker) +{ + ChamplainView *view; + gdouble lat, lng; + + g_return_if_fail (map && E_IS_CONTACT_MAP (map)); + g_return_if_fail (marker && CLUTTER_IS_ACTOR (marker)); + + lat = champlain_location_get_latitude (CHAMPLAIN_LOCATION (marker)); + lng = champlain_location_get_longitude (CHAMPLAIN_LOCATION (marker)); + + view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (map)); + + champlain_view_center_on (view, lat, lng); + champlain_view_set_zoom_level (view, 15); +} + +ChamplainView * +e_contact_map_get_view (EContactMap *map) +{ + g_return_val_if_fail (E_IS_CONTACT_MAP (map), NULL); + + return gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (map)); +} + +#endif /* WITH_CONTACT_MAPS */ diff --git a/addressbook/gui/widgets/e-contact-map.h b/addressbook/gui/widgets/e-contact-map.h new file mode 100644 index 0000000000..d9503e2c9b --- /dev/null +++ b/addressbook/gui/widgets/e-contact-map.h @@ -0,0 +1,106 @@ +/* + * e-contact-map.h + * + * 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 <http://www.gnu.org/licenses/> + * + * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com> + * + */ + +#ifndef E_CONTACT_MAP_H +#define E_CONTACT_MAP_H + +#ifdef WITH_CONTACT_MAPS + +#include <gtk/gtk.h> + +#include <champlain/champlain.h> +#include <champlain-gtk/champlain-gtk.h> + +#include <libebook/libebook.h> + +/* Standard GObject macros */ +#define E_TYPE_CONTACT_MAP \ + (e_contact_map_get_type ()) +#define E_CONTACT_MAP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CONTACT_MAP, EContactMap)) +#define E_CONTACT_MAP_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CONTACT_MAP, EContactMapClass)) +#define E_IS_CONTACT_MAP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CONTACT_MAP)) +#define E_IS_CONTACT_MAP_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CONTACT_MAP)) +#define E_CONTACT_MAP_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CONTACT_MAP, EContactMapClass)) + +G_BEGIN_DECLS + +typedef struct _EContactMap EContactMap; +typedef struct _EContactMapClass EContactMapClass; +typedef struct _EContactMapPrivate EContactMapPrivate; + +struct _EContactMap { + GtkChamplainEmbed parent; + EContactMapPrivate *priv; +}; + +struct _EContactMapClass { + GtkWindowClass parent_class; + + void (*contact_added) (EContactMap *map, + ClutterActor *marker); + + void (*contact_removed) (EContactMap *map, + const gchar *name); + + void (*geocoding_started) (EContactMap *map, + ClutterActor *marker); + + void (*geocoding_failed) (EContactMap *map, + const gchar *name); +}; + +GType e_contact_map_get_type (void) G_GNUC_CONST; +GtkWidget * e_contact_map_new (void); + +void e_contact_map_add_contact (EContactMap *map, + EContact *contact); + +void e_contact_map_add_marker (EContactMap *map, + const gchar *name, + const gchar *contact_uid, + EContactAddress *address, + EContactPhoto *photo); + +void e_contact_map_remove_contact (EContactMap *map, + const gchar *name); + +void e_contact_map_remove_marker (EContactMap *map, + ClutterActor *marker); + +void e_contact_map_zoom_on_marker (EContactMap *map, + ClutterActor *marker); + +ChamplainView * e_contact_map_get_view (EContactMap *map); + +G_END_DECLS + +#endif /* WITH_CONTACT_MAPS */ + +#endif diff --git a/addressbook/gui/widgets/e-contact-marker.c b/addressbook/gui/widgets/e-contact-marker.c new file mode 100644 index 0000000000..9ac9417c9f --- /dev/null +++ b/addressbook/gui/widgets/e-contact-marker.c @@ -0,0 +1,624 @@ +/* + * e-contact-marker.c + * + * 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 <http://www.gnu.org/licenses/> + * + * Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.com> + * Copyright (C) 2011 Jiri Techet <techet@gmail.com> + * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com> + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef WITH_CONTACT_MAPS + +#include "e-contact-marker.h" + +#include <champlain/champlain.h> +#include <gtk/gtk.h> +#include <clutter/clutter.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#include <cairo.h> +#include <math.h> +#include <string.h> + +#define E_CONTACT_MARKER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CONTACT_MARKER, EContactMarkerPrivate)) + +G_DEFINE_TYPE (EContactMarker, e_contact_marker, CHAMPLAIN_TYPE_LABEL); + +struct _EContactMarkerPrivate +{ + gchar *contact_uid; + + ClutterActor *image; + ClutterActor *text_actor; + + ClutterActor *shadow; + ClutterActor *background; + + guint total_width; + guint total_height; + + ClutterGroup *content_group; + + guint redraw_id; +}; + +enum { + DOUBLE_CLICKED, + LAST_SIGNAL +}; + +static gint signals[LAST_SIGNAL] = {0}; + +#define DEFAULT_FONT_NAME "Serif 9" + +static ClutterColor DEFAULT_COLOR = { 0x33, 0x33, 0x33, 0xff }; + +#define RADIUS 10 +#define PADDING (RADIUS / 2) + +static gboolean +contact_marker_clicked_cb (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data) +{ + gint click_count = clutter_event_get_click_count (event); + + if (click_count == 2) + g_signal_emit (E_CONTACT_MARKER (actor), signals[DOUBLE_CLICKED], 0); + + return TRUE; +} + +static ClutterActor * +texture_new_from_pixbuf (GdkPixbuf *pixbuf, + GError **error) +{ + ClutterActor *texture = NULL; + const guchar *data; + gboolean has_alpha, success; + gint width, height, rowstride; + ClutterTextureFlags flags = 0; + + data = gdk_pixbuf_get_pixels (pixbuf); + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + + texture = clutter_texture_new (); + success = clutter_texture_set_from_rgb_data ( + CLUTTER_TEXTURE (texture), + data, has_alpha, width, height, rowstride, + (has_alpha ? 4: 3), flags, NULL); + + if (!success) { + clutter_actor_destroy (CLUTTER_ACTOR (texture)); + texture = NULL; + } + + return texture; +} + +static ClutterActor * +contact_photo_to_texture (EContactPhoto *photo) +{ + GdkPixbuf *pixbuf; + + if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) { + GError *error = NULL; + + GdkPixbufLoader *loader = gdk_pixbuf_loader_new (); + gdk_pixbuf_loader_write ( + loader, photo->data.inlined.data, + photo->data.inlined.length, NULL); + gdk_pixbuf_loader_close (loader, NULL); + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + if (pixbuf) + g_object_ref (pixbuf); + g_object_unref (loader); + + if (error) { + g_error_free (error); + return NULL; + } + } else if (photo->type == E_CONTACT_PHOTO_TYPE_URI) { + GError *error = NULL; + + pixbuf = gdk_pixbuf_new_from_file (photo->data.uri, &error); + + if (error) { + g_error_free (error); + return NULL; + } + } else + return NULL; + + if (pixbuf) { + ClutterActor *texture; + GError *error = NULL; + + texture = texture_new_from_pixbuf (pixbuf, &error); + if (error) { + g_error_free (error); + g_object_unref (pixbuf); + return NULL; + } + + g_object_unref (pixbuf); + return texture; + } + + return NULL; +} + +static void +draw_box (cairo_t *cr, + gint width, + gint height, + gint point) +{ + cairo_move_to (cr, RADIUS, 0); + cairo_line_to (cr, width - RADIUS, 0); + cairo_arc (cr, width - RADIUS, RADIUS, RADIUS - 1, 3 * M_PI / 2.0, 0); + cairo_line_to (cr, width, height - RADIUS); + cairo_arc (cr, width - RADIUS, height - RADIUS, RADIUS - 1, 0, M_PI / 2.0); + cairo_line_to (cr, point, height); + cairo_line_to (cr, 0, height + point); + cairo_arc (cr, RADIUS, RADIUS, RADIUS - 1, M_PI, 3 * M_PI / 2.0); + cairo_close_path (cr); +} + +static void +draw_shadow (EContactMarker *marker, + gint width, + gint height, + gint point) +{ + EContactMarkerPrivate *priv = marker->priv; + ClutterActor *shadow = NULL; + cairo_t *cr; + gdouble slope; + gdouble scaling; + gint x; + cairo_matrix_t matrix; + + slope = -0.3; + scaling = 0.65; + x = -40 * slope; + + shadow = clutter_cairo_texture_new (width + x, (height + point)); + cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (shadow)); + + cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); + cairo_paint (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + + cairo_matrix_init (&matrix, 1, 0, slope, scaling, x, 0); + cairo_set_matrix (cr, &matrix); + + draw_box (cr, width, height, point); + + cairo_set_source_rgba (cr, 0, 0, 0, 0.15); + cairo_fill (cr); + + cairo_destroy (cr); + + clutter_actor_set_position (shadow, 0, height / 2.0); + + clutter_container_add_actor (CLUTTER_CONTAINER (priv->content_group), shadow); + + if (priv->shadow != NULL) { + clutter_container_remove_actor ( + CLUTTER_CONTAINER (priv->content_group), + priv->shadow); + } + + priv->shadow = shadow; +} + +static void +draw_background (EContactMarker *marker, + gint width, + gint height, + gint point) +{ + EContactMarkerPrivate *priv = marker->priv; + ClutterActor *bg = NULL; + const ClutterColor *color; + ClutterColor darker_color; + cairo_t *cr; + + bg = clutter_cairo_texture_new (width, height + point); + cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (bg)); + + cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); + cairo_paint (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + + /* If selected, add the selection color to the marker's color */ + if (champlain_marker_get_selected (CHAMPLAIN_MARKER (marker))) + color = champlain_marker_get_selection_color (); + else + color = &DEFAULT_COLOR; + + draw_box (cr, width, height, point); + + clutter_color_darken (color, &darker_color); + + cairo_set_source_rgba ( + cr, + color->red / 255.0, + color->green / 255.0, + color->blue / 255.0, + color->alpha / 255.0); + cairo_fill_preserve (cr); + + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgba ( + cr, + darker_color.red / 255.0, + darker_color.green / 255.0, + darker_color.blue / 255.0, + darker_color.alpha / 255.0); + cairo_stroke (cr); + cairo_destroy (cr); + + clutter_container_add_actor (CLUTTER_CONTAINER (priv->content_group), bg); + + if (priv->background != NULL) { + clutter_container_remove_actor ( + CLUTTER_CONTAINER (priv->content_group), + priv->background); + } + + priv->background = bg; +} + +static void +draw_marker (EContactMarker *marker) +{ + EContactMarkerPrivate *priv = marker->priv; + ChamplainLabel *label = CHAMPLAIN_LABEL (marker); + guint height = 0, point = 0; + guint total_width = 0, total_height = 0; + ClutterText *text; + + if (priv->image) { + clutter_actor_set_position (priv->image, 2 *PADDING, 2 *PADDING); + if (clutter_actor_get_parent (priv->image) == NULL) + clutter_container_add_actor ( + CLUTTER_CONTAINER (priv->content_group), + priv->image); + } + + if (priv->text_actor == NULL) { + priv->text_actor = clutter_text_new_with_text ( + "Serif 8", + champlain_label_get_text (label)); + champlain_label_set_font_name (label, "Serif 8"); + } + + text = CLUTTER_TEXT (priv->text_actor); + clutter_text_set_text ( + text, + champlain_label_get_text (label)); + clutter_text_set_font_name ( + text, + champlain_label_get_font_name (label)); + clutter_text_set_line_alignment (text, PANGO_ALIGN_CENTER); + clutter_text_set_line_wrap (text, TRUE); + clutter_text_set_line_wrap_mode (text, PANGO_WRAP_WORD); + clutter_text_set_ellipsize ( + text, + champlain_label_get_ellipsize (label)); + clutter_text_set_attributes ( + text, + champlain_label_get_attributes (label)); + clutter_text_set_use_markup ( + text, + champlain_label_get_use_markup (label)); + + if (priv->image) { + clutter_actor_set_width ( + priv->text_actor, + clutter_actor_get_width (priv->image)); + total_height = clutter_actor_get_height (priv->image) + 2 *PADDING + + clutter_actor_get_height (priv->text_actor) + 2 *PADDING; + total_width = clutter_actor_get_width (priv->image) + 4 *PADDING; + clutter_actor_set_position ( + priv->text_actor, PADDING, + clutter_actor_get_height (priv->image) + 2 *PADDING + 3); + } else { + total_height = clutter_actor_get_height (priv->text_actor) + 2 *PADDING; + total_width = clutter_actor_get_width (priv->text_actor) + 4 *PADDING; + clutter_actor_set_position (priv->text_actor, 2 * PADDING, PADDING); + } + + height += 2 * PADDING; + if (height > total_height) + total_height = height; + + clutter_text_set_color ( + CLUTTER_TEXT (priv->text_actor), + (champlain_marker_get_selected (CHAMPLAIN_MARKER (marker)) ? + champlain_marker_get_selection_text_color () : + champlain_label_get_text_color (CHAMPLAIN_LABEL (marker)))); + if (clutter_actor_get_parent (priv->text_actor) == NULL) + clutter_container_add_actor ( + CLUTTER_CONTAINER (priv->content_group), + priv->text_actor); + + if (priv->text_actor == NULL && priv->image == NULL) { + total_width = 6 * PADDING; + total_height = 6 * PADDING; + } + + point = (total_height + 2 * PADDING) / 4.0; + priv->total_width = total_width; + priv->total_height = total_height; + + draw_shadow (marker, total_width, total_height, point); + draw_background (marker, total_width, total_height, point); + + if (priv->text_actor != NULL && priv->background != NULL) + clutter_actor_raise (priv->text_actor, priv->background); + if (priv->image != NULL && priv->background != NULL) + clutter_actor_raise (priv->image, priv->background); + + clutter_actor_set_anchor_point (CLUTTER_ACTOR (marker), 0, total_height + point); +} + +static gboolean +redraw_on_idle (gpointer gobject) +{ + EContactMarker *marker = E_CONTACT_MARKER (gobject); + + draw_marker (marker); + marker->priv->redraw_id = 0; + return FALSE; +} + +static void +queue_redraw (EContactMarker *marker) +{ + EContactMarkerPrivate *priv = marker->priv; + + if (!priv->redraw_id) { + priv->redraw_id = g_idle_add_full ( + G_PRIORITY_DEFAULT, + (GSourceFunc) redraw_on_idle, + g_object_ref (marker), + (GDestroyNotify) g_object_unref); + } +} + +static void +allocate (ClutterActor *self, + const ClutterActorBox *box, + ClutterAllocationFlags flags) +{ + ClutterActorBox child_box; + EContactMarkerPrivate *priv = E_CONTACT_MARKER (self)->priv; + + CLUTTER_ACTOR_CLASS (e_contact_marker_parent_class)->allocate (self, box, flags); + + child_box.x1 = 0; + child_box.x2 = box->x2 - box->x1; + child_box.y1 = 0; + child_box.y2 = box->y2 - box->y1; + clutter_actor_allocate (CLUTTER_ACTOR (priv->content_group), &child_box, flags); +} + +static void +paint (ClutterActor *self) +{ + EContactMarkerPrivate *priv = E_CONTACT_MARKER (self)->priv; + + clutter_actor_paint (CLUTTER_ACTOR (priv->content_group)); +} + +static void +map (ClutterActor *self) +{ + EContactMarkerPrivate *priv = E_CONTACT_MARKER (self)->priv; + + CLUTTER_ACTOR_CLASS (e_contact_marker_parent_class)->map (self); + + clutter_actor_map (CLUTTER_ACTOR (priv->content_group)); +} + +static void +unmap (ClutterActor *self) +{ + EContactMarkerPrivate *priv = E_CONTACT_MARKER (self)->priv; + + CLUTTER_ACTOR_CLASS (e_contact_marker_parent_class)->unmap (self); + + clutter_actor_unmap (CLUTTER_ACTOR (priv->content_group)); +} + +static void +pick (ClutterActor *self, + const ClutterColor *color) +{ + EContactMarkerPrivate *priv = E_CONTACT_MARKER (self)->priv; + gfloat width, height; + + if (!clutter_actor_should_pick_paint (self)) + return; + + width = priv->total_width; + height = priv->total_height; + + cogl_path_new (); + + cogl_set_source_color4ub ( + color->red, + color->green, + color->blue, + color->alpha); + + cogl_path_move_to (RADIUS, 0); + cogl_path_line_to (width - RADIUS, 0); + cogl_path_arc (width - RADIUS, RADIUS, RADIUS, RADIUS, -90, 0); + cogl_path_line_to (width, height - RADIUS); + cogl_path_arc (width - RADIUS, height - RADIUS, RADIUS, RADIUS, 0, 90); + cogl_path_line_to (RADIUS, height); + cogl_path_arc (RADIUS, height - RADIUS, RADIUS, RADIUS, 90, 180); + cogl_path_line_to (0, RADIUS); + cogl_path_arc (RADIUS, RADIUS, RADIUS, RADIUS, 180, 270); + cogl_path_close (); + cogl_path_fill (); +} + +static void +notify_selected (GObject *gobject, + G_GNUC_UNUSED GParamSpec *pspec, + G_GNUC_UNUSED gpointer user_data) +{ + queue_redraw (E_CONTACT_MARKER (gobject)); +} + +static void +e_contact_marker_finalize (GObject *object) +{ + EContactMarkerPrivate *priv = E_CONTACT_MARKER (object)->priv; + + if (priv->contact_uid) { + g_free (priv->contact_uid); + priv->contact_uid = NULL; + } + + if (priv->redraw_id) { + g_source_remove (priv->redraw_id); + priv->redraw_id = 0; + } + + G_OBJECT_CLASS (e_contact_marker_parent_class)->finalize (object); +} + +static void +e_contact_marker_dispose (GObject *object) +{ + EContactMarkerPrivate *priv = E_CONTACT_MARKER (object)->priv; + + priv->background = NULL; + priv->shadow = NULL; + priv->text_actor = NULL; + + if (priv->content_group) { + clutter_actor_unparent (CLUTTER_ACTOR (priv->content_group)); + priv->content_group = NULL; + } + + G_OBJECT_CLASS (e_contact_marker_parent_class)->dispose (object); +} + +static void +e_contact_marker_class_init (EContactMarkerClass *class) +{ + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (EContactMarkerPrivate)); + + object_class->dispose = e_contact_marker_dispose; + object_class->finalize = e_contact_marker_finalize; + + actor_class->paint = paint; + actor_class->allocate = allocate; + actor_class->map = map; + actor_class->unmap = unmap; + actor_class->pick = pick; + + signals[DOUBLE_CLICKED] = g_signal_new ( + "double-clicked", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EContactMarkerClass, double_clicked), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +e_contact_marker_init (EContactMarker *marker) +{ + EContactMarkerPrivate *priv; + + priv = E_CONTACT_MARKER_GET_PRIVATE (marker); + + marker->priv = priv; + priv->contact_uid = NULL; + priv->image = NULL; + priv->background = NULL; + priv->shadow = NULL; + priv->text_actor = NULL; + priv->content_group = CLUTTER_GROUP (clutter_group_new ()); + priv->redraw_id = 0; + + clutter_actor_set_parent ( + CLUTTER_ACTOR (priv->content_group), CLUTTER_ACTOR (marker)); + clutter_actor_queue_relayout (CLUTTER_ACTOR (marker)); + + priv->total_width = 0; + priv->total_height = 0; + + g_signal_connect ( + marker, "notify::selected", + G_CALLBACK (notify_selected), NULL); + g_signal_connect ( + marker, "button-release-event", + G_CALLBACK (contact_marker_clicked_cb), NULL); +} + +ClutterActor * +e_contact_marker_new (const gchar *name, + const gchar *contact_uid, + EContactPhoto *photo) +{ + ClutterActor *marker = CLUTTER_ACTOR (g_object_new (E_TYPE_CONTACT_MARKER, NULL)); + EContactMarkerPrivate *priv = E_CONTACT_MARKER (marker)->priv; + + g_return_val_if_fail (name && *name, NULL); + g_return_val_if_fail (contact_uid && *contact_uid, NULL); + + champlain_label_set_text (CHAMPLAIN_LABEL (marker), name); + priv->contact_uid = g_strdup (contact_uid); + if (photo) + priv->image = contact_photo_to_texture (photo); + + queue_redraw (E_CONTACT_MARKER (marker)); + + return marker; +} + +const gchar * +e_contact_marker_get_contact_uid (EContactMarker *marker) +{ + g_return_val_if_fail (marker && E_IS_CONTACT_MARKER (marker), NULL); + + return marker->priv->contact_uid; +} + +#endif /* WITH_CONTACT_MAPS */ diff --git a/addressbook/gui/widgets/e-contact-marker.h b/addressbook/gui/widgets/e-contact-marker.h new file mode 100644 index 0000000000..791a9c46b5 --- /dev/null +++ b/addressbook/gui/widgets/e-contact-marker.h @@ -0,0 +1,84 @@ +/* + * e-contact-marker.h + * + * 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 <http://www.gnu.org/licenses/> + * + * Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.com> + * Copyright (C) 2011 Jiri Techet <techet@gmail.com> + * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com> + * + */ + +#ifndef E_CONTACT_MARKER_H +#define E_CONTACT_MARKER_H + +#ifdef WITH_CONTACT_MAPS + +#include <libebook/libebook.h> + +#include <champlain/champlain.h> + +#include <glib-object.h> +#include <clutter/clutter.h> + +G_BEGIN_DECLS + +#define E_TYPE_CONTACT_MARKER e_contact_marker_get_type () + +#define E_CONTACT_MARKER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CONTACT_MARKER, EContactMarker)) + +#define E_CONTACT_MARKER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CONTACT_MARKER, EContactMarkerClass)) + +#define E_IS_CONTACT_MARKER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CONTACT_MARKER)) + +#define E_IS_CONTACT_MARKER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CONTACT_MARKER)) + +#define E_CONTACT_MARKER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_CONTACT_MARKER, EContactMarkerClass)) + +typedef struct _EContactMarkerPrivate EContactMarkerPrivate; + +typedef struct _EContactMarker EContactMarker; +typedef struct _EContactMarkerClass EContactMarkerClass; + +struct _EContactMarker +{ + ChamplainLabel parent; + EContactMarkerPrivate *priv; +}; + +struct _EContactMarkerClass +{ + ChamplainLabelClass parent_class; + + void (*double_clicked) (ClutterActor *actor); +}; + +GType e_contact_marker_get_type (void); + +ClutterActor * e_contact_marker_new (const gchar *name, + const gchar *contact_uid, + EContactPhoto *photo); + +const gchar * e_contact_marker_get_contact_uid (EContactMarker *marker); + +G_END_DECLS + +#endif /* WITH_CONTACT_MAPS */ + +#endif diff --git a/addressbook/gui/widgets/eab-contact-display.c b/addressbook/gui/widgets/eab-contact-display.c index 549278b994..e71ea23b9f 100644 --- a/addressbook/gui/widgets/eab-contact-display.c +++ b/addressbook/gui/widgets/eab-contact-display.c @@ -31,10 +31,8 @@ #include <webkit/webkit.h> -#include "e-util/e-util.h" - +#include "e-contact-map.h" #include "eab-contact-formatter.h" - #include "eab-gui-util.h" #define EAB_CONTACT_DISPLAY_GET_PRIVATE(obj) \ diff --git a/addressbook/importers/Makefile.am b/addressbook/importers/Makefile.am index cd509f9eb6..6345d29300 100644 --- a/addressbook/importers/Makefile.am +++ b/addressbook/importers/Makefile.am @@ -10,7 +10,6 @@ libevolution_addressbook_importers_la_CPPFLAGS = \ -I$(top_builddir)/addressbook \ $(EVOLUTION_DATA_SERVER_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ - $(CHAMPLAIN_CFLAGS) \ $(GTKHTML_CFLAGS) libevolution_addressbook_importers_la_SOURCES = \ @@ -27,7 +26,6 @@ libevolution_addressbook_importers_la_LIBADD = \ $(top_builddir)/addressbook/util/libeabutil.la \ $(EVOLUTION_DATA_SERVER_LIBS) \ $(GNOME_PLATFORM_LIBS) \ - $(CHAMPLAIN_LIBS) \ $(GTKHTML_LIBS) \ $(IMPORTERS_LIBS) diff --git a/addressbook/printing/Makefile.am b/addressbook/printing/Makefile.am index fa92d19676..8b54d3817d 100644 --- a/addressbook/printing/Makefile.am +++ b/addressbook/printing/Makefile.am @@ -13,7 +13,6 @@ libecontactprint_la_CPPFLAGS = \ -DEVOLUTION_ECPSDIR=\""$(ecpsdir)"\" \ $(EVOLUTION_DATA_SERVER_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ - $(CHAMPLAIN_CFLAGS) \ $(GTKHTML_CFLAGS) noinst_LTLIBRARIES = libecontactprint.la @@ -27,7 +26,6 @@ libecontactprint_la_LIBADD = \ $(top_builddir)/e-util/libeutil.la \ $(EVOLUTION_DATA_SERVER_LIBS) \ $(GNOME_PLATFORM_LIBS) \ - $(CHAMPLAIN_LIBS) \ $(GTKHTML_LIBS) noinst_PROGRAMS = contact-print-test |