2 * Copyright (C) 2008, 2009 Collabora Ltd.
4 * This library 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.1 of the License, or (at your option) any later version.
9 * This library 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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 * Authors: Pierre-Luc Beaudoin <pierre-luc.beaudoin@collabora.co.uk>
25 #include <glib/gi18n.h>
26 #include <gdk/gdkkeysyms.h>
28 #include <champlain/champlain.h>
29 #include <champlain-gtk/champlain-gtk.h>
30 #include <clutter-gtk/clutter-gtk.h>
31 #include <telepathy-glib/util.h>
33 #include <libempathy/empathy-contact.h>
34 #include <libempathy/empathy-contact-manager.h>
35 #include <libempathy/empathy-utils.h>
36 #include <libempathy/empathy-location.h>
38 #include <libempathy-gtk/empathy-contact-list-store.h>
39 #include <libempathy-gtk/empathy-contact-list-view.h>
40 #include <libempathy-gtk/empathy-presence-chooser.h>
41 #include <libempathy-gtk/empathy-ui-utils.h>
43 #include "empathy-map-view.h"
44 #include "ephy-spinner.h"
46 #define DEBUG_FLAG EMPATHY_DEBUG_LOCATION
47 #include <libempathy/empathy-debug.h>
50 EmpathyContactListStore
*list_store
;
56 ChamplainView
*map_view
;
57 ChamplainLayer
*layer
;
62 map_view_state_changed (ChamplainView
*view
,
64 EmpathyMapView
*window
)
68 g_object_get (G_OBJECT (view
), "state", &state
, NULL
);
69 if (state
== CHAMPLAIN_STATE_LOADING
)
70 ephy_spinner_start (EPHY_SPINNER (window
->throbber
));
72 ephy_spinner_stop (EPHY_SPINNER (window
->throbber
));
76 map_view_marker_update_position (ChamplainMarker
*marker
,
77 EmpathyContact
*contact
)
83 location
= empathy_contact_get_location (contact
);
85 if (location
== NULL
||
86 g_hash_table_size (location
) == 0)
88 champlain_base_marker_animate_out (CHAMPLAIN_BASE_MARKER (marker
));
92 value
= g_hash_table_lookup (location
, EMPATHY_LOCATION_LAT
);
95 clutter_actor_hide (CLUTTER_ACTOR (marker
));
98 lat
= g_value_get_double (value
);
100 value
= g_hash_table_lookup (location
, EMPATHY_LOCATION_LON
);
103 clutter_actor_hide (CLUTTER_ACTOR (marker
));
106 lon
= g_value_get_double (value
);
108 champlain_base_marker_set_position (CHAMPLAIN_BASE_MARKER (marker
), lat
, lon
);
109 champlain_base_marker_animate_in (CHAMPLAIN_BASE_MARKER (marker
));
113 map_view_contact_location_notify (EmpathyContact
*contact
,
115 ChamplainMarker
*marker
)
117 map_view_marker_update_position (marker
, contact
);
121 map_view_zoom_in_cb (GtkWidget
*widget
,
122 EmpathyMapView
*window
)
124 champlain_view_zoom_in (window
->map_view
);
128 map_view_zoom_out_cb (GtkWidget
*widget
,
129 EmpathyMapView
*window
)
131 champlain_view_zoom_out (window
->map_view
);
135 map_view_zoom_fit_cb (GtkWidget
*widget
,
136 EmpathyMapView
*window
)
138 GList
*item
, *children
;
141 children
= clutter_container_get_children (CLUTTER_CONTAINER (window
->layer
));
142 markers
= g_ptr_array_sized_new (g_list_length (children
) + 1);
144 for (item
= children
; item
!= NULL
; item
= g_list_next (item
))
145 g_ptr_array_add (markers
, (gpointer
) item
->data
);
147 g_ptr_array_add (markers
, (gpointer
) NULL
);
148 champlain_view_ensure_markers_visible (window
->map_view
,
149 (ChamplainBaseMarker
**) markers
->pdata
,
152 g_ptr_array_free (markers
, TRUE
);
153 g_list_free (children
);
157 marker_clicked_cb (ChamplainMarker
*marker
,
158 ClutterButtonEvent
*event
,
159 EmpathyContact
*contact
)
163 if (event
->button
!= 3)
166 menu
= empathy_contact_menu_new (contact
,
167 EMPATHY_CONTACT_FEATURE_CHAT
|
168 EMPATHY_CONTACT_FEATURE_CALL
|
169 EMPATHY_CONTACT_FEATURE_LOG
|
170 EMPATHY_CONTACT_FEATURE_INFO
);
175 gtk_widget_show (menu
);
176 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
,
177 event
->button
, event
->time
);
183 map_view_contacts_update_label (ChamplainMarker
*marker
)
190 GHashTable
*location
;
191 EmpathyContact
*contact
;
193 contact
= g_object_get_data (G_OBJECT (marker
), "contact");
194 location
= empathy_contact_get_location (contact
);
195 name
= empathy_contact_get_name (contact
);
196 gtime
= g_hash_table_lookup (location
, EMPATHY_LOCATION_TIMESTAMP
);
202 loctime
= g_value_get_int64 (gtime
);
203 date
= empathy_time_to_string_relative (loctime
);
204 label
= g_strconcat ("<b>", name
, "</b>\n<small>", date
, "</small>", NULL
);
209 /* if location is older than a week */
210 if (now
- loctime
> (60 * 60 * 24 * 7))
211 clutter_actor_set_opacity (CLUTTER_ACTOR (marker
), 0.75 * 255);
215 label
= g_strconcat ("<b>", name
, "</b>\n", NULL
);
218 champlain_marker_set_use_markup (CHAMPLAIN_MARKER (marker
), TRUE
);
219 champlain_marker_set_text (CHAMPLAIN_MARKER (marker
), label
);
225 map_view_contacts_foreach (GtkTreeModel
*model
,
230 EmpathyMapView
*window
= (EmpathyMapView
*) user_data
;
231 EmpathyContact
*contact
;
232 ClutterActor
*marker
;
233 ClutterActor
*texture
;
234 GHashTable
*location
;
237 gtk_tree_model_get (model
, iter
, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT
,
243 location
= empathy_contact_get_location (contact
);
245 if (location
== NULL
|| g_hash_table_size (location
) == 0)
248 marker
= champlain_marker_new ();
250 avatar
= empathy_pixbuf_avatar_from_contact_scaled (contact
, 32, 32);
253 texture
= clutter_texture_new ();
254 gtk_clutter_texture_set_from_pixbuf (CLUTTER_TEXTURE (texture
), avatar
,
256 champlain_marker_set_image (CHAMPLAIN_MARKER (marker
), texture
);
257 g_object_unref (avatar
);
260 champlain_marker_set_image (CHAMPLAIN_MARKER (marker
), NULL
);
262 g_object_set_data_full (G_OBJECT (marker
), "contact",
263 g_object_ref (contact
), g_object_unref
);
265 map_view_contacts_update_label (CHAMPLAIN_MARKER (marker
));
267 clutter_actor_set_reactive (CLUTTER_ACTOR (marker
), TRUE
);
268 g_signal_connect (marker
, "button-release-event",
269 G_CALLBACK (marker_clicked_cb
), contact
);
271 clutter_container_add (CLUTTER_CONTAINER (window
->layer
), marker
, NULL
);
273 g_signal_connect (contact
, "notify::location",
274 G_CALLBACK (map_view_contact_location_notify
), marker
);
276 map_view_marker_update_position (CHAMPLAIN_MARKER (marker
), contact
);
278 g_object_unref (contact
);
283 map_view_destroy_cb (GtkWidget
*widget
,
284 EmpathyMapView
*window
)
288 g_source_remove (window
->timeout_id
);
290 item
= clutter_container_get_children (CLUTTER_CONTAINER (window
->layer
));
293 EmpathyContact
*contact
;
294 ChamplainMarker
*marker
;
296 marker
= CHAMPLAIN_MARKER (item
->data
);
297 contact
= g_object_get_data (G_OBJECT (marker
), "contact");
298 g_signal_handlers_disconnect_by_func (contact
,
299 map_view_contact_location_notify
, marker
);
301 item
= g_list_next (item
);
304 g_object_unref (window
->list_store
);
305 g_object_unref (window
->layer
);
306 g_slice_free (EmpathyMapView
, window
);
310 map_view_key_press_cb (GtkWidget
*widget
,
314 if ((event
->state
& GDK_CONTROL_MASK
&& event
->keyval
== GDK_w
)
315 || event
->keyval
== GDK_Escape
)
317 gtk_widget_destroy (widget
);
325 map_view_tick (EmpathyMapView
*window
)
329 marker
= clutter_container_get_children (CLUTTER_CONTAINER (window
->layer
));
331 for (; marker
; marker
= marker
->next
)
332 map_view_contacts_update_label (marker
->data
);
338 empathy_map_view_show (void)
340 static EmpathyMapView
*window
= NULL
;
344 GtkWidget
*throbber_holder
;
347 EmpathyContactList
*list_iface
;
348 EmpathyContactListStore
*list_store
;
352 empathy_window_present (GTK_WINDOW (window
->window
), TRUE
);
353 return window
->window
;
356 window
= g_slice_new0 (EmpathyMapView
);
358 /* Set up interface */
359 filename
= empathy_file_lookup ("empathy-map-view.ui", "src");
360 gui
= empathy_builder_get_file (filename
,
361 "map_view", &window
->window
,
362 "zoom_in", &window
->zoom_in
,
363 "zoom_out", &window
->zoom_out
,
364 "map_scrolledwindow", &sw
,
365 "throbber", &throbber_holder
,
369 empathy_builder_connect (gui
, window
,
370 "map_view", "destroy", map_view_destroy_cb
,
371 "map_view", "key-press-event", map_view_key_press_cb
,
372 "zoom_in", "clicked", map_view_zoom_in_cb
,
373 "zoom_out", "clicked", map_view_zoom_out_cb
,
374 "zoom_fit", "clicked", map_view_zoom_fit_cb
,
377 g_object_unref (gui
);
379 /* Clear the static pointer to window if the dialog is destroyed */
380 g_object_add_weak_pointer (G_OBJECT (window
->window
), (gpointer
*) &window
);
382 list_iface
= EMPATHY_CONTACT_LIST (empathy_contact_manager_dup_singleton ());
383 list_store
= empathy_contact_list_store_new (list_iface
);
384 empathy_contact_list_store_set_show_groups (list_store
, FALSE
);
385 empathy_contact_list_store_set_show_avatars (list_store
, TRUE
);
386 g_object_unref (list_iface
);
388 window
->throbber
= ephy_spinner_new ();
389 ephy_spinner_set_size (EPHY_SPINNER (window
->throbber
),
390 GTK_ICON_SIZE_LARGE_TOOLBAR
);
391 gtk_widget_show (window
->throbber
);
392 gtk_container_add (GTK_CONTAINER (throbber_holder
), window
->throbber
);
394 window
->list_store
= list_store
;
396 /* Set up map view */
397 embed
= gtk_champlain_embed_new ();
398 window
->map_view
= gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (embed
));
399 g_object_set (G_OBJECT (window
->map_view
), "zoom-level", 1,
400 "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC
, NULL
);
401 champlain_view_center_on (window
->map_view
, 36, 0);
403 gtk_container_add (GTK_CONTAINER (sw
), embed
);
404 gtk_widget_show_all (embed
);
406 window
->layer
= g_object_ref (champlain_layer_new ());
407 champlain_view_add_layer (window
->map_view
, window
->layer
);
409 g_signal_connect (window
->map_view
, "notify::state",
410 G_CALLBACK (map_view_state_changed
), window
);
412 /* Set up contact list. */
413 model
= GTK_TREE_MODEL (window
->list_store
);
414 gtk_tree_model_foreach (model
, map_view_contacts_foreach
, window
);
416 empathy_window_present (GTK_WINDOW (window
->window
), TRUE
);
418 /* Set up time updating loop */
419 window
->timeout_id
= g_timeout_add_seconds (5,
420 (GSourceFunc
) map_view_tick
, window
);
422 return window
->window
;