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

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-contact-map-window.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-contact-map-window.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-window.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 
 21 #ifdef HAVE_CONFIG_H
 22 #include <config.h>
 23 #endif
 24 
 25 #ifdef WITH_CONTACT_MAPS
 26 
 27 #include "e-contact-map.h"
 28 #include "e-contact-map-window.h"
 29 #include "e-contact-marker.h"
 30 
 31 #include <champlain/champlain.h>
 32 
 33 #include <string.h>
 34 
 35 #include <glib/gi18n.h>
 36 #include <glib-object.h>
 37 
 38 #define E_CONTACT_MAP_WINDOW_GET_PRIVATE(obj) \
 39         (G_TYPE_INSTANCE_GET_PRIVATE \
 40         ((obj), E_TYPE_CONTACT_MAP_WINDOW, EContactMapWindowPrivate))
 41 
 42 G_DEFINE_TYPE (EContactMapWindow, e_contact_map_window, GTK_TYPE_WINDOW)
 43 
 44 struct _EContactMapWindowPrivate {
 45 	EContactMap *map;
 46 
 47 	GtkWidget *zoom_in_btn;
 48 	GtkWidget *zoom_out_btn;
 49 
 50 	GtkWidget *search_entry;
 51 	GtkListStore *completion_model;
 52 
 53 	GHashTable *hash_table;		/* Hash table contact-name -> marker */
 54 
 55 	GtkWidget *spinner;
 56 	gint tasks_cnt;
 57 };
 58 
 59 enum {
 60 	SHOW_CONTACT_EDITOR,
 61 	LAST_SIGNAL
 62 };
 63 
 64 static gint signals[LAST_SIGNAL] = {0};
 65 
 66 static void
 67 marker_doubleclick_cb (ClutterActor *actor,
 68                        gpointer user_data)
 69 {
 70 	EContactMapWindow *window = user_data;
 71 	EContactMarker *marker;
 72 	const gchar *contact_uid;
 73 
 74 	marker = E_CONTACT_MARKER (actor);
 75 	contact_uid = e_contact_marker_get_contact_uid (marker);
 76 
 77 	g_signal_emit (window, signals[SHOW_CONTACT_EDITOR], 0, contact_uid);
 78 }
 79 
 80 static void
 81 book_contacts_received_cb (GObject *source_object,
 82                            GAsyncResult *result,
 83                            gpointer user_data)
 84 {
 85 	EContactMapWindow *window = user_data;
 86 	EBookClient *client = E_BOOK_CLIENT (source_object);
 87 	GSList *contacts = NULL, *p;
 88 	GError *error = NULL;
 89 
 90 	if (!e_book_client_get_contacts_finish (client, result, &contacts, &error))
 91 		contacts = NULL;
 92 
 93 	if (error != NULL) {
 94 		g_warning (
 95 			"%s: Failed to get contacts: %s",
 96 			G_STRFUNC, error->message);
 97 		g_error_free (error);
 98 	}
 99 
100 	for (p = contacts; p; p = p->next)
101 		e_contact_map_add_contact (
102 			window->priv->map, (EContact *) p->data);
103 
104 	e_client_util_free_object_slist (contacts);
105 	g_object_unref (client);
106 }
107 
108 static void
109 contact_map_window_zoom_in_cb (GtkButton *button,
110                                gpointer user_data)
111 {
112 	EContactMapWindow *window = user_data;
113 	ChamplainView *view;
114 
115 	view = e_contact_map_get_view (window->priv->map);
116 
117 	champlain_view_zoom_in (view);
118 }
119 
120 static void
121 contact_map_window_zoom_out_cb (GtkButton *button,
122                                 gpointer user_data)
123 {
124 	EContactMapWindow *window = user_data;
125 	ChamplainView *view;
126 
127 	view = e_contact_map_get_view (window->priv->map);
128 
129 	champlain_view_zoom_out (view);
130 }
131 static void
132 zoom_level_changed_cb (ChamplainView *view,
133                        GParamSpec *pspec,
134                        gpointer user_data)
135 {
136 	EContactMapWindow *window = user_data;
137 	gint zoom_level = champlain_view_get_zoom_level (view);
138 
139 	gtk_widget_set_sensitive (
140 		window->priv->zoom_in_btn,
141 		(zoom_level < champlain_view_get_max_zoom_level (view)));
142 
143 	gtk_widget_set_sensitive (
144 		window->priv->zoom_out_btn,
145 		(zoom_level > champlain_view_get_min_zoom_level (view)));
146 }
147 
148 /**
149  * Add contact to hash_table only when EContactMap tells us
150  * that the contact has really been added to map.
151  */
152 static void
153 map_contact_added_cb (EContactMap *map,
154                       ClutterActor *marker,
155                       gpointer user_data)
156 {
157 	EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv;
158 	const gchar *name;
159 	GtkTreeIter iter;
160 
161 	name = champlain_label_get_text (CHAMPLAIN_LABEL (marker));
162 
163 	g_hash_table_insert (
164 		priv->hash_table,
165 		g_strdup (name), marker);
166 
167 	gtk_list_store_append (priv->completion_model, &iter);
168 	gtk_list_store_set (
169 		priv->completion_model, &iter,
170 		0, name, -1);
171 
172 	g_signal_connect (
173 		marker, "double-clicked",
174 		G_CALLBACK (marker_doubleclick_cb), user_data);
175 
176 	priv->tasks_cnt--;
177 	if (priv->tasks_cnt == 0) {
178 		gtk_spinner_stop (GTK_SPINNER (priv->spinner));
179 		gtk_widget_hide (priv->spinner);
180 	}
181 }
182 
183 static void
184 map_contact_removed_cb (EContactMap *map,
185                         const gchar *name,
186                         gpointer user_data)
187 {
188 	EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv;
189 	GtkTreeIter iter;
190 	GtkTreeModel *model = GTK_TREE_MODEL (priv->completion_model);
191 
192 	g_hash_table_remove (priv->hash_table, name);
193 
194 	if (gtk_tree_model_get_iter_first (model, &iter)) {
195 		do {
196 			gchar *name_str;
197 			gtk_tree_model_get (model, &iter, 0, &name_str, -1);
198 			if (g_ascii_strcasecmp (name_str, name) == 0) {
199 				g_free (name_str);
200 				gtk_list_store_remove (priv->completion_model, &iter);
201 				break;
202 			}
203 			g_free (name_str);
204 		} while (gtk_tree_model_iter_next (model, &iter));
205 	}
206 }
207 
208 static void
209 map_contact_geocoding_started_cb (EContactMap *map,
210                                   ClutterActor *marker,
211                                   gpointer user_data)
212 {
213 	EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv;
214 
215 	gtk_spinner_start (GTK_SPINNER (priv->spinner));
216 	gtk_widget_show (priv->spinner);
217 
218 	priv->tasks_cnt++;
219 }
220 
221 static void
222 map_contact_geocoding_failed_cb (EContactMap *map,
223                                  const gchar *name,
224                                  gpointer user_data)
225 {
226 	EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv;
227 
228 	priv->tasks_cnt--;
229 
230 	if (priv->tasks_cnt == 0) {
231 		gtk_spinner_stop (GTK_SPINNER (priv->spinner));
232 		gtk_widget_hide (priv->spinner);
233 	}
234 }
235 
236 static void
237 contact_map_window_find_contact_cb (GtkButton *button,
238                                     gpointer user_data)
239 {
240 	EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv;
241 	ClutterActor *marker;
242 
243 	marker = g_hash_table_lookup (
244 		priv->hash_table,
245 		gtk_entry_get_text (GTK_ENTRY (priv->search_entry)));
246 
247 	if (marker)
248 		e_contact_map_zoom_on_marker (priv->map, marker);
249 }
250 
251 static gboolean
252 contact_map_window_entry_key_pressed_cb (GtkWidget *entry,
253                                          GdkEventKey *event,
254                                          gpointer user_data)
255 {
256 	if (event->keyval == GDK_KEY_Return)
257 		contact_map_window_find_contact_cb (NULL, user_data);
258 
259 	return FALSE;
260 }
261 
262 static gboolean
263 entry_completion_match_selected_cb (GtkEntryCompletion *widget,
264                                     GtkTreeModel *model,
265                                     GtkTreeIter *iter,
266                                     gpointer user_data)
267 {
268 	GValue name_val = {0};
269 	EContactMapWindowPrivate *priv = E_CONTACT_MAP_WINDOW (user_data)->priv;
270 	const gchar *name;
271 
272 	gtk_tree_model_get_value (model, iter, 0, &name_val);
273 	g_return_val_if_fail (G_VALUE_HOLDS_STRING (&name_val), FALSE);
274 
275 	name = g_value_get_string (&name_val);
276 	gtk_entry_set_text (GTK_ENTRY (priv->search_entry), name);
277 
278 	contact_map_window_find_contact_cb (NULL, user_data);
279 
280 	return TRUE;
281 }
282 
283 static void
284 contact_map_window_finalize (GObject *object)
285 {
286 	EContactMapWindowPrivate *priv;
287 
288 	priv = E_CONTACT_MAP_WINDOW (object)->priv;
289 
290 	if (priv->hash_table) {
291 		g_hash_table_destroy (priv->hash_table);
292 		priv->hash_table = NULL;
293 	}
294 
295 	/* Chain up to parent's finalize() method. */
296 	G_OBJECT_CLASS (e_contact_map_window_parent_class)->finalize (object);
297 }
298 
299 static void
300 contact_map_window_dispose (GObject *object)
301 {
302 	EContactMapWindowPrivate *priv;
303 
304 	priv = E_CONTACT_MAP_WINDOW (object)->priv;
305 
306 	if (priv->map) {
307 		gtk_widget_destroy (GTK_WIDGET (priv->map));
308 		priv->map = NULL;
309 	}
310 
311 	if (priv->completion_model) {
312 		g_object_unref (priv->completion_model);
313 		priv->completion_model = NULL;
314 	}
315 
316 	G_OBJECT_CLASS (e_contact_map_window_parent_class)->dispose (object);
317 }
318 
319 static void
320 e_contact_map_window_class_init (EContactMapWindowClass *class)
321 {
322 	GObjectClass *object_class;
323 
324 	g_type_class_add_private (class, sizeof (EContactMapWindowPrivate));
325 
326 	object_class = G_OBJECT_CLASS (class);
327 	object_class->finalize = contact_map_window_finalize;
328 	object_class->dispose = contact_map_window_dispose;
329 
330 	signals[SHOW_CONTACT_EDITOR] = g_signal_new (
331 		"show-contact-editor",
332 		G_TYPE_FROM_CLASS (class),
333 		G_SIGNAL_RUN_FIRST,
334 		G_STRUCT_OFFSET (EContactMapWindowClass, show_contact_editor),
335 		NULL, NULL,
336 		g_cclosure_marshal_VOID__STRING,
337 		G_TYPE_NONE, 1, G_TYPE_STRING);
338 }
339 
340 static void
341 e_contact_map_window_init (EContactMapWindow *window)
342 {
343 	EContactMapWindowPrivate *priv;
344 	GtkWidget *map;
345 	GtkWidget *button, *entry;
346 	GtkWidget *hbox, *vbox, *viewport;
347 	GtkEntryCompletion *entry_completion;
348 	GtkListStore *completion_model;
349 	ChamplainView *view;
350 	GHashTable *hash_table;
351 
352 	priv = E_CONTACT_MAP_WINDOW_GET_PRIVATE (window);
353 	window->priv = priv;
354 
355 	priv->tasks_cnt = 0;
356 
357 	hash_table = g_hash_table_new_full (
358 		(GHashFunc) g_str_hash,
359 		(GEqualFunc) g_str_equal,
360 		(GDestroyNotify) g_free,
361 		(GDestroyNotify) NULL);
362 	priv->hash_table = hash_table;
363 
364 	gtk_window_set_title (GTK_WINDOW (window), _("Contacts Map"));
365 	gtk_container_set_border_width (GTK_CONTAINER (window), 12);
366 	gtk_widget_set_size_request (GTK_WIDGET (window), 800, 600);
367 
368 	/* The map view itself */
369 	map = e_contact_map_new ();
370 	view = e_contact_map_get_view (E_CONTACT_MAP (map));
371 	champlain_view_set_zoom_level (view, 2);
372 	priv->map = E_CONTACT_MAP (map);
373 	g_signal_connect (
374 		view, "notify::zoom-level",
375 		G_CALLBACK (zoom_level_changed_cb), window);
376 	g_signal_connect (
377 		map, "contact-added",
378 		G_CALLBACK (map_contact_added_cb), window);
379 	g_signal_connect (
380 		map, "contact-removed",
381 		G_CALLBACK (map_contact_removed_cb), window);
382 	g_signal_connect (
383 		map, "geocoding-started",
384 		G_CALLBACK (map_contact_geocoding_started_cb), window);
385 	g_signal_connect (
386 		map, "geocoding-failed",
387 		G_CALLBACK (map_contact_geocoding_failed_cb), window);
388 
389 	/* HBox container */
390 	hbox = gtk_hbox_new (FALSE, 7);
391 
392 	/* Spinner */
393 	button = gtk_spinner_new ();
394 	gtk_container_add (GTK_CONTAINER (hbox), button);
395 	gtk_widget_hide (button);
396 	priv->spinner = button;
397 
398 	/* Zoom-in button */
399 	button = gtk_button_new_from_stock (GTK_STOCK_ZOOM_IN);
400 	g_signal_connect (
401 		button, "clicked",
402 		G_CALLBACK (contact_map_window_zoom_in_cb), window);
403 	priv->zoom_in_btn = button;
404 	gtk_container_add (GTK_CONTAINER (hbox), button);
405 
406 	/* Zoom-out button */
407 	button = gtk_button_new_from_stock (GTK_STOCK_ZOOM_OUT);
408 	g_signal_connect (
409 		button, "clicked",
410 		G_CALLBACK (contact_map_window_zoom_out_cb), window);
411 	priv->zoom_out_btn = button;
412 	gtk_container_add (GTK_CONTAINER (hbox), button);
413 
414 	/* Completion model */
415 	completion_model = gtk_list_store_new (1, G_TYPE_STRING);
416 	priv->completion_model = completion_model;
417 
418 	/* Entry completion */
419 	entry_completion = gtk_entry_completion_new ();
420 	gtk_entry_completion_set_model (
421 		entry_completion, GTK_TREE_MODEL (completion_model));
422 	gtk_entry_completion_set_text_column (entry_completion, 0);
423 	g_signal_connect (
424 		entry_completion, "match-selected",
425 		G_CALLBACK (entry_completion_match_selected_cb), window);
426 
427 	/* Search entry */
428 	entry = gtk_entry_new ();
429 	gtk_entry_set_completion (GTK_ENTRY (entry), entry_completion);
430 	g_signal_connect (
431 		entry, "key-press-event",
432 		G_CALLBACK (contact_map_window_entry_key_pressed_cb), window);
433 	window->priv->search_entry = entry;
434 	gtk_container_add (GTK_CONTAINER (hbox), entry);
435 
436 	/* Search button */
437 	button = gtk_button_new_from_stock (GTK_STOCK_FIND);
438 	g_signal_connect (
439 		button, "clicked",
440 		G_CALLBACK (contact_map_window_find_contact_cb), window);
441 	gtk_container_add (GTK_CONTAINER (hbox), button);
442 
443 	viewport = gtk_frame_new (NULL);
444 	gtk_container_add (GTK_CONTAINER (viewport), map);
445 
446 	vbox = gtk_vbox_new (FALSE, 6);
447 	gtk_container_add (GTK_CONTAINER (vbox), viewport);
448 	gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
449 
450 	gtk_container_add (GTK_CONTAINER (window), vbox);
451 
452 	gtk_widget_show_all (vbox);
453 	gtk_widget_hide (priv->spinner);
454 }
455 
456 EContactMapWindow *
457 e_contact_map_window_new (void)
458 {
459 	return g_object_new (
460 		E_TYPE_CONTACT_MAP_WINDOW, NULL);
461 }
462 
463 /**
464  * Gets all contacts from @book and puts them
465  * on the map view
466  */
467 void
468 e_contact_map_window_load_addressbook (EContactMapWindow *map,
469                                        EBookClient *book_client)
470 {
471 	EBookQuery *book_query;
472 	gchar *query_string;
473 
474 	g_return_if_fail (E_IS_CONTACT_MAP_WINDOW (map));
475 	g_return_if_fail (E_IS_BOOK_CLIENT (book_client));
476 
477 	/* Reference book, so that it does not get deleted before the callback is
478 	 * involved. The book is unrefed in the callback */
479 	g_object_ref (book_client);
480 
481 	book_query = e_book_query_field_exists (E_CONTACT_ADDRESS);
482 	query_string = e_book_query_to_string (book_query);
483 	e_book_query_unref (book_query);
484 
485 	e_book_client_get_contacts (
486 		book_client, query_string, NULL,
487 		book_contacts_received_cb, map);
488 
489 	g_free (query_string);
490 }
491 
492 EContactMap *
493 e_contact_map_window_get_map (EContactMapWindow *window)
494 {
495 	g_return_val_if_fail (E_IS_CONTACT_MAP_WINDOW (window), NULL);
496 
497 	return window->priv->map;
498 }
499 
500 #endif /* WITH_CONTACT_MAPS */