From a77c32c837b9c9bb433e28aab0f4f030e895cdca Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Mon, 21 Oct 2013 22:29:40 +0100 Subject: [PATCH] Enable a search item by date feature from an Aggregate Layer level. Tracks are searched first in preference to waypoints. It auto selects the first item found that matches the selected date. --- help/C/viking.xml | 8 +++++ src/dialog.c | 39 +++++++++++++++++++++ src/dialog.h | 1 + src/vikaggregatelayer.c | 48 ++++++++++++++++++++++++++ src/viktrwlayer.c | 75 +++++++++++++++++++++++++++++++++++++++++ src/viktrwlayer.h | 2 ++ 6 files changed, 173 insertions(+) diff --git a/help/C/viking.xml b/help/C/viking.xml index efdd34b2..0c314fc9 100644 --- a/help/C/viking.xml +++ b/help/C/viking.xml @@ -1679,6 +1679,14 @@ This offers a quick way to set all the visibilities of each item within the cont +
Search by Date + +This opens calendar dialog to select a date to search by within this Aggregate layer. +The first item that is found on that date will be selected. +Tracks are searched first in preference over waypoints. + +
+
GeoRef Layer diff --git a/src/dialog.c b/src/dialog.c index 72ef2338..5b7fa7e8 100644 --- a/src/dialog.c +++ b/src/dialog.c @@ -550,6 +550,45 @@ gchar *a_dialog_new_track ( GtkWindow *parent, gchar *default_name, gboolean is_ return NULL; } +/** + * a_dialog_get_date: + * + * Returns: a date as a string - always in ISO8601 format (YYYY-MM-DD) + * This string can be NULL (especially when the dialog is cancelled) + * Free the string after use + */ +gchar *a_dialog_get_date ( GtkWindow *parent, const gchar *title ) +{ + GtkWidget *dialog = gtk_dialog_new_with_buttons ( title, + parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL); + GtkWidget *cal = gtk_calendar_new (); + + gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), cal, FALSE, FALSE, 0); + + gtk_widget_show ( cal ); + + gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT ); + + gchar *date_str = NULL; + if ( gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT ) + { + guint year; + guint month; + guint day; + gtk_calendar_get_date ( GTK_CALENDAR(cal), &year, &month, &day ); + month = month+1; + date_str = g_strdup_printf ( "%d-%02d-%02d", year, month, day ); + } + gtk_widget_destroy ( dialog ); + return date_str; +} + /* creates a vbox full of labels */ GtkWidget *a_dialog_create_label_vbox ( gchar **texts, int label_count, gint spacing, gint padding ) { diff --git a/src/dialog.h b/src/dialog.h index f4734c08..7e3ea30f 100644 --- a/src/dialog.h +++ b/src/dialog.h @@ -62,6 +62,7 @@ gchar *a_dialog_waypoint ( GtkWindow *parent, gchar *default_name, VikTrwLayer * gchar *a_dialog_new_track ( GtkWindow *parent, gchar *default_name, gboolean is_route ); +gchar *a_dialog_get_date ( GtkWindow *parent, const gchar *title ); gboolean a_dialog_yes_or_no ( GtkWindow *parent, const gchar *message, const gchar *extra ); gboolean a_dialog_custom_zoom ( GtkWindow *parent, gdouble *xmpp, gdouble *ympp ); gboolean a_dialog_time_threshold ( GtkWindow *parent, gchar *title_text, gchar *label_text, guint *thr ); diff --git a/src/vikaggregatelayer.c b/src/vikaggregatelayer.c index ea7ee1e9..0fb3ab8c 100644 --- a/src/vikaggregatelayer.c +++ b/src/vikaggregatelayer.c @@ -494,6 +494,47 @@ static void aggregate_layer_waypoint_list_dialog ( menu_array_values values ) g_free ( title ); } +/** + * Search all TrackWaypoint layers in this aggregate layer for an item on the user specified date + */ +static void aggregate_layer_search_date ( menu_array_values values ) +{ + VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( values[MA_VAL] ); + VikCoord position; + gchar *date_str = a_dialog_get_date ( VIK_GTK_WINDOW_FROM_LAYER(val), _("Search by Date") ); + + if ( !date_str ) + return; + + VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(val)) ); + + GList *gl = NULL; + gl = vik_aggregate_layer_get_all_layers_of_type ( val, gl, VIK_LAYER_TRW, TRUE ); + gboolean found = FALSE; + // Search tracks first + while ( gl && !found ) { + // Make it auto select the item if found + found = vik_trw_layer_find_date ( VIK_TRW_LAYER(gl->data), date_str, &position, vvp, TRUE, TRUE ); + gl = g_list_next ( gl ); + } + g_list_free ( gl ); + if ( !found ) { + // Reset and try on Waypoints + gl = NULL; + gl = vik_aggregate_layer_get_all_layers_of_type ( val, gl, VIK_LAYER_TRW, TRUE ); + while ( gl && !found ) { + // Make it auto select the item if found + found = vik_trw_layer_find_date ( VIK_TRW_LAYER(gl->data), date_str, &position, vvp, FALSE, TRUE ); + gl = g_list_next ( gl ); + } + g_list_free ( gl ); + } + + if ( !found ) + a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(val), _("No items found with the requested date.") ); + g_free ( date_str ); +} + /** * aggregate_layer_track_create_list: * @vl: The layer that should create the track and layers list @@ -632,6 +673,13 @@ static void aggregate_layer_add_menu_items ( VikAggregateLayer *val, GtkMenu *me g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_waypoint_list_dialog), values ); gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_with_mnemonic ( _("Search _by Date...") ); + gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(aggregate_layer_search_date), values ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_set_tooltip_text (item, _("Find the first item with a specified date")); + gtk_widget_show ( item ); } static void disconnect_layer_signal ( VikLayer *vl, VikAggregateLayer *val ) diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c index 091607cc..ddebc9f1 100644 --- a/src/viktrwlayer.c +++ b/src/viktrwlayer.c @@ -775,6 +775,81 @@ GType vik_trw_layer_get_type () return vtl_type; } +typedef struct { + gboolean found; + const gchar *date_str; + const VikTrack *trk; + const VikWaypoint *wpt; + gpointer *trk_id; + gpointer *wpt_id; +} date_finder_type; + +static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df ) +{ + gchar date_buf[20]; + date_buf[0] = '\0'; + // Might be an easier way to compare dates rather than converting the strings all the time... + if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) { + strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp))); + + if ( ! g_strcmp0 ( df->date_str, date_buf ) ) { + df->found = TRUE; + df->trk = trk; + df->trk_id = id; + } + } + return df->found; +} + +static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df ) +{ + gchar date_buf[20]; + date_buf[0] = '\0'; + // Might be an easier way to compare dates rather than converting the strings all the time... + if ( wpt->has_timestamp ) { + strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp))); + + if ( ! g_strcmp0 ( df->date_str, date_buf ) ) { + df->found = TRUE; + df->wpt = wpt; + df->wpt_id = id; + } + } + return df->found; +} + +/** + * Find an item by date + */ +gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select ) +{ + date_finder_type df; + df.found = FALSE; + df.date_str = date_str; + df.trk = NULL; + df.wpt = NULL; + // Only tracks ATM + if ( do_tracks ) + g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df ); + else + g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df ); + + if ( select && df.found ) { + if ( do_tracks && df.trk ) { + struct LatLon maxmin[2] = { {0,0}, {0,0} }; + trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin ); + trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin ); + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE ); + } + else if ( df.wpt ) { + vik_viewport_set_center_coord ( vvp, &(df.wpt->coord) ); + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE ); + } + vik_layer_emit_update ( VIK_LAYER(vtl) ); + } + return df.found; +} + static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer ) { static menu_array_sublayer values; diff --git a/src/viktrwlayer.h b/src/viktrwlayer.h index 2b324e4d..bf35ba58 100644 --- a/src/viktrwlayer.h +++ b/src/viktrwlayer.h @@ -57,6 +57,8 @@ GType vik_trw_layer_get_type (); typedef struct _VikTrwLayer VikTrwLayer; +gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select ); + /* These are meant for use in file loaders (gpspoint.c, gpx.c, etc). * These copy the name, so you should free it if necessary. */ void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp ); -- 2.39.5