Mercurial > geeqie
comparison src/bar_gps.c @ 1604:c6d522fe3e5e
added GPS map support - patch by Colin Clark
author | nadvornik |
---|---|
date | Tue, 12 May 2009 18:25:18 +0000 |
parents | |
children | 0806ccdfe06b |
comparison
equal
deleted
inserted
replaced
1603:1b29d2445991 | 1604:c6d522fe3e5e |
---|---|
1 /* | |
2 * Geeqie | |
3 * (C) 2004 John Ellis | |
4 * Copyright (C) 2008 - 2009 The Geeqie Team | |
5 * | |
6 * Author: Colin Clark | |
7 * | |
8 * This software is released under the GNU General Public License (GNU GPL). | |
9 * Please read the included file COPYING for more information. | |
10 * This software comes with no warranty of any kind, use at your own risk! | |
11 */ | |
12 | |
13 #include "main.h" | |
14 #ifdef HAVE_LIBCHAMPLAIN | |
15 #ifdef HAVE_LIBCHAMPLAIN_GTK | |
16 | |
17 #include "bar_gps.h" | |
18 | |
19 #include "bar.h" | |
20 #include "filedata.h" | |
21 #include "layout.h" | |
22 #include "metadata.h" | |
23 #include "menu.h" | |
24 #include "rcfile.h" | |
25 #include "thumb.h" | |
26 #include "ui_menu.h" | |
27 | |
28 #include <clutter-gtk/gtk-clutter-embed.h> | |
29 #include <champlain/champlain.h> | |
30 #include <champlain-gtk/champlain-gtk.h> | |
31 | |
32 #define MARKER_COLOUR 0x00, 0x00, 0xff, 0xff | |
33 #define TEXT_COLOUR 0x00, 0x00, 0x00, 0xff | |
34 #define THUMB_COLOUR 0xff, 0xff, 0xff, 0xff | |
35 #define THUMB_SIZE 100 | |
36 | |
37 /* | |
38 *------------------------------------------------------------------- | |
39 * GPS Map utils | |
40 *------------------------------------------------------------------- | |
41 */ | |
42 | |
43 typedef struct _PaneGPSData PaneGPSData; | |
44 struct _PaneGPSData | |
45 { | |
46 PaneData pane; | |
47 GtkWidget *widget; | |
48 FileData *fd; | |
49 gchar *map_source; | |
50 gint height; | |
51 ClutterActor *gps_view; | |
52 ChamplainLayer *icon_layer; | |
53 GList *selection_list; | |
54 GPtrArray *marker_list; | |
55 guint create_markers_id; | |
56 GtkWidget *progress; | |
57 GtkWidget *slider; | |
58 GtkWidget *state; | |
59 gint selection_count; | |
60 gboolean centre_map_checked; | |
61 gboolean enable_markers_checked; | |
62 }; | |
63 | |
64 static void bar_pane_gps_thumb_done_cb(ThumbLoader *tl, gpointer data) | |
65 { | |
66 FileData *fd; | |
67 ClutterActor *marker; | |
68 ClutterActor *actor; | |
69 | |
70 marker = CLUTTER_ACTOR(data); | |
71 fd = g_object_get_data(G_OBJECT(marker), "file_fd"); | |
72 if (fd->thumb_pixbuf != NULL) | |
73 { | |
74 actor = clutter_texture_new(); | |
75 gtk_clutter_texture_set_from_pixbuf(CLUTTER_TEXTURE(actor), fd->thumb_pixbuf); | |
76 champlain_marker_set_image(CHAMPLAIN_MARKER(marker), actor); | |
77 } | |
78 thumb_loader_free(tl); | |
79 } | |
80 | |
81 static void bar_pane_gps_thumb_error_cb(ThumbLoader *tl, gpointer data) | |
82 { | |
83 thumb_loader_free(tl); | |
84 } | |
85 | |
86 static gboolean bar_pane_gps_marker_keypress_cb(GtkWidget *widget, ClutterButtonEvent *bevent, gpointer data) | |
87 { | |
88 //PaneGPSData *pgd = data; | |
89 FileData *fd; | |
90 ClutterActor *marker; | |
91 ClutterColor marker_colour = { MARKER_COLOUR }; | |
92 ClutterColor text_colour = { TEXT_COLOUR }; | |
93 ClutterColor thumb_colour = { THUMB_COLOUR }; | |
94 gchar *current_text; | |
95 ClutterActor *actor; | |
96 ClutterActor *current_image; | |
97 GString *text; | |
98 gint height, width, rotate; | |
99 gchar *altitude = NULL; | |
100 ThumbLoader *tl; | |
101 | |
102 if (bevent->button == MOUSE_BUTTON_LEFT) | |
103 { | |
104 marker = CLUTTER_ACTOR(widget); | |
105 fd = g_object_get_data(G_OBJECT(marker), "file_fd"); | |
106 | |
107 /* If the marker is showing a thumbnail, delete it | |
108 */ | |
109 current_image = champlain_marker_get_image(CHAMPLAIN_MARKER(marker)); | |
110 if (current_image != NULL) | |
111 { | |
112 clutter_actor_destroy(CLUTTER_ACTOR(current_image)); | |
113 champlain_marker_set_image(CHAMPLAIN_MARKER(marker), NULL); | |
114 } | |
115 | |
116 current_text = g_strdup(champlain_marker_get_text(CHAMPLAIN_MARKER(marker))); | |
117 | |
118 /* If the marker is showing only the text character, replace it with a | |
119 * thumbnail and date and altitude | |
120 */ | |
121 if (g_strcmp0(current_text, "i") == 0) | |
122 { | |
123 /* If a thumbail has already been generated, use that. If not try the pixbuf of the full image. | |
124 * If not, call the thumb_loader to generate a thumbnail and update the marker later in the | |
125 * thumb_loader callback | |
126 */ | |
127 if (fd->thumb_pixbuf != NULL) | |
128 { | |
129 actor = clutter_texture_new(); | |
130 gtk_clutter_texture_set_from_pixbuf(CLUTTER_TEXTURE(actor), fd->thumb_pixbuf); | |
131 champlain_marker_set_image(CHAMPLAIN_MARKER(marker), actor); | |
132 } | |
133 else if (fd->pixbuf != NULL) | |
134 { | |
135 actor = clutter_texture_new(); | |
136 width = gdk_pixbuf_get_width (fd->pixbuf); | |
137 height = gdk_pixbuf_get_height (fd->pixbuf); | |
138 switch (fd->exif_orientation) | |
139 { | |
140 case 8: | |
141 rotate = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE; | |
142 break; | |
143 case 3: | |
144 rotate = GDK_PIXBUF_ROTATE_UPSIDEDOWN; | |
145 break; | |
146 case 6: | |
147 rotate = GDK_PIXBUF_ROTATE_CLOCKWISE; | |
148 break; | |
149 default: | |
150 rotate = GDK_PIXBUF_ROTATE_NONE; | |
151 } | |
152 | |
153 gtk_clutter_texture_set_from_pixbuf(CLUTTER_TEXTURE(actor), | |
154 gdk_pixbuf_rotate_simple(gdk_pixbuf_scale_simple(fd->pixbuf, THUMB_SIZE, height * THUMB_SIZE / width, | |
155 GDK_INTERP_NEAREST), rotate)); | |
156 champlain_marker_set_image(CHAMPLAIN_MARKER(marker), actor); | |
157 } | |
158 else | |
159 { | |
160 tl = thumb_loader_new(THUMB_SIZE, THUMB_SIZE); | |
161 thumb_loader_set_callbacks(tl, | |
162 bar_pane_gps_thumb_done_cb, | |
163 bar_pane_gps_thumb_error_cb, | |
164 NULL, | |
165 marker); | |
166 thumb_loader_start(tl, fd); | |
167 } | |
168 | |
169 text = g_string_new(fd->name); | |
170 g_string_append(text, "\n"); | |
171 g_string_append(text, text_from_time(fd->date)); | |
172 g_string_append(text, "\n"); | |
173 altitude = metadata_read_string(fd, "formatted.GPSAltitude", METADATA_FORMATTED); | |
174 if (altitude != NULL) | |
175 { | |
176 g_string_append(text, altitude); | |
177 } | |
178 | |
179 champlain_marker_set_text(CHAMPLAIN_MARKER(marker), text->str); | |
180 champlain_marker_set_color(CHAMPLAIN_MARKER(marker), &thumb_colour); | |
181 champlain_marker_set_text_color(CHAMPLAIN_MARKER(marker), &text_colour); | |
182 champlain_marker_set_font_name(CHAMPLAIN_MARKER(marker), "sans 8"); | |
183 | |
184 g_free(altitude); | |
185 g_string_free(text, TRUE); | |
186 } | |
187 /* otherwise, revert to the hidden text marker | |
188 */ | |
189 else | |
190 { | |
191 champlain_marker_set_text(CHAMPLAIN_MARKER(marker), "i"); | |
192 champlain_marker_set_color(CHAMPLAIN_MARKER(marker), &marker_colour); | |
193 champlain_marker_set_text_color(CHAMPLAIN_MARKER(marker), &marker_colour); | |
194 champlain_marker_set_font_name(CHAMPLAIN_MARKER(marker), "courier 5"); | |
195 } | |
196 | |
197 g_free(current_text); | |
198 | |
199 return TRUE; | |
200 } | |
201 return TRUE; | |
202 } | |
203 | |
204 static gboolean bar_pane_gps_create_markers_cb(gpointer data) | |
205 { | |
206 PaneGPSData *pgd = data; | |
207 gdouble latitude; | |
208 gdouble longitude; | |
209 GList *work; | |
210 ClutterActor *marker; | |
211 FileData *fd; | |
212 ClutterColor marker_colour = { MARKER_COLOUR }; | |
213 GString *message; | |
214 | |
215 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pgd->progress), | |
216 (gdouble)(pgd->selection_count - g_list_length(pgd->selection_list)) / | |
217 (gdouble)pgd->selection_count); | |
218 | |
219 message = g_string_new(""); | |
220 g_string_printf(message, "%i/%i", (pgd->selection_count - g_list_length(pgd->selection_list)), | |
221 pgd->selection_count); | |
222 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pgd->progress), message->str); | |
223 g_string_free(message, TRUE); | |
224 | |
225 work = pgd->selection_list; | |
226 while (work) | |
227 { | |
228 fd = work->data; | |
229 pgd->selection_list = g_list_remove(pgd->selection_list, work->data); | |
230 if (fd != NULL) | |
231 { | |
232 latitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLatitude", 1000); | |
233 longitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLongitude", 1000); | |
234 | |
235 if ((latitude != 1000) && (longitude != 1000)) | |
236 { | |
237 marker = champlain_marker_new_with_text("i","courier 5", &marker_colour, &marker_colour); | |
238 | |
239 champlain_base_marker_set_position(CHAMPLAIN_BASE_MARKER(marker), latitude, longitude); | |
240 clutter_container_add(CLUTTER_CONTAINER(pgd->icon_layer), marker, NULL); | |
241 clutter_actor_set_reactive(marker, TRUE); | |
242 | |
243 g_signal_connect(G_OBJECT(marker), "button_press_event", | |
244 G_CALLBACK(bar_pane_gps_marker_keypress_cb), pgd); | |
245 | |
246 g_object_set_data(G_OBJECT(marker), "file_fd", fd); | |
247 | |
248 g_ptr_array_add(pgd->marker_list, marker); | |
249 if (pgd->centre_map_checked) | |
250 { | |
251 g_ptr_array_add(pgd->marker_list, NULL); | |
252 champlain_view_ensure_markers_visible(CHAMPLAIN_VIEW(pgd->gps_view), | |
253 (void *)pgd->marker_list->pdata, FALSE); | |
254 g_ptr_array_remove(pgd->marker_list, NULL); | |
255 } | |
256 } | |
257 } | |
258 return TRUE; | |
259 } | |
260 | |
261 if (pgd->marker_list->len >= 1) | |
262 { | |
263 g_ptr_array_add(pgd->marker_list, NULL); | |
264 | |
265 if (pgd->centre_map_checked) | |
266 { | |
267 champlain_view_ensure_markers_visible(CHAMPLAIN_VIEW(pgd->gps_view), (void *)pgd->marker_list->pdata, FALSE); | |
268 } | |
269 } | |
270 | |
271 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pgd->progress), 0); | |
272 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pgd->progress), NULL); | |
273 g_list_free(pgd->selection_list); | |
274 g_ptr_array_free(pgd->marker_list, TRUE); | |
275 pgd->create_markers_id = 0; | |
276 | |
277 return FALSE; | |
278 } | |
279 | |
280 static void bar_pane_gps_update(PaneGPSData *pgd) | |
281 { | |
282 GList *list; | |
283 GList *work; | |
284 | |
285 /* The widget does not have a parent during bar_pane_gps_new, so calling gtk_widget_show_all there gives a | |
286 * "Gtk-CRITICAL **: gtk_widget_realize: assertion `GTK_WIDGET_ANCHORED (widget) || GTK_IS_INVISIBLE (widget)' failed" | |
287 * error. gtk_widget_show_all can be given after it has been added to the bar. | |
288 */ | |
289 if (gtk_widget_get_parent(pgd->widget) != NULL) | |
290 gtk_widget_show_all(pgd->widget); | |
291 | |
292 /* If a create-marker background process is running, kill it | |
293 * and start again | |
294 */ | |
295 if (pgd->create_markers_id != 0) | |
296 { | |
297 if (g_idle_remove_by_data(pgd)) | |
298 { | |
299 pgd->create_markers_id = 0; | |
300 } | |
301 else | |
302 { | |
303 return; | |
304 } | |
305 } | |
306 | |
307 /* Delete any markers currently displayed | |
308 */ | |
309 work = clutter_container_get_children(CLUTTER_CONTAINER(pgd->icon_layer)); | |
310 while (work) | |
311 { | |
312 clutter_container_remove(CLUTTER_CONTAINER(pgd->icon_layer), work->data, NULL); | |
313 work = work->next; | |
314 } | |
315 g_list_free(work); | |
316 | |
317 if (!pgd->enable_markers_checked) | |
318 { | |
319 return; | |
320 } | |
321 | |
322 /* For each selected photo that has GPS data, create a marker containing | |
323 * a single, small text character the same colour as the marker background. | |
324 * Use a background process in case the user selects a large number of files. | |
325 */ | |
326 list = layout_selection_list(pgd->pane.lw); | |
327 | |
328 if (list != NULL) | |
329 { | |
330 pgd->selection_list = g_list_copy(list); | |
331 pgd->marker_list = g_ptr_array_new(); | |
332 pgd->selection_count = g_list_length(pgd->selection_list); | |
333 pgd->create_markers_id = g_idle_add(bar_pane_gps_create_markers_cb, pgd); | |
334 } | |
335 | |
336 g_list_free(list); | |
337 g_list_free(work); | |
338 } | |
339 | |
340 void bar_pane_gps_set_map_source(PaneGPSData *pgd, const gchar *map_id) | |
341 { | |
342 ChamplainMapSource *map_source; | |
343 ChamplainMapSourceFactory *map_factory; | |
344 | |
345 map_factory = champlain_map_source_factory_get_default(); | |
346 map_source = champlain_map_source_factory_create(map_factory, map_id); | |
347 | |
348 if (map_source != NULL) | |
349 { | |
350 g_object_set(G_OBJECT(pgd->gps_view), "map-source", map_source, NULL); | |
351 g_object_unref(map_factory); | |
352 } | |
353 | |
354 g_object_unref(map_source); | |
355 } | |
356 | |
357 void bar_pane_gps_enable_markers_checked_toggle_cb(GtkWidget *menu_widget, gpointer data) | |
358 { | |
359 PaneGPSData *pgd = data; | |
360 | |
361 if (pgd->enable_markers_checked) | |
362 { | |
363 pgd->enable_markers_checked = FALSE; | |
364 } | |
365 else | |
366 { | |
367 pgd->enable_markers_checked = TRUE; | |
368 } | |
369 } | |
370 | |
371 static void bar_pane_gps_centre_map_checked_toggle_cb(GtkWidget *menu_widget, gpointer data) | |
372 { | |
373 PaneGPSData *pgd = data; | |
374 | |
375 if (pgd->centre_map_checked) | |
376 { | |
377 pgd->centre_map_checked = FALSE; | |
378 } | |
379 else | |
380 { | |
381 pgd->centre_map_checked = TRUE; | |
382 } | |
383 } | |
384 | |
385 static void bar_pane_gps_change_map_cb(GtkWidget *widget, gpointer data) | |
386 { | |
387 PaneGPSData *pgd; | |
388 gchar *mapsource; | |
389 | |
390 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) | |
391 return; | |
392 | |
393 pgd = (PaneGPSData *) submenu_item_get_data(widget); | |
394 | |
395 if (!pgd) return; | |
396 | |
397 mapsource = data; | |
398 bar_pane_gps_set_map_source(pgd, mapsource); | |
399 } | |
400 | |
401 static void bar_pane_gps_notify_selection(GtkWidget *bar, gint count) | |
402 { | |
403 PaneGPSData *pgd; | |
404 | |
405 if (count == 0) return; | |
406 | |
407 pgd = g_object_get_data(G_OBJECT(bar), "pane_data"); | |
408 if (!pgd) return; | |
409 | |
410 bar_pane_gps_update(pgd); | |
411 } | |
412 | |
413 static void bar_pane_gps_set_fd(GtkWidget *bar, FileData *fd) | |
414 { | |
415 PaneGPSData *pgd; | |
416 | |
417 pgd = g_object_get_data(G_OBJECT(bar), "pane_data"); | |
418 if (!pgd) return; | |
419 | |
420 file_data_unref(pgd->fd); | |
421 pgd->fd = file_data_ref(fd); | |
422 | |
423 bar_pane_gps_update(pgd); | |
424 } | |
425 | |
426 static gint bar_pane_gps_event(GtkWidget *bar, GdkEvent *event) | |
427 { | |
428 PaneGPSData *pgd; | |
429 | |
430 pgd = g_object_get_data(G_OBJECT(bar), "pane_data"); | |
431 if (!pgd) return FALSE; | |
432 | |
433 if (GTK_WIDGET_HAS_FOCUS(pgd->widget)) return gtk_widget_event(GTK_WIDGET(pgd->widget), event); | |
434 | |
435 return FALSE; | |
436 } | |
437 | |
438 static void bar_pane_gps_write_config(GtkWidget *pane, GString *outstr, gint indent) | |
439 { | |
440 PaneGPSData *pgd; | |
441 gint zoom; | |
442 ChamplainMapSource *mapsource; | |
443 const gchar *map_id; | |
444 gchar *str = NULL; | |
445 GString *buffer = g_string_new(str); | |
446 gdouble position; | |
447 gint int_position; | |
448 | |
449 pgd = g_object_get_data(G_OBJECT(pane), "pane_data"); | |
450 if (!pgd) return; | |
451 | |
452 WRITE_NL(); | |
453 WRITE_STRING("<pane_gps "); | |
454 write_char_option(outstr, indent, "id", pgd->pane.id); | |
455 write_char_option(outstr, indent, "title", gtk_label_get_text(GTK_LABEL(pgd->pane.title))); | |
456 WRITE_BOOL(pgd->pane, expanded); | |
457 WRITE_INT(*pgd, height); | |
458 indent++; | |
459 | |
460 g_object_get(G_OBJECT(pgd->gps_view), "map-source", &mapsource, NULL); | |
461 map_id = champlain_map_source_get_id(mapsource); | |
462 WRITE_NL(); | |
463 write_char_option(outstr, indent, "map-id", map_id); | |
464 | |
465 g_object_get(G_OBJECT(pgd->gps_view), "zoom-level", &zoom, NULL); | |
466 g_string_printf(buffer, "%d", zoom); | |
467 WRITE_NL(); | |
468 write_char_option(outstr, indent, "zoom-level", buffer->str); | |
469 | |
470 g_object_get(G_OBJECT(pgd->gps_view), "latitude", &position, NULL); | |
471 int_position = position * 1000000; | |
472 g_string_printf(buffer, "%i", int_position); | |
473 WRITE_NL(); | |
474 write_char_option(outstr, indent, "latitude", buffer->str); | |
475 | |
476 g_object_get(G_OBJECT(pgd->gps_view), "longitude", &position, NULL); | |
477 int_position = position * 1000000; | |
478 g_string_printf(buffer, "%i", int_position); | |
479 WRITE_NL(); | |
480 write_char_option(outstr, indent, "longitude", buffer->str); | |
481 | |
482 indent--; | |
483 WRITE_NL(); | |
484 WRITE_STRING("/>"); | |
485 | |
486 g_object_unref(mapsource); | |
487 | |
488 } | |
489 | |
490 static void bar_pane_gps_slider_changed_cb(GtkScaleButton *slider, | |
491 gdouble zoom, | |
492 gpointer data) | |
493 { | |
494 PaneGPSData *pgd = data; | |
495 GString *message; | |
496 | |
497 message = g_string_new(""); | |
498 g_string_printf(message, _("Zoom %i"), (gint)zoom); | |
499 | |
500 g_object_set(G_OBJECT(CHAMPLAIN_VIEW(pgd->gps_view)), "zoom-level", (gint)zoom, NULL); | |
501 gtk_widget_set_tooltip_text(GTK_WIDGET(slider), message->str); | |
502 g_string_free(message, TRUE); | |
503 | |
504 } | |
505 static void bar_pane_gps_view_state_changed_cb(ChamplainView *view, | |
506 GParamSpec *gobject, | |
507 gpointer data) | |
508 { | |
509 PaneGPSData *pgd = data; | |
510 ChamplainState status; | |
511 gint zoom; | |
512 GString *message; | |
513 | |
514 g_object_get(G_OBJECT(view), "zoom-level", &zoom, NULL); | |
515 message = g_string_new(""); | |
516 g_string_printf(message, _("Zoom level %i"), zoom); | |
517 | |
518 g_object_get(G_OBJECT(view), "state", &status, NULL); | |
519 if (status == CHAMPLAIN_STATE_LOADING) | |
520 { | |
521 gtk_label_set_text(GTK_LABEL(pgd->state), _("Loading map")); | |
522 } | |
523 else | |
524 { | |
525 gtk_label_set_text(GTK_LABEL(pgd->state), message->str); | |
526 } | |
527 | |
528 gtk_widget_set_tooltip_text(GTK_WIDGET(pgd->slider), message->str); | |
529 gtk_scale_button_set_value(GTK_SCALE_BUTTON(pgd->slider), (gdouble)zoom); | |
530 | |
531 g_string_free(message, TRUE); | |
532 } | |
533 | |
534 static void bar_pane_gps_notify_cb(FileData *fd, NotifyType type, gpointer data) | |
535 { | |
536 PaneGPSData *pgd = data; | |
537 | |
538 if ((type & (NOTIFY_REREAD | NOTIFY_CHANGE | NOTIFY_METADATA)) && fd == pgd->fd) | |
539 { | |
540 bar_pane_gps_update(pgd); | |
541 } | |
542 } | |
543 | |
544 const gchar *bar_pane_gps_get_map_id(PaneGPSData *pgd) | |
545 { | |
546 const gchar *map_id; | |
547 ChamplainMapSource *mapsource; | |
548 | |
549 g_object_get(G_OBJECT(pgd->gps_view), "map-source", &mapsource, NULL); | |
550 map_id = champlain_map_source_get_id(mapsource); | |
551 | |
552 g_object_unref(mapsource); | |
553 | |
554 return map_id; | |
555 } | |
556 | |
557 static GtkWidget *bar_pane_gps_add_radio(GtkWidget *menu, GtkWidget *parent, | |
558 const gchar *label, GCallback func, gchar *value, | |
559 gboolean show_current, const gchar *current_value) | |
560 { | |
561 GtkWidget *item; | |
562 | |
563 if (show_current) | |
564 { | |
565 item = menu_item_add_radio(menu, parent, | |
566 label, (g_strcmp0(value, current_value) == 0), func, value); | |
567 } | |
568 else | |
569 { | |
570 item = menu_item_add(menu, label, func, value); | |
571 } | |
572 | |
573 return item; | |
574 } | |
575 | |
576 static GtkWidget *bar_pane_gps_menu(PaneGPSData *pgd) | |
577 { | |
578 GtkWidget *menu; | |
579 GtkWidget *map_centre; | |
580 static gboolean show_current = TRUE; | |
581 GtkWidget *parent; | |
582 ChamplainMapSourceFactory *map_factory; | |
583 GSList *map_list; | |
584 ChamplainMapSourceDesc *map_desc; | |
585 | |
586 menu = popup_menu_short_lived(); | |
587 | |
588 map_factory = champlain_map_source_factory_get_default(); | |
589 map_list = champlain_map_source_factory_get_list(map_factory); | |
590 map_desc = (ChamplainMapSourceDesc *)(map_list->data); | |
591 map_list = g_slist_next(map_list); | |
592 | |
593 g_object_set_data(G_OBJECT(menu), "submenu_data", pgd); | |
594 | |
595 parent = bar_pane_gps_add_radio(menu, NULL, (map_desc->name), G_CALLBACK(bar_pane_gps_change_map_cb), map_desc->id, show_current, bar_pane_gps_get_map_id(pgd)); | |
596 | |
597 while (map_list) | |
598 { | |
599 map_desc = (ChamplainMapSourceDesc *)(map_list->data); | |
600 bar_pane_gps_add_radio(menu, parent, (map_desc->name), G_CALLBACK(bar_pane_gps_change_map_cb), map_desc->id, | |
601 show_current, bar_pane_gps_get_map_id(pgd)); | |
602 map_list = g_slist_next(map_list); | |
603 } | |
604 | |
605 menu_item_add_divider(menu); | |
606 menu_item_add_check(menu, _("Enable markers"), pgd->enable_markers_checked, | |
607 G_CALLBACK(bar_pane_gps_enable_markers_checked_toggle_cb), pgd); | |
608 map_centre = menu_item_add_check(menu, _("Centre map on marker"), pgd->centre_map_checked, | |
609 G_CALLBACK(bar_pane_gps_centre_map_checked_toggle_cb), pgd); | |
610 if (!pgd->enable_markers_checked) | |
611 { | |
612 gtk_widget_set_sensitive(map_centre, FALSE); | |
613 } | |
614 | |
615 g_slist_free(map_list); | |
616 g_object_unref(map_factory); | |
617 //g_object_unref(map_centre); | |
618 | |
619 return menu; | |
620 } | |
621 | |
622 /* Determine if the map is to be re-centred on the marker when another photo is selected | |
623 */ | |
624 void bar_pane_gps_map_centreing(PaneGPSData *pgd) | |
625 { | |
626 GtkWidget *dialog; | |
627 GString *message = g_string_new(""); | |
628 | |
629 if (pgd->centre_map_checked) | |
630 { | |
631 message = g_string_append(message, _("Move map centre to marker\n is disabled")); | |
632 pgd->centre_map_checked = FALSE; | |
633 } | |
634 else | |
635 { | |
636 message = g_string_append(message, _("Move map centre to marker\n is enabled")); | |
637 pgd->centre_map_checked = TRUE; | |
638 } | |
639 | |
640 dialog = gtk_message_dialog_new(NULL, | |
641 GTK_DIALOG_DESTROY_WITH_PARENT, | |
642 GTK_MESSAGE_INFO, | |
643 GTK_BUTTONS_CLOSE, | |
644 "%s", message->str); | |
645 gtk_window_set_title(GTK_WINDOW(dialog), _("Map Centreing")); | |
646 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); | |
647 gtk_dialog_run(GTK_DIALOG(dialog)); | |
648 | |
649 gtk_widget_destroy(dialog); | |
650 g_string_free(message, TRUE); | |
651 } | |
652 | |
653 static gboolean bar_pane_gps_map_keypress_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) | |
654 { | |
655 PaneGPSData *pgd = data; | |
656 GtkWidget *menu; | |
657 | |
658 if (bevent->button == MOUSE_BUTTON_RIGHT) | |
659 { | |
660 menu = bar_pane_gps_menu(pgd); | |
661 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, bevent->button, bevent->time); | |
662 return TRUE; | |
663 } | |
664 else if (bevent->button == MOUSE_BUTTON_MIDDLE) | |
665 { | |
666 bar_pane_gps_map_centreing(pgd); | |
667 return TRUE; | |
668 } | |
669 else if (bevent->button == MOUSE_BUTTON_LEFT) | |
670 { | |
671 return FALSE; | |
672 } | |
673 else | |
674 { | |
675 return FALSE; | |
676 } | |
677 } | |
678 | |
679 static void bar_pane_gps_destroy(GtkWidget *widget, gpointer data) | |
680 { | |
681 PaneGPSData *pgd = data; | |
682 | |
683 file_data_unregister_notify_func(bar_pane_gps_notify_cb, pgd); | |
684 | |
685 file_data_unref(pgd->fd); | |
686 g_free(pgd->map_source); | |
687 g_free(pgd->pane.id); | |
688 clutter_actor_destroy(pgd->gps_view); | |
689 g_free(pgd); | |
690 } | |
691 | |
692 | |
693 GtkWidget *bar_pane_gps_new(const gchar *id, const gchar *title, const gchar *map_id, | |
694 const gint zoom, const gdouble latitude, const gdouble longitude, | |
695 gboolean expanded, gint height) | |
696 { | |
697 PaneGPSData *pgd; | |
698 GtkWidget *vbox, *scrolled; | |
699 GtkWidget *gpswidget, *viewport; | |
700 GtkWidget *status, *state, *progress, *slider; | |
701 ChamplainLayer *layer; | |
702 ClutterActor *view; | |
703 const gchar *slider_list[] = {GTK_STOCK_ZOOM_IN, GTK_STOCK_ZOOM_OUT, NULL}; | |
704 const gchar **slider_icons = slider_list; | |
705 | |
706 pgd = g_new0(PaneGPSData, 1); | |
707 | |
708 pgd->pane.pane_set_fd = bar_pane_gps_set_fd; | |
709 pgd->pane.pane_notify_selection = bar_pane_gps_notify_selection; | |
710 pgd->pane.pane_event = bar_pane_gps_event; | |
711 pgd->pane.pane_write_config = bar_pane_gps_write_config; | |
712 pgd->pane.title = bar_pane_expander_title(title); | |
713 pgd->pane.id = g_strdup(id); | |
714 pgd->pane.type = PANE_GPS; | |
715 pgd->pane.expanded = expanded; | |
716 pgd->height = height; | |
717 | |
718 scrolled = gtk_scrolled_window_new(NULL, NULL); | |
719 vbox = gtk_vbox_new(FALSE, 0); | |
720 view = champlain_view_new(); | |
721 gpswidget = champlain_view_embed_new(CHAMPLAIN_VIEW(view)); | |
722 viewport = gtk_viewport_new(NULL, NULL); | |
723 | |
724 gtk_container_add(GTK_CONTAINER(viewport), gpswidget); | |
725 gtk_box_pack_start(GTK_BOX(vbox),viewport, TRUE, TRUE, 0); | |
726 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), vbox); | |
727 | |
728 status = gtk_hbox_new(FALSE,0); | |
729 slider = gtk_scale_button_new(GTK_ICON_SIZE_SMALL_TOOLBAR, 1, 17, 1, slider_icons); | |
730 gtk_widget_set_tooltip_text(slider, "Zoom"); | |
731 gtk_scale_button_set_value(GTK_SCALE_BUTTON(slider), (gdouble)zoom); | |
732 | |
733 progress = gtk_progress_bar_new(); | |
734 state = gtk_label_new(""); | |
735 gtk_label_set_justify(GTK_LABEL(state), GTK_JUSTIFY_CENTER); | |
736 | |
737 gtk_box_pack_start(GTK_BOX(status), GTK_WIDGET(slider), FALSE, FALSE, 0); | |
738 gtk_box_pack_start(GTK_BOX(status), GTK_WIDGET(state), FALSE, FALSE, 5); | |
739 gtk_box_pack_end(GTK_BOX(status), GTK_WIDGET(progress), FALSE, FALSE, 0); | |
740 gtk_box_pack_end(GTK_BOX(vbox),GTK_WIDGET(status), FALSE, FALSE, 0); | |
741 | |
742 layer = champlain_layer_new(); | |
743 champlain_view_add_layer(CHAMPLAIN_VIEW(view), layer); | |
744 | |
745 pgd->icon_layer = layer; | |
746 pgd->gps_view = view; | |
747 pgd->widget = scrolled; | |
748 pgd->progress = progress; | |
749 pgd->slider = slider; | |
750 pgd->state = state; | |
751 | |
752 bar_pane_gps_set_map_source(pgd, map_id); | |
753 | |
754 g_object_set(G_OBJECT(CHAMPLAIN_VIEW(view)), "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC, | |
755 "zoom-level", zoom, | |
756 "keep-center-on-resize", TRUE, | |
757 "decel-rate", 1.0, | |
758 "show-license", TRUE, | |
759 "zoom-on-double-click", FALSE, | |
760 "max-zoom-level", 17, | |
761 "min-zoom-level", 1, | |
762 NULL); | |
763 champlain_view_center_on(CHAMPLAIN_VIEW(view), latitude, longitude); | |
764 pgd->centre_map_checked = TRUE; | |
765 g_object_set_data(G_OBJECT(pgd->widget), "pane_data", pgd); | |
766 g_signal_connect(G_OBJECT(pgd->widget), "destroy", G_CALLBACK(bar_pane_gps_destroy), pgd); | |
767 | |
768 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); | |
769 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); | |
770 | |
771 gtk_widget_set_size_request(pgd->widget, -1, height); | |
772 | |
773 clutter_set_motion_events_enabled(TRUE); | |
774 g_signal_connect(G_OBJECT(vbox), "button_press_event", G_CALLBACK(bar_pane_gps_map_keypress_cb), pgd); | |
775 g_signal_connect(pgd->gps_view, "notify::state", G_CALLBACK(bar_pane_gps_view_state_changed_cb), pgd); | |
776 g_signal_connect(pgd->gps_view, "notify::zoom-level", G_CALLBACK(bar_pane_gps_view_state_changed_cb), pgd); | |
777 g_signal_connect(G_OBJECT(slider), "value-changed", G_CALLBACK(bar_pane_gps_slider_changed_cb), pgd); | |
778 | |
779 file_data_register_notify_func(bar_pane_gps_notify_cb, pgd, NOTIFY_PRIORITY_LOW); | |
780 | |
781 pgd->create_markers_id = 0; | |
782 pgd->enable_markers_checked = TRUE; | |
783 pgd->centre_map_checked = TRUE; | |
784 | |
785 return pgd->widget; | |
786 } | |
787 | |
788 GtkWidget *bar_pane_gps_new_from_config(const gchar **attribute_names, const gchar **attribute_values) | |
789 { | |
790 gchar *title = g_strdup(_("GPS Map")); | |
791 gchar *map_id = NULL; | |
792 gboolean expanded = TRUE; | |
793 gint height = 350; | |
794 gint zoom = 7; | |
795 gdouble latitude; | |
796 gdouble longitude; | |
797 /* Latitude and longitude are stored in the config file as an integer of | |
798 * (actual value * 1,000,000). There is no READ_DOUBLE utilty function. | |
799 */ | |
800 gint int_latitude = 54000000; | |
801 gint int_longitude = -4000000; | |
802 gchar *id = g_strdup("gps"); | |
803 GtkWidget *ret; | |
804 | |
805 while (*attribute_names) | |
806 { | |
807 const gchar *option = *attribute_names++; | |
808 const gchar *value = *attribute_values++; | |
809 | |
810 if (READ_CHAR_FULL("title", title)) | |
811 continue; | |
812 if (READ_CHAR_FULL("map-id", map_id)) | |
813 continue; | |
814 /* There is a bug in the libchamplain libraries which prevents correct | |
815 * initialisation if the zoom level starts higher than 8 | |
816 */ | |
817 if (READ_INT_CLAMP_FULL("zoom-level", zoom, 1, 8)) | |
818 continue; | |
819 if (READ_INT_CLAMP_FULL("latitude", int_latitude, -90000000, +90000000)) | |
820 continue; | |
821 if (READ_INT_CLAMP_FULL("longitude", int_longitude, -90000000, +90000000)) | |
822 continue; | |
823 if (READ_BOOL_FULL("expanded", expanded)) | |
824 continue; | |
825 if (READ_INT_FULL("height", height)) | |
826 continue; | |
827 if (READ_CHAR_FULL("id", id)) | |
828 continue; | |
829 | |
830 log_printf("unknown attribute %s = %s\n", option, value); | |
831 } | |
832 | |
833 bar_pane_translate_title(PANE_COMMENT, id, &title); | |
834 latitude = int_latitude / 1000000; | |
835 longitude = int_longitude / 1000000; | |
836 ret = bar_pane_gps_new(id, title, map_id, zoom, latitude, longitude, expanded, height); | |
837 g_free(title); | |
838 g_free(map_id); | |
839 g_free(id); | |
840 return ret; | |
841 } | |
842 | |
843 void bar_pane_gps_update_from_config(GtkWidget *pane, const gchar **attribute_names, | |
844 const gchar **attribute_values) | |
845 { | |
846 PaneGPSData *pgd; | |
847 gint zoom; | |
848 gint int_longitude, int_latitude; | |
849 gdouble longitude, latitude; | |
850 | |
851 pgd = g_object_get_data(G_OBJECT(pane), "pane_data"); | |
852 if (!pgd) | |
853 return; | |
854 | |
855 gchar *title = NULL; | |
856 | |
857 while (*attribute_names) | |
858 { | |
859 const gchar *option = *attribute_names++; | |
860 const gchar *value = *attribute_values++; | |
861 | |
862 if (READ_CHAR_FULL("title", title)) | |
863 continue; | |
864 if (READ_CHAR_FULL("map-id", pgd->map_source)) | |
865 continue; | |
866 if (READ_BOOL_FULL("expanded", pgd->pane.expanded)) | |
867 continue; | |
868 if (READ_INT_FULL("height", pgd->height)) | |
869 continue; | |
870 if (READ_CHAR_FULL("id", pgd->pane.id)) | |
871 continue; | |
872 if (READ_INT_CLAMP_FULL("zoom-level", zoom, 1, 8)) | |
873 { | |
874 g_object_set(G_OBJECT(CHAMPLAIN_VIEW(pgd->gps_view)), "zoom-level", zoom, NULL); | |
875 continue; | |
876 } | |
877 if (READ_INT_CLAMP_FULL("longitude", int_longitude, -90000000, +90000000)) | |
878 { | |
879 longitude = int_longitude / 1000000; | |
880 g_object_set(G_OBJECT(CHAMPLAIN_VIEW(pgd->gps_view)), "longitude", longitude, NULL); | |
881 continue; | |
882 } | |
883 if (READ_INT_CLAMP_FULL("latitude", int_latitude, -90000000, +90000000)) | |
884 { | |
885 latitude = int_latitude / 1000000; | |
886 g_object_set(G_OBJECT(CHAMPLAIN_VIEW(pgd->gps_view)), "latitude", latitude, NULL); | |
887 continue; | |
888 } | |
889 log_printf("unknown attribute %s = %s\n", option, value); | |
890 } | |
891 | |
892 if (title) | |
893 { | |
894 bar_pane_translate_title(PANE_COMMENT, pgd->pane.id, &title); | |
895 gtk_label_set_text(GTK_LABEL(pgd->pane.title), title); | |
896 g_free(title); | |
897 } | |
898 | |
899 gtk_widget_set_size_request(pgd->widget, -1, pgd->height); | |
900 bar_update_expander(pane); | |
901 } | |
902 | |
903 #endif | |
904 #endif | |
905 | |
906 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ |