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