No issues found
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 |
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 */