59#ifdef GDK_WINDOWING_QUARTZ
63#include <gdk/gdkkeysyms.h>
72#define DT_GEOTAG_PARTS_NB 7
81typedef struct dt_lib_tracks_data_t
85} dt_lib_tracks_data_t;
87typedef struct dt_lib_tracks_t
89 dt_lib_tracks_data_t td[1];
92typedef enum dt_tracks_cols_t
94 DT_GEO_TRACKS_ACTIVE = 0,
95 DT_GEO_TRACKS_DATETIME,
99 DT_GEO_TRACKS_TOOLTIP,
100 DT_GEO_TRACKS_NUM_COLS
131 dt_lib_tracks_t *tracks;
134 GtkWidget *gpx_section, *preview_button, *apply_gpx_button,
135 *select_button, *nb_imgs_label;
136 GtkTreeViewColumn *sel_tracks;
166 return _(
"Datetime and GPS");
172 static const char *
v[] = {
"lighttable",
"map", NULL};
174 static const char *
v[] = {
"lighttable", NULL};
211 if(!g_ascii_strcasecmp(filter_info->mime_type,
"application/gpx+xml"))
return TRUE;
213 const gchar *filename = filter_info->filename;
214 const char *cc = filename + strlen(filename);
215 for(; *cc !=
'.' && cc > filename; cc--)
218 if(!g_ascii_strcasecmp(cc,
".gpx"))
return TRUE;
224 const int col,
const int line,
const int ellipsize)
227 gtk_label_set_ellipsize(GTK_LABEL(label), ellipsize);
228 if(ellipsize != PANGO_ELLIPSIZE_NONE)
229 gtk_widget_set_visible(label,
TRUE);
230 gtk_widget_set_halign(label, align);
231 gtk_widget_set_hexpand(label,
TRUE);
232 gtk_grid_attach(GTK_GRID(grid), label, col, line, 1, 1);
239 GDateTime *local_dt = g_date_time_to_timezone(utc_dt, tz_camera);
240 gchar *dts = g_date_time_format(local_dt, full ?
"%Y:%m:%d %H:%M:%S" :
"%H:%M:%S");
241 g_date_time_unref(local_dt);
246 GTimeZone *tz_camera, GTimeZone *tz_utc,
250 GDateTime *dt_offset = g_date_time_add(exif_time, offset);
251 GDateTime *utc_time = g_date_time_to_timezone(dt_offset, tz_utc);
253 g_date_time_unref(exif_time);
254 g_date_time_unref(dt_offset);
264 for(GList *
i =
d->imgs;
i;
i = g_list_next(
i))
270 if((g_date_time_compare(dt,
t->start_dt) >= 0
271 && g_date_time_compare(dt,
t->end_dt) <= 0)
272 || (
n && g_date_time_compare(dt,
t->end_dt) >= 0
273 && g_date_time_compare(dt,
n->start_dt) <= 0))
278 g_date_time_unref(dt);
285static gchar *_utc_timeval_to_utc_text(GDateTime *utc_dt,
const gboolean full)
287 gchar *dts = g_date_time_format(utc_dt, full ?
"%Y:%m:%d %H:%M:%S" :
"%H:%M:%S");
291static gchar *_datetime_tooltip(GDateTime *start, GDateTime *end, GTimeZone *tz)
295 gchar *dtsu = _utc_timeval_to_utc_text(start,
FALSE);
296 gchar *dteu = _utc_timeval_to_utc_text(end,
FALSE);
297 gchar *res = g_strdup_printf(
"%s -> %s LT\n%s -> %s UTC", dtsl, dtel, dtsu, dteu);
308 for(GList *
i =
d->imgs;
i;
i = g_list_next(
i))
319static void _refresh_images_displayed_on_track(
const int segid,
const gboolean active,
dt_lib_module_t *self)
322 for(GList *
i =
d->imgs;
i;
i = g_list_next(
i))
325 if(im->segid == segid && active)
329 im->gl.latitude = NAN;
330 g_date_time_unref(dt);
332 else if(im->segid == segid && !active && im->image)
336 im->gl.latitude = NAN;
340 for(GList *
i =
d->imgs; active &&
i;
i = g_list_next(
i))
343 if(im->segid == segid && !isnan(im->gl.latitude))
352 struct {int32_t imgid;
float latitude;
float longitude;
int count;}
p;
354 p.latitude = im->gl.latitude;
355 p.longitude = im->gl.longitude;
356 p.count = count == 1 ? 0 : count;
357 GList *img = g_list_prepend(NULL, &
p);
370 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->map.gpx_view));
372 gboolean valid = gtk_tree_model_get_iter_first(
model, &iter);
374 for(
int segid = 0; valid && segid <
d->map.nb_tracks; segid++)
378 gtk_tree_model_get(
model, &iter, DT_GEO_TRACKS_ACTIVE, &active,
379 DT_GEO_TRACKS_IMAGES, &nb, -1);
382 valid = gtk_tree_model_iter_next(
model, &iter);
384 d->map.nb_imgs = nb_imgs;
385 gchar *nb = g_strdup_printf(
"%d/%d", nb_imgs,
d->nb_imgs);
386 gtk_label_set_text(GTK_LABEL(
d->map.nb_imgs_label), nb);
393 gtk_widget_set_sensitive(
d->map.preview_button,
d->map.nb_tracks);
394 GtkWidget *label = gtk_bin_get_child(GTK_BIN(
d->map.apply_gpx_button));
395 gtk_label_set_text(GTK_LABEL(label),
d->offset ? _(
"apply offset and geo-location")
396 : _(
"apply geo-location"));
397 gtk_widget_set_tooltip_text(
d->map.apply_gpx_button,
398 d->offset ? _(
"apply offset and geo-location to matching images"
399 "\ndouble operation: two ctrl-Z to undo")
400 : _(
"apply geo-location to matching images"));
401 gtk_widget_set_sensitive(
d->map.apply_gpx_button,
d->map.nb_imgs);
402 gtk_widget_set_sensitive(
d->map.select_button,
403 d->map.nb_imgs &&
d->map.nb_imgs !=
d->nb_imgs);
410 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->map.gpx_view));
414 gboolean valid = gtk_tree_model_get_iter_first(
model, &iter);
418 gtk_tree_model_get(
model, &iter, DT_GEO_TRACKS_ACTIVE, &active, -1);
421 for(GList *
i =
d->imgs;
i;
i = g_list_next(
i))
424 if(im->segid == segid)
425 imgs = g_list_prepend(imgs, GINT_TO_POINTER(im->imgid));
428 valid = gtk_tree_model_iter_next(
model, &iter);
437 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->map.gpx_view));
439 const gboolean preview = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
d->map.preview_button));
440 gboolean valid = gtk_tree_model_get_iter_first(
model, &iter);
441 for(
int segid = 0; valid && segid <
d->map.nb_tracks; segid++)
444 gtk_tree_model_get(
model, &iter, DT_GEO_TRACKS_ACTIVE, &active, -1);
445 _refresh_images_displayed_on_track(segid, active && preview, self);
446 valid = gtk_tree_model_iter_next(
model, &iter);
450static gboolean _update_map_box(
const guint segid, GList *pts,
dt_lib_module_t *self)
456 d->map.tracks->td[segid].map_box.lon1 = 180.0;
457 d->map.tracks->td[segid].map_box.lon2 = -180.0;
458 d->map.tracks->td[segid].map_box.lat1 = -90.0;
459 d->map.tracks->td[segid].map_box.lat2 = 90.0;
460 for(GList *pt = pts; pt; pt = g_list_next(pt))
463 if(
p->lon <
d->map.tracks->td[segid].map_box.lon1)
464 d->map.tracks->td[segid].map_box.lon1 =
MAX(-180.0,
p->lon);
465 if(
p->lon >
d->map.tracks->td[segid].map_box.lon2)
466 d->map.tracks->td[segid].map_box.lon2 =
MIN(180.0,
p->lon);
467 if(
p->lat >
d->map.tracks->td[segid].map_box.lat1)
468 d->map.tracks->td[segid].map_box.lat1 =
MIN(90.0,
p->lat);
469 if(
p->lat <
d->map.tracks->td[segid].map_box.lat2)
470 d->map.tracks->td[segid].map_box.lat2 =
MAX(-90.0,
p->lat);
479 for(
int i = 0;
i <
d->map.nb_tracks;
i++)
481 if(
d->map.tracks->td[
i].track)
483 if(
d->map.tracks->td[
i].map_box.lon1 < lon1)
484 lon1 =
d->map.tracks->td[
i].map_box.lon1;
485 if(
d->map.tracks->td[
i].map_box.lon2 > lon2)
486 lon2 =
d->map.tracks->td[
i].map_box.lon2;
487 if(
d->map.tracks->td[
i].map_box.lat1 > lat1)
488 lat1 =
d->map.tracks->td[
i].map_box.lat1;
489 if(
d->map.tracks->td[
i].map_box.lat2 < lat2)
490 lat2 =
d->map.tracks->td[
i].map_box.lat2;
493 const gboolean grow = lon1 <
d->map.map_box.lon1 || lon2 >
d->map.map_box.lon1 ||
494 lat1 >
d->map.map_box.lat1 || lat2 <
d->map.map_box.lat2;
495 d->map.map_box.lon1 = lon1;
496 d->map.map_box.lon2 = lon2;
497 d->map.map_box.lat1 = lat1;
498 d->map.map_box.lat2 = lat2;
508 for(
int i = 0;
i <
d->map.nb_tracks;
i++)
510 if(
d->map.tracks->td[
i].track)
513 d->map.tracks->td[
i].track);
514 d->map.tracks->td[
i].track = NULL;
526GdkRGBA color[] = {(GdkRGBA){.red = 1.0, .green = 0.0, .blue = 0.0, .alpha = 0.5 },
527 (GdkRGBA){.red = 0.0, .green = 1.0, .blue = 1.0, .alpha = 0.5 },
528 (GdkRGBA){.red = 0.0, .green = 0.0, .blue = 1.0, .alpha = 0.5 },
529 (GdkRGBA){.red = 1.0, .green = 1.0, .blue = 0.0, .alpha = 0.5 },
530 (GdkRGBA){.red = 0.0, .green = 1.0, .blue = 0.0, .alpha = 0.5 },
531 (GdkRGBA){.red = 1.0, .green = 0.0, .blue = 1.0, .alpha = 0.5 }};
533static gboolean _refresh_display_track(
const gboolean active,
const int segid,
dt_lib_module_t *self)
536 gboolean grow =
FALSE;
540 if(!
d->map.tracks->td[segid].track)
543 osm_gps_map_track_set_color((OsmGpsMapTrack *)
d->map.tracks->td[segid].track, &color[segid % 6]);
544 grow = _update_map_box(segid, pts, self);
550 if(
d->map.tracks->td[segid].track != NULL)
552 d->map.tracks->td[segid].track);
553 d->map.tracks->td[segid].track = NULL;
554 _update_map_box(segid, NULL, self);
562 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->map.gpx_view));
564 gboolean valid = gtk_tree_model_get_iter_first(
model, &iter);
565 gboolean grow =
FALSE;
566 for(
int segid = 0; valid && segid <
d->map.nb_tracks; segid++)
569 gtk_tree_model_get(
model, &iter, DT_GEO_TRACKS_ACTIVE, &active, -1);
570 grow = _refresh_display_track(active, segid, self) ?
TRUE : grow;
571 valid = gtk_tree_model_iter_next(
model, &iter);
577 d->map.map_box.lon2,
d->map.map_box.lat2);
579 _refresh_displayed_images(self);
582static void _track_seg_toggled(GtkCellRendererToggle *cell_renderer, gchar *path_str,
dt_lib_module_t *self)
585 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->map.gpx_view));
587 GtkTreePath *
path = gtk_tree_path_new_from_string(path_str);
591 gtk_tree_model_get_iter(
model, &iter, path);
592 gtk_tree_model_get(
model, &iter, DT_GEO_TRACKS_ACTIVE, &active,
593 DT_GEO_TRACKS_SEGID, &segid, -1);
594 gtk_list_store_set(GTK_LIST_STORE(
model), &iter, DT_GEO_TRACKS_ACTIVE, !active, -1);
595 gtk_tree_path_free(path);
598 if(_refresh_display_track(active, segid, self))
601 d->map.map_box.lon2,
d->map.map_box.lat2);
604 const gboolean preview = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
d->map.preview_button));
605 _refresh_images_displayed_on_track(segid, active && preview, self);
606 _update_nb_images(self);
607 _update_buttons(self);
610static void _all_tracks_toggled(GtkTreeViewColumn *column,
dt_lib_module_t *self)
613 GtkWidget *toggle = gtk_tree_view_column_get_widget(column);
614 gboolean active = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle));
615 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle), active);
617 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->map.gpx_view));
619 gboolean valid = gtk_tree_model_get_iter_first(
model, &iter);
620 gboolean grow =
FALSE;
621 for(
int segid = 0; valid && segid <
d->map.nb_tracks; segid++)
623 gtk_list_store_set(GTK_LIST_STORE(
model), &iter,
624 DT_GEO_TRACKS_ACTIVE, active,-1);
625 grow = _refresh_display_track(active, segid, self) ?
TRUE : grow;
626 valid = gtk_tree_model_iter_next(
model, &iter);
631 d->map.map_box.lon2,
d->map.map_box.lat2);
633 _refresh_displayed_images(self);
634 _update_nb_images(self);
635 _update_buttons(self);
640 GList *imgs = _get_images_on_active_tracks(self);
647static void _images_preview_toggled(GtkToggleButton *button,
dt_lib_module_t *self)
649 _refresh_displayed_images(self);
658 _remove_images_from_map(self);
659 for(GList *
i =
d->imgs;
i;
i = g_list_next(
i))
662 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->map.gpx_view));
664 gboolean valid = gtk_tree_model_get_iter_first(
model, &iter);
665 for(GList *ts = trkseg; ts && valid; ts = g_list_next(ts))
671 gtk_tree_model_get(
model, &iter, DT_GEO_TRACKS_ACTIVE, &active, -1);
672 gchar *
tooltip = _datetime_tooltip(
t->start_dt,
t->end_dt,
d->tz_camera);
673 gtk_list_store_set(GTK_LIST_STORE(
model), &iter,
674 DT_GEO_TRACKS_DATETIME, dts,
675 DT_GEO_TRACKS_POINTS,
t->nb_trkpt,
676 DT_GEO_TRACKS_IMAGES, nb_imgs,
677 DT_GEO_TRACKS_TOOLTIP,
tooltip,
681 valid = gtk_tree_model_iter_next(
model, &iter);
683 _update_nb_images(self);
684 _refresh_displayed_images(self);
685 _update_buttons(self);
691 _remove_tracks_from_map(self);
692 d->map.gpx =
dt_gpx_new(gtk_label_get_text(GTK_LABEL(
d->map.gpx_file)));
693 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->map.gpx_view));
695 gtk_tree_view_set_model(GTK_TREE_VIEW(
d->map.gpx_view), NULL);
696 gtk_list_store_clear(GTK_LIST_STORE(
model));
699 d->map.nb_tracks = g_list_length(trkseg);
700 d->map.tracks = g_malloc0(
sizeof(dt_lib_tracks_data_t) *
d->map.nb_tracks);
702 _remove_images_from_map(self);
703 for(GList *
i =
d->imgs;
i;
i = g_list_next(
i))
707 const gboolean active = gtk_toggle_button_get_active(
708 GTK_TOGGLE_BUTTON(gtk_tree_view_column_get_widget(
d->map.sel_tracks)));
710 for(GList *ts = trkseg; ts; ts = g_list_next(ts))
714 gchar *
tooltip = _datetime_tooltip(
t->start_dt,
t->end_dt,
d->tz_camera);
716 gtk_list_store_append(GTK_LIST_STORE(
model), &iter);
717 gtk_list_store_set(GTK_LIST_STORE(
model), &iter,
718 DT_GEO_TRACKS_ACTIVE, active,
719 DT_GEO_TRACKS_DATETIME, dts,
720 DT_GEO_TRACKS_POINTS,
t->nb_trkpt,
721 DT_GEO_TRACKS_IMAGES, nb_imgs,
722 DT_GEO_TRACKS_SEGID, segid,
723 DT_GEO_TRACKS_TOOLTIP,
tooltip,
729 gtk_tree_view_set_model(GTK_TREE_VIEW(
d->map.gpx_view),
model);
730 g_object_unref(
model);
732 gtk_tree_view_column_set_clickable(
d->map.sel_tracks,
TRUE);
733 _update_nb_images(self);
734 _update_buttons(self);
735 _refresh_display_all_tracks(self);
742 GList *imgs = _get_images_on_active_tracks(self);
747 GList *imgs2 = g_list_copy(imgs);
753 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->map.preview_button),
FALSE);
759 gtk_widget_set_visible(
d->gpx_button, !
d->map.view);
760 gtk_widget_set_visible(
d->map.gpx_section,
d->map.view);
767 if(old_view != new_view)
769 d->map.view = !g_strcmp0(new_view->module_name,
"map");
773 _refresh_track_list(self);
779static void _geotag_changed(gpointer instance, GList *imgs,
const int locid,
dt_lib_module_t *self)
782 if(
d->map.view && !locid)
784 _refresh_displayed_images(self);
785 _update_nb_images(self);
786 _update_buttons(self);
793 for(GList *
i =
d->imgs;
i;
i = g_list_next(
i))
806 gboolean res =
FALSE;
807 GtkTreePath *
path = NULL;
808 GtkTreeViewColumn *column = NULL;
811 gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(
view),
x, y, &tx, &ty);
812 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(
view), tx, ty, &path, &column, NULL, NULL))
814 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
view));
816 if(gtk_tree_model_get_iter(
model, &iter, path))
819 gtk_tree_model_get(
model, &iter, DT_GEO_TRACKS_TOOLTIP, &text, -1);
820 if(text && text[0] !=
'\0')
822 gtk_tooltip_set_text(
tooltip, text);
828 gtk_tree_path_free(path);
837 GtkWidget *dialog = gtk_dialog_new_with_buttons(
838 _(
"GPX file track segments"), GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
839 _(
"done"), GTK_RESPONSE_CANCEL, NULL);
841 gchar *filedir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
845 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
846 GtkWidget *w = gtk_scrolled_window_new(NULL, NULL);
848 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
849 gtk_box_pack_start(GTK_BOX(area), w,
TRUE,
TRUE, 0);
857 _set_up_label(_(
"name"), GTK_ALIGN_START, grid, 0, line, PANGO_ELLIPSIZE_NONE);
858 _set_up_label(_(
"start time"), GTK_ALIGN_START, grid, 1, line, PANGO_ELLIPSIZE_NONE);
859 _set_up_label(_(
"end time"), GTK_ALIGN_START, grid, 2, line, PANGO_ELLIPSIZE_NONE);
860 _set_up_label(_(
"points"), GTK_ALIGN_CENTER, grid, 3, line, PANGO_ELLIPSIZE_NONE);
861 _set_up_label(_(
"images"), GTK_ALIGN_CENTER, grid, 4, line, PANGO_ELLIPSIZE_NONE);
863 for(GList *
i =
d->imgs;
i;
i = g_list_next(
i))
868 for(GList *ts = trkseg; ts; ts = g_list_next(ts))
875 total_imgs += nb_imgs;
877 _set_up_label(
t->name, GTK_ALIGN_START, grid, 0, line, PANGO_ELLIPSIZE_NONE);
878 _set_up_label(dts, GTK_ALIGN_START, grid, 1, line, PANGO_ELLIPSIZE_NONE);
879 _set_up_label(dte, GTK_ALIGN_START, grid, 2, line, PANGO_ELLIPSIZE_NONE);
880 char *nb = g_strdup_printf(
"%d",
t->nb_trkpt);
881 _set_up_label(nb, GTK_ALIGN_CENTER, grid, 3, line, PANGO_ELLIPSIZE_NONE);
883 nb = g_strdup_printf(
"%d", nb_imgs);
884 _set_up_label(nb, GTK_ALIGN_CENTER, grid, 4, line, PANGO_ELLIPSIZE_NONE);
887 total_pts +=
t->nb_trkpt;
892 char *nb = g_strdup_printf(
"%d", total_pts);
893 _set_up_label(nb, GTK_ALIGN_CENTER, grid, 3, line, PANGO_ELLIPSIZE_NONE);
895 nb = g_strdup_printf(
"%d / %d", total_imgs,
d->nb_imgs);
896 _set_up_label(nb, GTK_ALIGN_CENTER, grid, 4, line, PANGO_ELLIPSIZE_NONE);
901 gtk_container_add(GTK_CONTAINER(w), grid);
903#ifdef GDK_WINDOWING_QUARTZ
906 gtk_widget_show_all(dialog);
907 gtk_dialog_run(GTK_DIALOG(dialog));
909 gtk_widget_destroy(dialog);
919 _remove_images_from_map(self);
929 "SELECT imgid FROM main.selected_images",
931 while(sqlite3_step(stmt) == SQLITE_ROW)
933 const int32_t imgid = sqlite3_column_int(stmt, 0);
944 d->imgs = g_list_prepend(
d->imgs, img);
947 sqlite3_finalize(stmt);
955 GtkWidget *filechooser = gtk_file_chooser_dialog_new(
956 _(
"open GPX file"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_OPEN,
957 _(
"preview"), GTK_RESPONSE_ACCEPT,
958 _(
"_cancel"), GTK_RESPONSE_CANCEL,
959 _(
"_open"), GTK_RESPONSE_OK, (
char *)NULL);
960#ifdef GDK_WINDOWING_QUARTZ
966 GtkFileFilter *filter;
967 filter = GTK_FILE_FILTER(gtk_file_filter_new());
968 gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME | GTK_FILE_FILTER_MIME_TYPE,
970 gtk_file_filter_set_name(filter, _(
"GPS data exchange format"));
971 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
973 filter = GTK_FILE_FILTER(gtk_file_filter_new());
974 gtk_file_filter_add_pattern(filter,
"*");
975 gtk_file_filter_set_name(filter, _(
"all files"));
976 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
981 int res = gtk_dialog_run(GTK_DIALOG(filechooser));
982 while(res == GTK_RESPONSE_ACCEPT)
985 res = gtk_dialog_run(GTK_DIALOG(filechooser));
987 if(res == GTK_RESPONSE_OK)
991 gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
996 gtk_label_set_text(GTK_LABEL(
d->map.gpx_file), filename);
997 _show_gpx_tracks(self);
998 gtk_widget_set_visible(
d->map.gpx_view,
d->map.view);
1013 gtk_widget_destroy(filechooser);
1023 gboolean utc_neg_a = g_str_has_prefix(tz_a->
display,
"(UTC-");
1024 gboolean utc_neg_b = g_str_has_prefix(tz_b->
display,
"(UTC-");
1026 gboolean utc_pos_a = g_str_has_prefix(tz_a->
display,
"(UTC+");
1027 gboolean utc_pos_b = g_str_has_prefix(tz_b->
display,
"(UTC+");
1029 if(utc_neg_a && utc_neg_b)
1031 char *iter_a = tz_a->
display + strlen(
"(UTC-");
1032 char *iter_b = tz_b->
display + strlen(
"(UTC-");
1034 while(((*iter_a >=
'0' && *iter_a <=
'9') || *iter_a ==
':') &&
1035 ((*iter_b >=
'0' && *iter_b <=
'9') || *iter_b ==
':'))
1037 if(*iter_a != *iter_b)
return *iter_b - *iter_a;
1042 else if(utc_neg_a && utc_pos_b)
return -1;
1043 else if(utc_pos_a && utc_neg_b)
return 1;
1052 GList *timezones = NULL;
1061#define MAX_LINE_LENGTH 256
1066 gchar *zone_tab = g_strdup(
"/usr/share/zoneinfo/zone.tab");
1067 if(!g_file_test(zone_tab, G_FILE_TEST_IS_REGULAR))
1070 zone_tab = g_strdup(
"/usr/lib/zoneinfo/zone.tab");
1071 if(!g_file_test(zone_tab, G_FILE_TEST_IS_REGULAR))
1074 zone_tab = g_build_filename(g_getenv(
"TZDIR"),
"zone.tab", NULL);
1075 if(!g_file_test(zone_tab, G_FILE_TEST_IS_REGULAR))
1080 zone_tab = g_build_filename(datadir,
"zone.tab", NULL);
1081 if(!g_file_test(zone_tab, G_FILE_TEST_IS_REGULAR))
1092 fp = g_fopen(zone_tab,
"r");
1099 if(line[0] ==
'#' || line[0] ==
'\0')
continue;
1100 gchar **tokens = g_strsplit_set(line,
" \t\n", 0);
1103 int n_found = -1,
i;
1104 for(
i = 0; tokens[
i] && n_found < 2;
i++)
if(*tokens[
i]) n_found++;
1110 gchar *
name = g_strdup(tokens[
i - 1]);
1117 size_t last_char = strlen(
name) - 1;
1118 if(
name[last_char] ==
'\n')
name[last_char] =
'\0';
1122 timezones = g_list_prepend(timezones, tz_tuple);
1132 utc->name =
utc->display;
1133 timezones = g_list_prepend(timezones,
utc);
1135#undef MAX_LINE_LENGTH
1139 char *keypath =
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\";
1142 if(RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1146 &hKey) == ERROR_SUCCESS)
1148 DWORD n_subkeys, max_subkey_len;
1150 if(RegQueryInfoKey(hKey,
1161 NULL) == ERROR_SUCCESS)
1163 wchar_t *subkeyname = (
wchar_t *)malloc(
sizeof(
wchar_t) * (max_subkey_len + 1));
1165 for(DWORD
i = 1;
i < n_subkeys;
i++)
1167 DWORD subkeyname_length = max_subkey_len + 1;
1168 if(RegEnumKeyExW(hKey,
1175 NULL) == ERROR_SUCCESS)
1178 char *subkeyname_utf8 = g_utf16_to_utf8(subkeyname, -1, NULL, NULL, NULL);
1179 char *subkeypath_utf8 = g_strconcat(keypath,
"\\", subkeyname_utf8, NULL);
1180 wchar_t *subkeypath = g_utf8_to_utf16(subkeypath_utf8, -1, NULL, NULL, NULL);
1181 if(RegGetValueW(HKEY_LOCAL_MACHINE,
1187 &buffer_size) == ERROR_SUCCESS)
1189 wchar_t *display_name = (
wchar_t *)malloc(buffer_size);
1190 if(RegGetValueW(HKEY_LOCAL_MACHINE,
1196 &buffer_size) == ERROR_SUCCESS)
1200 tz->
name = subkeyname_utf8;
1201 tz->
display = g_utf16_to_utf8(display_name, -1, NULL, NULL, NULL);
1202 timezones = g_list_prepend(timezones, tz);
1204 subkeyname_utf8 = NULL;
1232 const gboolean neg = offset_int < 0;
1233 gtk_label_set_text(GTK_LABEL(
d->of.sign), neg ?
"- " :
"");
1235 GTimeSpan off = neg ? -offset_int : offset_int;
1238 gint64 total_ms = off / 1000;
1241 gint ms = total_ms % 1000;
1242 gint64 total_s = total_ms / 1000;
1244 gint s = total_s % 60;
1245 gint64 total_m = total_s / 60;
1247 gint
m = total_m % 60;
1248 gint64 total_h = total_m / 60;
1250 gint h = total_h % 24;
1251 gint64 total_d = total_h / 24;
1253 gint D = total_d % 100;
1258 g_snprintf(text,
sizeof(text),
"%03d", ms);
1259 gtk_entry_set_text(GTK_ENTRY(
d->of.widget[6]), text);
1261 g_snprintf(text,
sizeof(text),
"%02d", s);
1262 gtk_entry_set_text(GTK_ENTRY(
d->of.widget[5]), text);
1264 g_snprintf(text,
sizeof(text),
"%02d",
m);
1265 gtk_entry_set_text(GTK_ENTRY(
d->of.widget[4]), text);
1267 g_snprintf(text,
sizeof(text),
"%02d", h);
1268 gtk_entry_set_text(GTK_ENTRY(
d->of.widget[3]), text);
1270 g_snprintf(text,
sizeof(text),
"%02d", D);
1271 gtk_entry_set_text(GTK_ENTRY(
d->of.widget[2]), text);
1275 gtk_label_set_text(GTK_LABEL(
d->of.sign),
"");
1277 gtk_entry_set_text(GTK_ENTRY(
d->of.widget[
i]),
"-");
1279 const gboolean locked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
d->lock_offset));
1280 gtk_widget_set_sensitive(
d->apply_offset,
d->imgid && valid && !off2 && offset_int);
1281 gtk_widget_set_sensitive(
d->lock_offset, locked || (
d->imgid && valid && !off2 && offset_int));
1282 gtk_widget_set_sensitive(
d->apply_datetime,
d->imgid && !locked);
1285 _update_buttons(self);
1297 char value[8] = {0};
1298 snprintf(
value,
sizeof(
value),
"%04d", g_date_time_get_year(datetime));
1299 gtk_entry_set_text(GTK_ENTRY(dtw->
widget[0]),
value);
1300 snprintf(
value,
sizeof(
value),
"%02d", g_date_time_get_month(datetime));
1301 gtk_entry_set_text(GTK_ENTRY(dtw->
widget[1]),
value);
1302 snprintf(
value,
sizeof(
value),
"%02d", g_date_time_get_day_of_month(datetime));
1303 gtk_entry_set_text(GTK_ENTRY(dtw->
widget[2]),
value);
1304 snprintf(
value,
sizeof(
value),
"%02d", g_date_time_get_hour(datetime));
1305 gtk_entry_set_text(GTK_ENTRY(dtw->
widget[3]),
value);
1306 snprintf(
value,
sizeof(
value),
"%02d", g_date_time_get_minute(datetime));
1307 gtk_entry_set_text(GTK_ENTRY(dtw->
widget[4]),
value);
1308 snprintf(
value,
sizeof(
value),
"%02d", g_date_time_get_second(datetime));
1309 gtk_entry_set_text(GTK_ENTRY(dtw->
widget[5]),
value);
1310 snprintf(
value,
sizeof(
value),
"%03d", (
int)(g_date_time_get_microsecond(datetime) * 0.001));
1311 gtk_entry_set_text(GTK_ENTRY(dtw->
widget[6]),
value);
1316 gtk_entry_set_text(GTK_ENTRY(dtw->
widget[
i]),
"-");
1327 const int year = atoi(gtk_entry_get_text(GTK_ENTRY(
d->dt.widget[0])));
1328 const int month = atoi(gtk_entry_get_text(GTK_ENTRY(
d->dt.widget[1])));
1329 const int day = atoi(gtk_entry_get_text(GTK_ENTRY(
d->dt.widget[2])));
1330 const int hour = atoi(gtk_entry_get_text(GTK_ENTRY(
d->dt.widget[3])));
1331 const int minute = atoi(gtk_entry_get_text(GTK_ENTRY(
d->dt.widget[4])));
1332 const int second = atoi(gtk_entry_get_text(GTK_ENTRY(
d->dt.widget[5])));
1333 const int millisecond = atoi(gtk_entry_get_text(GTK_ENTRY(
d->dt.widget[6])));
1334 const gdouble second2 = (gdouble)second + (gdouble)millisecond * 0.001;
1347 g_date_time_unref(
d->datetime);
1348 d->datetime = datetime;
1349 d->offset = g_date_time_difference(
d->datetime,
d->datetime0);
1353 _refresh_track_list(self);
1373 if(imgid < 0)
return NULL;
1374 GDateTime *datetime = NULL;
1379 if(datetime_s[0] !=
'\0')
1391 const gboolean locked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
d->lock_offset));
1394 g_date_time_unref(
d->datetime0);
1395 d->datetime0 = datetime;
1399 GDateTime *datetime2 = g_date_time_add(datetime,
d->offset);
1407 g_date_time_ref(datetime);
1416 for(GList *
i = imgs;
i;
i = g_list_next(
i))
1418 if(GPOINTER_TO_INT(
i->data) ==
d->imgid)
1427 _refresh_selected_images_datetime(self);
1428 _refresh_track_list(self);
1450 _refresh_track_list(self);
1463 if(w ==
d->dt.widget[
i])
break;
1469 if (delta_y < 0) increment = 1;
1470 else if (delta_y > 0) increment = -1;
1476 GDateTime *datetime;
1480 datetime = g_date_time_add_years(
d->datetime, increment);
1483 datetime = g_date_time_add_months(
d->datetime, increment);
1486 datetime = g_date_time_add_days(
d->datetime, increment);
1489 datetime = g_date_time_add_hours(
d->datetime, increment);
1492 datetime = g_date_time_add_minutes(
d->datetime, increment);
1495 datetime = g_date_time_add_seconds(
d->datetime, increment);
1498 datetime = g_date_time_add(
d->datetime, increment * 1000);
1514 gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(flow), 2);
1522 if(
i == 0 &&
type == 2)
1524 dt->
sign = gtk_label_new(
"");
1527 if(
i >= 2 ||
type != 2)
1529 dt->
widget[
i] = gtk_entry_new();
1531 gtk_entry_set_width_chars(GTK_ENTRY(dt->
widget[
i]),
i == 0 ? 4 :
i == 6 ? 3 : 2);
1532 gtk_entry_set_alignment(GTK_ENTRY(dt->
widget[
i]), 0.5);
1544 if(
i == 2 ||
i == 6)
1546 gtk_widget_set_halign(GTK_WIDGET(box), GTK_ALIGN_END);
1547 gtk_widget_set_hexpand(GTK_WIDGET(box),
TRUE);
1548 gtk_container_add(GTK_CONTAINER(flow), GTK_WIDGET(box));
1551 else if(
i > 2 ||
type != 2)
1553 GtkWidget *label = gtk_label_new(
i < 2 ?
"-" :
i == 5 ?
"." :
":");
1555 g_object_set_data(G_OBJECT(dt->
widget[
i]),
"msec_label", label);
1556 gtk_box_pack_start(box, label,
FALSE,
FALSE, 0);
1560 gtk_container_foreach(GTK_CONTAINER(flow), (GtkCallback)gtk_widget_set_can_focus, GINT_TO_POINTER(
FALSE));
1572 case GDK_KEY_Escape:
1578 _refresh_track_list(self);
1595 case GDK_KEY_Delete:
1596 case GDK_KEY_BackSpace:
1605 case GDK_KEY_ISO_Left_Tab:
1606 case GDK_KEY_Return:
1608 g_signal_emit_by_name(
d->dt.widget[0],
"changed");
1619 const gchar *tz = gtk_entry_get_text(GTK_ENTRY(
d->timezone));
1622 for(GList *iter =
d->timezones; iter; iter = g_list_next(iter))
1625 if(!strcmp(tz_tuple->
display, tz))
1628 if(
d->tz_camera) g_time_zone_unref(
d->tz_camera);
1629 d->tz_camera = !
name ? g_time_zone_new_utc() : g_time_zone_new(
name);
1631 gtk_entry_set_text(GTK_ENTRY(
d->timezone),
name ?
name :
"UTC");
1632 gtk_label_set_text(GTK_LABEL (
d->timezone_changed),
"");
1637 _refresh_track_list(self);
1647 case GDK_KEY_Return:
1651 case GDK_KEY_Escape:
1656 gtk_label_set_text(GTK_LABEL (
d->timezone_changed),
" *");
1671 gboolean res =
FALSE;
1673 GtkEditable *e = (GtkEditable *)gtk_entry_completion_get_entry(completion);
1674 if(!GTK_IS_EDITABLE(e))
1677 GtkTreeModel *
model = gtk_entry_completion_get_model(completion);
1678 const int column = gtk_entry_completion_get_text_column(completion);
1680 if(gtk_tree_model_get_column_type(
model, column) != G_TYPE_STRING)
1684 gtk_tree_model_get(
model, iter, column, &tag, -1);
1687 char *normalized = g_utf8_normalize(tag, -1, G_NORMALIZE_ALL);
1690 char *casefold = g_utf8_casefold(normalized, -1);
1693 res = g_strstr_len(casefold, -1,
key) != NULL;
1707 const gboolean locked = gtk_toggle_button_get_active(button);
1710 gtk_widget_set_sensitive(
d->dt.widget[
i], !locked);
1712 gtk_widget_set_sensitive(
d->apply_datetime,
d->imgid && !locked);
1716 const float xalign,
const int m_col,
1717 const int ellipsize)
1719 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
1720 GtkTreeViewColumn *column = gtk_tree_view_column_new();
1721 gtk_tree_view_column_pack_start(column, renderer,
TRUE);
1722 gtk_tree_view_column_set_attributes(column, renderer,
"text", m_col, NULL);
1723 gtk_tree_view_column_set_expand(column, expand);
1725 gtk_widget_show(label);
1726 gtk_tree_view_column_set_widget(column, label);
1727 gtk_label_set_ellipsize(GTK_LABEL(label), expand ? PANGO_ELLIPSIZE_MIDDLE : PANGO_ELLIPSIZE_NONE);
1728 g_object_set(renderer,
"xalign", xalign,
"ellipsize", ellipsize, NULL);
1734 const gboolean milliseconds =
dt_conf_get_bool(
"lighttable/ui/milliseconds");
1735 gtk_widget_set_visible(
d->dt.widget[6], milliseconds);
1736 gtk_widget_set_visible(
d->dt0.widget[6], milliseconds);
1737 gtk_widget_set_visible(
d->of.widget[6], milliseconds);
1738 gtk_widget_set_visible(g_object_get_data(G_OBJECT(
d->dt.widget[5]),
"msec_label"), milliseconds);
1739 gtk_widget_set_visible(g_object_get_data(G_OBJECT(
d->dt0.widget[5]),
"msec_label"), milliseconds);
1740 gtk_widget_set_visible(g_object_get_data(G_OBJECT(
d->of.widget[5]),
"msec_label"), milliseconds);
1751 self->
data = (
void *)
d;
1755 GtkGrid *grid = GTK_GRID(gtk_grid_new());
1760 gtk_grid_attach(grid, label, 0, line, 2, 1);
1761 gtk_widget_set_tooltip_text(label, _(
"enter the new date/time (YYYY:MM:DD hh:mm:ss[.sss])"
1762 "\nkey in the new numbers or scroll over the cell"));
1765 gtk_grid_attach(grid, box, 2, line++, 2, 1);
1768 gtk_grid_attach(grid, label, 0, line, 2, 1);
1771 gtk_grid_attach(grid, box, 2, line++, 2, 1);
1774 gtk_grid_attach(grid, label, 0, line, 2, 1);
1775 gtk_widget_set_tooltip_text(label, _(
"offset or difference ([-]dd hh:mm:ss[.sss])"));
1778 gtk_widget_set_tooltip_text(
d->lock_offset, _(
"lock date/time offset value to apply it onto another selection"));
1779 gtk_widget_set_halign(
d->lock_offset, GTK_ALIGN_START);
1780 gtk_grid_attach(grid,
d->lock_offset, 2, line, 1, 1);
1784 gtk_grid_attach(grid, box, 3, line++, 1, 1);
1788 _(
"apply offset to selected images"), 0, 0);
1789 gtk_grid_attach(grid,
d->apply_offset , 0, line, 2, 1);
1792 _(
"apply the same date/time to selected images"), 0, 0);
1793 gtk_grid_attach(grid,
d->apply_datetime , 2, line++, 2, 1);
1799 gtk_grid_attach(grid, label, 0, line, 2, 1);
1801 d->timezone = gtk_entry_new();
1803 gtk_widget_set_tooltip_text(
d->timezone, _(
"start typing to show a list of permitted values and select your timezone.\npress enter to confirm, so that the asterisk * disappears"));
1807 gtk_box_pack_start(GTK_BOX(timezone_box),
d->timezone,
TRUE,
TRUE, 0);
1808 gtk_box_pack_end(GTK_BOX(timezone_box),
d->timezone_changed,
FALSE,
FALSE, 0);
1810 gtk_grid_attach(grid, timezone_box, 2, line++, 2, 1);
1812 GtkCellRenderer *renderer;
1813 GtkTreeIter tree_iter;
1814 GtkListStore *
model = gtk_list_store_new(2, G_TYPE_STRING , G_TYPE_STRING );
1815 GtkWidget *tz_selection = gtk_combo_box_new_with_model(GTK_TREE_MODEL(
model));
1816 renderer = gtk_cell_renderer_text_new();
1817 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(tz_selection), renderer,
FALSE);
1818 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(tz_selection), renderer,
"text", 0, NULL);
1821 d->tz_camera = (
IS_NULL_PTR(tz)) ? g_time_zone_new_utc() : g_time_zone_new(tz);
1822 for(GList *iter =
d->timezones; iter; iter = g_list_next(iter))
1825 gtk_list_store_append(
model, &tree_iter);
1826 gtk_list_store_set(
model, &tree_iter, 0, tz_tuple->
display, 1, tz_tuple->
name, -1);
1827 if(!strcmp(tz_tuple->
name, tz))
1828 gtk_entry_set_text(GTK_ENTRY(
d->timezone), tz_tuple->
display);
1833 GtkEntryCompletion *completion = gtk_entry_completion_new();
1834 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(
model));
1835 gtk_entry_completion_set_text_column(completion, 0);
1836 gtk_entry_completion_set_inline_completion(completion,
TRUE);
1837 gtk_entry_completion_set_popup_set_width(completion,
FALSE);
1839 gtk_entry_completion_set_minimum_key_length(completion, 0);
1840 gtk_entry_set_completion(GTK_ENTRY(
d->timezone), completion);
1842 g_signal_connect(G_OBJECT(
d->timezone),
"focus-out-event", G_CALLBACK(
_timezone_focus_out), self);
1846 _(
"parses a GPX file and updates location of selected images"), 0, 0);
1847 gtk_grid_attach(grid,
d->gpx_button, 0, line++, 4, 1);
1848 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(grid),
TRUE,
TRUE, 0);
1852 grid = GTK_GRID(gtk_grid_new());
1857 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(
d->map.gpx_section),
TRUE,
TRUE, 0);
1860 gtk_grid_attach(grid, label, 0, line++, 4, 1);
1863 gtk_widget_set_hexpand(
d->map.gpx_button,
FALSE);
1864 gtk_widget_set_halign(
d->map.gpx_button, GTK_ALIGN_START);
1865 gtk_widget_set_tooltip_text(
d->map.gpx_button, _(
"select a GPX track file..."));
1866 gtk_grid_attach(grid,
d->map.gpx_button, 0, line, 1, 1);
1870 gtk_label_set_ellipsize(GTK_LABEL(
d->map.gpx_file ), PANGO_ELLIPSIZE_MIDDLE);
1871 gtk_widget_set_hexpand(
d->map.gpx_file,
TRUE);
1872 gtk_grid_attach(grid,
d->map.gpx_file, 1, line++, 3, 1);
1873 gtk_box_pack_start(GTK_BOX(
d->map.gpx_section), GTK_WIDGET(grid),
TRUE,
TRUE, 0);
1875 model = gtk_list_store_new(DT_GEO_TRACKS_NUM_COLS, G_TYPE_BOOLEAN, G_TYPE_STRING,
1876 G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING);
1878 d->map.gpx_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(
model));
1879 g_object_unref(
model);
1880 gtk_widget_set_name(
d->map.gpx_view,
"gpx_list");
1881 gtk_widget_set_tooltip_text(GTK_WIDGET(
d->map.gpx_view),
1882 _(
"list of track segments in the GPX file, for each segment:"
1883 "\n- the start date/time in local time (LT)"
1884 "\n- the number of track points"
1885 "\n- the number of matching images"
1886 " based on images date/time, offset and time zone"
1887 "\n- more detailed time information hovering the row"));
1888 renderer = gtk_cell_renderer_toggle_new();
1889 g_signal_connect(renderer,
"toggled", G_CALLBACK(_track_seg_toggled), self);
1890 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
"", renderer,
"active", DT_GEO_TRACKS_ACTIVE, NULL);
1891 gtk_tree_view_append_column(GTK_TREE_VIEW(
d->map.gpx_view), column);
1892 d->map.sel_tracks = column;
1893 GtkWidget *button = gtk_check_button_new();
1894 gtk_widget_show(button);
1895 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
FALSE);
1896 gtk_tree_view_column_set_widget(column, button);
1897 gtk_tree_view_column_set_alignment(column, 0.5);
1898 g_signal_connect(column,
"clicked", G_CALLBACK(_all_tracks_toggled), self);
1901 gtk_tree_view_append_column(GTK_TREE_VIEW(
d->map.gpx_view), column);
1903 gtk_tree_view_append_column(GTK_TREE_VIEW(
d->map.gpx_view), column);
1905 gtk_tree_view_append_column(GTK_TREE_VIEW(
d->map.gpx_view), column);
1907 g_object_set(G_OBJECT(
d->map.gpx_view),
"has-tooltip",
TRUE, NULL);
1908 g_signal_connect(G_OBJECT(
d->map.gpx_view),
"query-tooltip", G_CALLBACK(
_row_tooltip_setup), self);
1911 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(
d->map.gpx_view),
FALSE);
1914 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(
d->map.gpx_view),
TRUE);
1915 gtk_box_pack_start(GTK_BOX(
d->map.gpx_section), w,
TRUE,
TRUE, 0);
1917 grid = GTK_GRID(gtk_grid_new());
1921 d->map.preview_button = gtk_check_button_new_with_label(_(
"preview images"));
1922 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->map.preview_button),
TRUE);
1923 gtk_widget_set_sensitive(
d->map.preview_button,
FALSE);
1924 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
d->map.preview_button))), PANGO_ELLIPSIZE_END);
1925 gtk_grid_attach(grid,
d->map.preview_button, 0, line, 1, 1);
1926 gtk_widget_set_tooltip_text(
d->map.preview_button, _(
"show on map matching images"));
1927 g_signal_connect(GTK_TOGGLE_BUTTON(
d->map.preview_button),
"toggled", G_CALLBACK(_images_preview_toggled), self);
1930 _(
"select matching images"), 0, 0);
1931 gtk_widget_set_hexpand(
d->map.select_button,
TRUE);
1932 gtk_widget_set_sensitive(
d->map.select_button,
FALSE);
1933 gtk_grid_attach(grid,
d->map.select_button, 1, line, 1, 1);
1936 gtk_widget_set_halign(
d->map.nb_imgs_label, GTK_ALIGN_END);
1937 gtk_widget_set_tooltip_text(GTK_WIDGET(
d->map.nb_imgs_label),
1938 _(
"number of matching images versus selected images"));
1939 gtk_grid_attach(grid,
d->map.nb_imgs_label, 2, line++, 1, 1);
1942 _(
"apply geo-location to matching images"), 0, 0);
1943 gtk_widget_set_hexpand(
d->map.apply_gpx_button,
TRUE);
1944 gtk_widget_set_sensitive(
d->map.apply_gpx_button,
FALSE);
1945 gtk_grid_attach(grid,
d->map.apply_gpx_button, 0, line++, 3, 1);
1947 gtk_box_pack_start(GTK_BOX(
d->map.gpx_section), GTK_WIDGET(grid),
TRUE,
TRUE, 0);
1950 gtk_widget_show_all(self->
widget);
1951 gtk_widget_set_no_show_all(self->
widget,
TRUE);
1958 g_date_time_ref(
d->datetime);
1984 G_CALLBACK(_geotag_changed), self);
1989 gtk_widget_show_all(self->
widget);
1990 gtk_widget_set_no_show_all(self->
widget,
TRUE);
2007 d->timezones = NULL;
2008 g_time_zone_unref(
d->tz_camera);
2010 g_date_time_unref(
d->datetime);
2012 g_date_time_unref(
d->datetime0);
2019 _remove_images_from_map(self);
2030 _remove_tracks_from_map(self);
int32_t dt_act_on_get_first_image()
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
void dt_image_get_datetime(const int32_t imgid, char *datetime)
int dt_conf_get_bool(const char *name)
const char * dt_confgen_get_tooltip(const char *name)
gchar * dt_conf_get_string(const char *name)
void dt_conf_set_string(const char *name, const char *val)
void dt_conf_set_folder_from_file_chooser(const char *name, GtkFileChooser *chooser)
const char * dt_confgen_get_label(const char *name)
gboolean dt_conf_get_folder_to_file_chooser(const char *name, GtkFileChooser *chooser)
int32_t dt_control_get_mouse_over_id()
void dt_control_gpx_apply(const gchar *filename, int32_t filmid, const gchar *tz, GList *imgs)
void dt_control_datetime(const GTimeSpan offset, const char *datetime, GList *imgs)
uint32_t view(const dt_view_t *self)
#define DT_MODULE(MODVER)
static void dt_free_gpointer(gpointer ptr)
static const dt_aligned_pixel_simd_t value
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
sqlite3 * dt_database_get(const dt_database_t *db)
gboolean dt_datetime_img_to_exif(char *exif, const size_t exif_size, const dt_image_t *img)
gboolean dt_datetime_gdatetime_to_exif(char *exif, const size_t exif_size, GDateTime *gdt)
GDateTime * dt_datetime_exif_to_gdatetime(const char *exif, const GTimeZone *tz)
#define DT_DATETIME_LENGTH
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
void dtgtk_cairo_paint_directory(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_lock(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dt_loc_get_datadir(char *datadir, size_t bufsize)
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
static gboolean _timezone_focus_out(GtkWidget *entry, GdkEventKey *event, dt_lib_module_t *self)
static GDateTime * _localtime_text_to_utc_timeval(const char *date_time, GTimeZone *tz_camera, GTimeZone *tz_utc, GTimeSpan offset)
static void _choose_gpx_callback(GtkWidget *widget, dt_lib_module_t *self)
static void free_tz_tuple(gpointer data)
static void _setup_selected_images_list(dt_lib_module_t *self)
static gboolean _timezone_key_pressed(GtkWidget *entry, GdkEventKey *event, dt_lib_module_t *self)
static void _show_milliseconds(dt_lib_geotagging_t *d)
static int _sort_timezones(gconstpointer a, gconstpointer b)
static void _apply_offset_callback(GtkWidget *widget, dt_lib_module_t *self)
static GtkWidget * _gui_init_datetime(dt_lib_datetime_t *dt, const int type, dt_lib_module_t *self)
static GDateTime * _get_image_datetime(dt_lib_module_t *self)
static void _display_datetime(dt_lib_datetime_t *dtw, GDateTime *datetime, const gboolean lock, dt_lib_module_t *self)
static GtkWidget * _set_up_label(const char *name, const int align, GtkWidget *grid, const int col, const int line, const int ellipsize)
void gui_cleanup(dt_lib_module_t *self)
static void _preview_gpx_file(GtkWidget *widget, dt_lib_module_t *self)
static void _selection_changed_callback(gpointer instance, dt_lib_module_t *self)
static void _datetime_entry_changed(GtkWidget *entry, dt_lib_module_t *self)
static gboolean _datetime_scroll_over(GtkWidget *w, GdkEventScroll *event, dt_lib_module_t *self)
static void _display_offset(const GTimeSpan offset_int, const gboolean valid, dt_lib_module_t *self)
static void _apply_datetime_callback(GtkWidget *widget, dt_lib_module_t *self)
static void _mouse_over_image_callback(gpointer instance, dt_lib_module_t *self)
static void _dt_pref_change_callback(gpointer instance, dt_lib_module_t *self)
static gboolean _completion_match_func(GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data)
#define DT_GEOTAG_PARTS_NB
static GDateTime * _read_datetime_entry(dt_lib_module_t *self)
static GList * _lib_geotagging_get_timezones(void)
uint32_t container(dt_lib_module_t *self)
GtkTreeViewColumn * _new_tree_text_column(const char *name, const gboolean expand, const float xalign, const int m_col, const int ellipsize)
static void _refresh_image_datetime(dt_lib_module_t *self)
void gui_init(dt_lib_module_t *self)
static int _count_images_per_track(dt_gpx_track_segment_t *t, dt_gpx_track_segment_t *n, dt_lib_module_t *self)
static void _image_info_changed(gpointer instance, gpointer imgs, dt_lib_module_t *self)
const char ** views(dt_lib_module_t *self)
static gboolean _lib_geotagging_filter_gpx(const GtkFileFilterInfo *filter_info, gpointer data)
static gchar * _utc_timeval_to_localtime_text(GDateTime *utc_dt, GTimeZone *tz_camera, const gboolean full)
static void _toggle_lock_button_callback(GtkToggleButton *button, dt_lib_module_t *self)
static void _new_datetime(GDateTime *datetime, dt_lib_module_t *self)
static void _timezone_save(dt_lib_module_t *self)
static gboolean _datetime_key_pressed(GtkWidget *entry, GdkEventKey *event, dt_lib_module_t *self)
void dt_gpx_destroy(struct dt_gpx_t *gpx)
GList * dt_gpx_get_trkpts(struct dt_gpx_t *gpx, const guint segid)
gboolean dt_gpx_get_location(struct dt_gpx_t *gpx, GDateTime *timestamp, dt_image_geoloc_t *geoloc)
dt_gpx_t * dt_gpx_new(const gchar *filename)
GList * dt_gpx_get_trkseg(struct dt_gpx_t *gpx)
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
GtkWidget * dt_ui_scroll_wrap(GtkWidget *w, gint min_size, char *config_str, dt_ui_resize_mode_t mode)
Wrap a scrollable content widget in a recessed, vertically resizable scrolled window.
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnects accels when a text or search entry gets the focus, and reconnects them when it looses it....
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
static GtkWidget * dt_ui_section_label_new(const gchar *str)
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
static GtkWidget * dt_ui_label_new(const gchar *str)
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
GtkWidget * dt_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
static void _view_changed(gpointer instance, dt_view_t *old_view, dt_view_t *new_view, dt_view_t *self)
void dt_osx_disallow_fullscreen(GtkWidget *widget)
void dt_selection_select_list(struct dt_selection_t *selection, const GList *const l)
void dt_selection_clear(dt_selection_t *selection)
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
@ DT_SIGNAL_GEOTAG_CHANGED
This signal is raised when a geotag is added/deleted/changed
@ DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE
This signal is raised when mouse hovers over image thumbs both on lighttable and in the filmstrip....
@ DT_SIGNAL_IMAGE_INFO_CHANGED
This signal is raised when any of image info has changed
@ DT_SIGNAL_PREFERENCES_CHANGE
This signal is raised after preferences have been changed no parameters no return.
@ DT_SIGNAL_SELECTION_CHANGED
This signal is raised when the selection is changed no param, no returned value.
@ DT_SIGNAL_VIEWMANAGER_VIEW_CHANGED
This signal is raised by viewmanager when a view has changed. 1 : dt_view_t * the old view 2 : dt_vie...
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
struct _GtkWidget GtkWidget
static const char *const day[7]
struct dt_gui_gtk_t * gui
struct dt_selection_t * selection
const struct dt_database_t * db
struct dt_control_signal_t * signals
struct dt_image_cache_t * image_cache
struct dt_view_manager_t * view_manager
GtkWidget * apply_datetime
GtkWidget * timezone_changed
gchar dt[DT_DATETIME_LENGTH]
static gboolean _row_tooltip_setup(GtkWidget *treeview, gint x, gint y, gboolean kb_mode, GtkTooltip *tooltip, dt_lib_module_t *self)
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER