evolution-3.6.4/widgets/misc/e-contact-map.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-contact-map.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-contact-map.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /*
  2  * e-contact-map.c
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com>
 18  */
 19 
 20 #ifdef HAVE_CONFIG_H
 21 #include <config.h>
 22 #endif
 23 
 24 #ifdef WITH_CONTACT_MAPS
 25 
 26 #include "e-contact-map.h"
 27 #include "e-contact-marker.h"
 28 
 29 #include <e-util/e-marshal.h>
 30 
 31 #include <champlain/champlain.h>
 32 #include <champlain-gtk/champlain-gtk.h>
 33 #include <geoclue/geoclue-address.h>
 34 #include <geoclue/geoclue-position.h>
 35 #include <geoclue/geoclue-geocode.h>
 36 
 37 #include <clutter/clutter.h>
 38 
 39 #include <string.h>
 40 #include <glib/gi18n.h>
 41 #include <math.h>
 42 
 43 #define E_CONTACT_MAP_GET_PRIVATE(obj) \
 44         (G_TYPE_INSTANCE_GET_PRIVATE \
 45         ((obj), E_TYPE_CONTACT_MAP, EContactMapPrivate))
 46 
 47 G_DEFINE_TYPE (EContactMap, e_contact_map, GTK_CHAMPLAIN_TYPE_EMBED)
 48 
 49 struct _EContactMapPrivate {
 50 	GHashTable *markers; /* Hash table contact-name -> marker */
 51 
 52 	ChamplainMarkerLayer *marker_layer;
 53 };
 54 
 55 struct GeoclueCallbackData {
 56 	EContactMap *map;
 57 	EContactMarker *marker;
 58 };
 59 
 60 enum {
 61 	CONTACT_ADDED,
 62 	CONTACT_REMOVED,
 63 	GEOCODING_STARTED,
 64 	GEOCODING_FAILED,
 65 	LAST_SIGNAL
 66 };
 67 
 68 static gint signals[LAST_SIGNAL] = {0};
 69 
 70 static GHashTable *
 71 contact_map_geocode_address (EContactAddress *address)
 72 {
 73 	GHashTable *details;
 74 
 75 	g_return_val_if_fail (address, NULL);
 76 
 77 	details = geoclue_address_details_new ();
 78 	g_hash_table_insert (details, g_strdup (GEOCLUE_ADDRESS_KEY_POSTALCODE), g_strdup (address->code));
 79 	g_hash_table_insert (details, g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRY), g_strdup (address->country));
 80 	g_hash_table_insert (details, g_strdup (GEOCLUE_ADDRESS_KEY_REGION), g_strdup (address->region));
 81 	g_hash_table_insert (details, g_strdup (GEOCLUE_ADDRESS_KEY_LOCALITY), g_strdup (address->locality));
 82 	g_hash_table_insert (details, g_strdup (GEOCLUE_ADDRESS_KEY_STREET), g_strdup (address->street));
 83 
 84 	return details;
 85 }
 86 
 87 static void
 88 contact_map_address_resolved_cb (GeoclueGeocode *geocode,
 89                                  GeocluePositionFields fields,
 90                                  double latitude,
 91                                  double longitude,
 92                                  double altitude,
 93                                  GeoclueAccuracy *accuracy,
 94                                  GError *error,
 95                                  struct GeoclueCallbackData *data)
 96 {
 97 	EContactMapPrivate *priv;
 98 	gpointer marker_ptr;
 99 	const gchar *name;
100 
101 	g_return_if_fail (data);
102 	g_return_if_fail (data->map && E_IS_CONTACT_MAP (data->map));
103 	g_return_if_fail (data->map->priv);
104 	g_return_if_fail (data->marker && E_IS_CONTACT_MARKER (data->marker));
105 
106 	/* If the marker_layer does not exist anymore, the map has probably been destroyed before this
107 	 * callback was launched. It's not a failure, just silently clean up what was left behind
108 	 * a pretend nothing happend */
109 
110 	if (!data->map->priv->marker_layer || !CHAMPLAIN_IS_MARKER_LAYER (data->map->priv->marker_layer)) {
111 		goto exit;
112 	}
113 
114 	if (error ||
115 	    (((fields & GEOCLUE_POSITION_FIELDS_LATITUDE) == 0) && ((fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) == 0))) {
116 		const gchar *name;
117 		if (error)
118 			g_error_free (error);
119 		name = champlain_label_get_text (CHAMPLAIN_LABEL (data->marker));
120 		g_signal_emit (data->map, signals[GEOCODING_FAILED], 0, name);
121 		goto exit;
122 	}
123 
124 	priv = data->map->priv;
125 
126 	/* Move the marker to resolved position */
127 	champlain_location_set_location (
128 		CHAMPLAIN_LOCATION (data->marker),
129 		latitude, longitude);
130 	champlain_marker_layer_add_marker (
131 		data->map->priv->marker_layer,
132 		CHAMPLAIN_MARKER (data->marker));
133 	champlain_marker_set_selected (CHAMPLAIN_MARKER (data->marker), FALSE);
134 
135 	/* Store the marker in the hash table. Use it's label as key */
136 	name = champlain_label_get_text (CHAMPLAIN_LABEL (data->marker));
137 	marker_ptr = g_hash_table_lookup (priv->markers, name);
138 
139 	if (marker_ptr) {
140 		g_hash_table_remove (priv->markers, name);
141 		champlain_marker_layer_remove_marker (priv->marker_layer, marker_ptr);
142 	}
143 	g_hash_table_insert (
144 		priv->markers,
145 		g_strdup (name), data->marker);
146 
147 	g_signal_emit (data->map, signals[CONTACT_ADDED], 0, data->marker);
148 
149 exit:
150 	g_object_unref (data->map);
151 	g_free (data);
152 
153 	if (geocode)
154 		g_object_unref (geocode);
155 }
156 
157 static void
158 resolve_marker_position (EContactMap *map,
159                          EContactMarker *marker,
160                          EContactAddress *address)
161 {
162 	GHashTable *details;
163 
164 	g_return_if_fail (map && E_IS_CONTACT_MAP (map));
165 	details = contact_map_geocode_address (address);
166 
167 	if (details) {
168 		GeoclueGeocode *geocoder;
169 		struct GeoclueCallbackData *callback_data = g_new0 (struct GeoclueCallbackData, 1);
170 
171 		callback_data->map = map;
172 		callback_data->marker = marker;
173 
174 		/* Make sure the map won't cease to exist before the address
175 		 * is resolved */
176 		g_object_ref (map);
177 
178 		geocoder = geoclue_geocode_new (
179 			"org.freedesktop.Geoclue.Providers.Nominatim",
180 			"/org/freedesktop/Geoclue/Providers/Nominatim");
181 
182 		geoclue_geocode_address_to_position_async (
183 			geocoder, details,
184 			(GeoclueGeocodeCallback) contact_map_address_resolved_cb,
185 			callback_data);
186 
187 		g_hash_table_destroy (details);
188 
189 		g_signal_emit (map, signals[GEOCODING_STARTED], 0, marker);
190 	}
191 }
192 
193 static void
194 contact_map_finalize (GObject *object)
195 {
196 	EContactMapPrivate *priv;
197 
198 	priv = E_CONTACT_MAP (object)->priv;
199 
200 	if (priv->markers) {
201 		g_hash_table_destroy (priv->markers);
202 		priv->markers = NULL;
203 	}
204 
205 	/* Chain up to parent's finalize() method. */
206 	G_OBJECT_CLASS (e_contact_map_parent_class)->finalize (object);
207 }
208 
209 static void
210 e_contact_map_class_init (EContactMapClass *class)
211 {
212 	GObjectClass *object_class;
213 
214 	g_type_class_add_private (class, sizeof (EContactMapPrivate));
215 
216 	object_class = G_OBJECT_CLASS (class);
217 	object_class->finalize = contact_map_finalize;
218 
219 	signals[CONTACT_ADDED] = g_signal_new (
220 		"contact-added",
221 		G_TYPE_FROM_CLASS (class),
222 		G_SIGNAL_RUN_FIRST,
223 		G_STRUCT_OFFSET (EContactMapClass, contact_added),
224 		NULL, NULL,
225 		g_cclosure_marshal_VOID__OBJECT,
226 		G_TYPE_NONE, 1, G_TYPE_OBJECT);
227 
228 	signals[CONTACT_REMOVED] = g_signal_new (
229 		"contact-removed",
230 		G_TYPE_FROM_CLASS (class),
231 		G_SIGNAL_RUN_FIRST,
232 		G_STRUCT_OFFSET (EContactMapClass, contact_removed),
233 		NULL, NULL,
234 		g_cclosure_marshal_VOID__STRING,
235 		G_TYPE_NONE, 1, G_TYPE_STRING);
236 
237 	signals[GEOCODING_STARTED] = g_signal_new (
238 		"geocoding-started",
239 		G_TYPE_FROM_CLASS (class),
240 		G_SIGNAL_RUN_FIRST,
241 		G_STRUCT_OFFSET (EContactMapClass, geocoding_started),
242 		NULL, NULL,
243 		g_cclosure_marshal_VOID__OBJECT,
244 		G_TYPE_NONE, 1, G_TYPE_OBJECT);
245 
246 	signals[GEOCODING_FAILED] = g_signal_new (
247 		"geocoding-failed",
248 		G_TYPE_FROM_CLASS (class),
249 		G_SIGNAL_RUN_FIRST,
250 		G_STRUCT_OFFSET (EContactMapClass, geocoding_failed),
251 		NULL, NULL,
252 		g_cclosure_marshal_VOID__STRING,
253 		G_TYPE_NONE, 1, G_TYPE_STRING);
254 }
255 
256 static void
257 e_contact_map_init (EContactMap *map)
258 {
259 	GHashTable *hash_table;
260 	ChamplainMarkerLayer *layer;
261 	ChamplainView *view;
262 
263 	map->priv = E_CONTACT_MAP_GET_PRIVATE (map);
264 
265 	hash_table = g_hash_table_new_full (
266 		g_str_hash, g_str_equal,
267 			(GDestroyNotify) g_free, NULL);
268 
269 	map->priv->markers = hash_table;
270 
271 	view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (map));
272 	/* This feature is somehow broken sometimes, so disable it for now */
273 	champlain_view_set_zoom_on_double_click (view, FALSE);
274 	layer = champlain_marker_layer_new_full (CHAMPLAIN_SELECTION_SINGLE);
275 	champlain_view_add_layer (view, CHAMPLAIN_LAYER (layer));
276 	map->priv->marker_layer = layer;
277 }
278 
279 GtkWidget *
280 e_contact_map_new (void)
281 {
282 	return g_object_new (
283 		E_TYPE_CONTACT_MAP,NULL);
284 }
285 
286 void
287 e_contact_map_add_contact (EContactMap *map,
288                            EContact *contact)
289 {
290 	EContactAddress *address;
291 	EContactPhoto *photo;
292 	const gchar *contact_uid;
293 	gchar *name;
294 
295 	g_return_if_fail (map && E_IS_CONTACT_MAP (map));
296 	g_return_if_fail (contact && E_IS_CONTACT (contact));
297 
298 	photo = e_contact_get (contact, E_CONTACT_PHOTO);
299 	contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
300 
301 	address = e_contact_get (contact, E_CONTACT_ADDRESS_HOME);
302 	if (address) {
303 		name = g_strconcat (e_contact_get_const (contact, E_CONTACT_FILE_AS), " (", _("Home"), ")", NULL);
304 		e_contact_map_add_marker (map, name, contact_uid, address, photo);
305 		g_free (name);
306 		e_contact_address_free (address);
307 	}
308 
309 	address = e_contact_get (contact, E_CONTACT_ADDRESS_WORK);
310 	if (address) {
311 		name = g_strconcat (e_contact_get_const (contact, E_CONTACT_FILE_AS), " (", _("Work"), ")", NULL);
312 		e_contact_map_add_marker (map, name, contact_uid, address, photo);
313 		g_free (name);
314 		e_contact_address_free (address);
315 	}
316 
317 	if (photo)
318 		e_contact_photo_free (photo);
319 }
320 
321 void
322 e_contact_map_add_marker (EContactMap *map,
323                           const gchar *name,
324                           const gchar *contact_uid,
325                           EContactAddress *address,
326                           EContactPhoto *photo)
327 {
328 	EContactMarker *marker;
329 
330 	g_return_if_fail (map && E_IS_CONTACT_MAP (map));
331 	g_return_if_fail (name && *name);
332 	g_return_if_fail (contact_uid && *contact_uid);
333 	g_return_if_fail (address);
334 
335 	marker = E_CONTACT_MARKER (e_contact_marker_new (name, contact_uid, photo));
336 
337 	resolve_marker_position (map, marker, address);
338 }
339 
340 /**
341  * The \name parameter must match the label of the
342  * marker (for example "John Smith (work)")
343  */
344 void
345 e_contact_map_remove_contact (EContactMap *map,
346                               const gchar *name)
347 {
348 	ChamplainMarker *marker;
349 
350 	g_return_if_fail (map && E_IS_CONTACT_MAP (map));
351 	g_return_if_fail (name && *name);
352 
353 	marker = g_hash_table_lookup (map->priv->markers, name);
354 
355 	champlain_marker_layer_remove_marker (map->priv->marker_layer, marker);
356 
357 	g_hash_table_remove (map->priv->markers, name);
358 
359 	g_signal_emit (map, signals[CONTACT_REMOVED], 0, name);
360 }
361 
362 void
363 e_contact_map_remove_marker (EContactMap *map,
364                              ClutterActor *marker)
365 {
366 	const gchar *name;
367 
368 	g_return_if_fail (map && E_IS_CONTACT_MAP (map));
369 	g_return_if_fail (marker && CLUTTER_IS_ACTOR (marker));
370 
371 	name = champlain_label_get_text (CHAMPLAIN_LABEL (marker));
372 
373 	e_contact_map_remove_contact (map, name);
374 }
375 
376 void
377 e_contact_map_zoom_on_marker (EContactMap *map,
378                               ClutterActor *marker)
379 {
380 	ChamplainView *view;
381 	gdouble lat, lng;
382 
383 	g_return_if_fail (map && E_IS_CONTACT_MAP (map));
384 	g_return_if_fail (marker && CLUTTER_IS_ACTOR (marker));
385 
386 	lat = champlain_location_get_latitude (CHAMPLAIN_LOCATION (marker));
387 	lng = champlain_location_get_longitude (CHAMPLAIN_LOCATION (marker));
388 
389 	view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (map));
390 
391 	champlain_view_center_on (view, lat, lng);
392 	champlain_view_set_zoom_level (view, 15);
393 }
394 
395 ChamplainView *
396 e_contact_map_get_view (EContactMap *map)
397 {
398 	g_return_val_if_fail (E_IS_CONTACT_MAP (map), NULL);
399 
400 	return gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (map));
401 }
402 
403 #endif /* WITH_CONTACT_MAPS */