+/*
+ * Generate tooltip text for the layer.
+ * This is relatively complicated as it considers information for
+ * no tracks, a single track or multiple tracks
+ * (which may or may not have timing information)
+ */
+static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
+{
+ gchar tbuf1[32];
+ gchar tbuf2[64];
+ gchar tbuf3[64];
+ gchar tbuf4[10];
+ tbuf1[0] = '\0';
+ tbuf2[0] = '\0';
+ tbuf3[0] = '\0';
+ tbuf4[0] = '\0';
+
+ static gchar tmp_buf[128];
+ tmp_buf[0] = '\0';
+
+ // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
+
+ // Safety check - I think these should always be valid
+ if ( vtl->tracks && vtl->waypoints ) {
+ tooltip_tracks tt = { 0.0, 0, 0 };
+ g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
+
+ GDate* gdate_start = g_date_new ();
+ g_date_set_time_t (gdate_start, tt.start_time);
+
+ GDate* gdate_end = g_date_new ();
+ g_date_set_time_t (gdate_end, tt.end_time);
+
+ if ( g_date_compare (gdate_start, gdate_end) ) {
+ // Dates differ so print range on separate line
+ g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
+ g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
+ g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
+ }
+ else {
+ // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
+ if ( tt.start_time != 0 )
+ g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
+ }
+
+ tbuf2[0] = '\0';
+ if ( tt.length > 0.0 ) {
+ gdouble len_in_units;
+
+ // Setup info dependent on distance units
+ if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
+ g_snprintf (tbuf4, sizeof(tbuf4), "miles");
+ len_in_units = VIK_METERS_TO_MILES(tt.length);
+ }
+ else {
+ g_snprintf (tbuf4, sizeof(tbuf4), "kms");
+ len_in_units = tt.length/1000.0;
+ }
+
+ // Timing information if available
+ tbuf1[0] = '\0';
+ if ( tt.duration > 0 ) {
+ g_snprintf (tbuf1, sizeof(tbuf1),
+ _(" in %d:%02d hrs:mins"),
+ (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
+ }
+ g_snprintf (tbuf2, sizeof(tbuf2),
+ _("\n%sTotal Length %.1f %s%s"),
+ tbuf3, len_in_units, tbuf4, tbuf1);
+ }
+
+ // Put together all the elements to form compact tooltip text
+ g_snprintf (tmp_buf, sizeof(tmp_buf),
+ _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
+ g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
+
+ g_date_free (gdate_start);
+ g_date_free (gdate_end);
+
+ }
+
+ return tmp_buf;
+}
+
+static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
+{
+ switch ( subtype )
+ {
+ case VIK_TRW_LAYER_SUBLAYER_TRACKS:
+ {
+ // Very simple tooltip - may expand detail in the future...
+ static gchar tmp_buf[32];
+ g_snprintf (tmp_buf, sizeof(tmp_buf),
+ _("Tracks: %d"),
+ g_hash_table_size (l->tracks));
+ return tmp_buf;
+ }
+ break;
+ case VIK_TRW_LAYER_SUBLAYER_ROUTES:
+ {
+ // Very simple tooltip - may expand detail in the future...
+ static gchar tmp_buf[32];
+ g_snprintf (tmp_buf, sizeof(tmp_buf),
+ _("Routes: %d"),
+ g_hash_table_size (l->routes));
+ return tmp_buf;
+ }
+ break;
+
+ case VIK_TRW_LAYER_SUBLAYER_ROUTE:
+ // Same tooltip for a route
+ case VIK_TRW_LAYER_SUBLAYER_TRACK:
+ {
+ VikTrack *tr;
+ if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
+ tr = g_hash_table_lookup ( l->tracks, sublayer );
+ else
+ tr = g_hash_table_lookup ( l->routes, sublayer );
+
+ if ( tr ) {
+ // Could be a better way of handling strings - but this works...
+ gchar time_buf1[20];
+ gchar time_buf2[20];
+ time_buf1[0] = '\0';
+ time_buf2[0] = '\0';
+ static gchar tmp_buf[100];
+ // Compact info: Short date eg (11/20/99), duration and length
+ // Hopefully these are the things that are most useful and so promoted into the tooltip
+ if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
+ // %x The preferred date representation for the current locale without the time.
+ strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
+ if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
+ gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
+ if ( dur > 0 )
+ g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
+ }
+ }
+ // Get length and consider the appropriate distance units
+ gdouble tr_len = vik_track_get_length(tr);
+ vik_units_distance_t dist_units = a_vik_get_units_distance ();
+ switch (dist_units) {
+ case VIK_UNITS_DISTANCE_KILOMETRES:
+ g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
+ break;
+ case VIK_UNITS_DISTANCE_MILES:
+ g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
+ break;
+ default:
+ break;
+ }
+ return tmp_buf;
+ }
+ }
+ break;
+ case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
+ {
+ // Very simple tooltip - may expand detail in the future...
+ static gchar tmp_buf[32];
+ g_snprintf (tmp_buf, sizeof(tmp_buf),
+ _("Waypoints: %d"),
+ g_hash_table_size (l->waypoints));
+ return tmp_buf;
+ }
+ break;
+ case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
+ {
+ VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
+ // NB It's OK to return NULL
+ if ( w )
+ return w->comment;
+ }
+ break;
+ default: break;
+ }
+ return NULL;
+}
+
+/*
+ * Function to show basic track point information on the statusbar
+ */
+static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
+{
+ gchar tmp_buf1[64];
+ switch (a_vik_get_units_height ()) {
+ case VIK_UNITS_HEIGHT_FEET:
+ g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
+ break;
+ default:
+ //VIK_UNITS_HEIGHT_METRES:
+ g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
+ }
+
+ gchar tmp_buf2[64];
+ tmp_buf2[0] = '\0';
+ if ( trkpt->has_timestamp ) {
+ // Compact date time format
+ strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
+ }
+
+ // Position part
+ // Position is put later on, as this bit may not be seen if the display is not big enough,
+ // one can easily use the current pointer position to see this if needed
+ gchar *lat = NULL, *lon = NULL;
+ static struct LatLon ll;
+ vik_coord_to_latlon (&(trkpt->coord), &ll);
+ a_coords_latlon_to_string ( &ll, &lat, &lon );
+
+ // Track name
+ // Again is put later on, as this bit may not be seen if the display is not big enough
+ // trackname can be seen from the treeview (when enabled)
+ // Also name could be very long to not leave room for anything else
+ gchar tmp_buf3[64];
+ tmp_buf3[0] = '\0';
+ if ( vtl->current_tp_track ) {
+ g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
+ }
+
+ // Combine parts to make overall message
+ gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
+ vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
+ g_free ( lat );
+ g_free ( lon );
+ g_free ( msg );
+}
+
+/*
+ * Function to show basic waypoint information on the statusbar
+ */
+static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
+{
+ gchar tmp_buf1[64];
+ switch (a_vik_get_units_height ()) {
+ case VIK_UNITS_HEIGHT_FEET:
+ g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
+ break;
+ default:
+ //VIK_UNITS_HEIGHT_METRES:
+ g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
+ }
+
+ // Position part
+ // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
+ // one can easily use the current pointer position to see this if needed
+ gchar *lat = NULL, *lon = NULL;
+ static struct LatLon ll;
+ vik_coord_to_latlon (&(wpt->coord), &ll);
+ a_coords_latlon_to_string ( &ll, &lat, &lon );
+
+ // Combine parts to make overall message
+ gchar *msg;
+ if ( wpt->comment )
+ // Add comment if available
+ msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
+ else
+ msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
+ vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
+ g_free ( lat );
+ g_free ( lon );
+ g_free ( msg );
+}
+
+/**
+ * General layer selection function, find out which bit is selected and take appropriate action
+ */
+static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
+{
+ // Reset
+ l->current_wp = NULL;
+ l->current_wp_id = NULL;
+ trw_layer_cancel_current_tp ( l, FALSE );
+
+ // Clear statusbar
+ vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
+
+ switch ( type )
+ {
+ case VIK_TREEVIEW_TYPE_LAYER:
+ {
+ vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
+ /* Mark for redraw */
+ return TRUE;
+ }
+ break;
+
+ case VIK_TREEVIEW_TYPE_SUBLAYER:
+ {
+ switch ( subtype )
+ {
+ case VIK_TRW_LAYER_SUBLAYER_TRACKS:
+ {
+ vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
+ /* Mark for redraw */
+ return TRUE;
+ }
+ break;
+ case VIK_TRW_LAYER_SUBLAYER_TRACK:
+ {
+ VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
+ vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
+ /* Mark for redraw */
+ return TRUE;
+ }
+ break;
+ case VIK_TRW_LAYER_SUBLAYER_ROUTES:
+ {
+ vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
+ /* Mark for redraw */
+ return TRUE;
+ }
+ break;
+ case VIK_TRW_LAYER_SUBLAYER_ROUTE:
+ {
+ VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
+ vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
+ /* Mark for redraw */
+ return TRUE;
+ }
+ break;
+ case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
+ {
+ vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
+ /* Mark for redraw */
+ return TRUE;
+ }
+ break;
+ case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
+ {
+ VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
+ if ( wpt ) {
+ vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
+ // Show some waypoint info
+ set_statusbar_msg_info_wpt ( l, wpt );
+ /* Mark for redraw */
+ return TRUE;
+ }
+ }
+ break;
+ default:
+ {
+ return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
+ }
+ break;
+ }
+ return FALSE;
+ }
+ break;
+
+ default:
+ return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
+ break;
+ }
+}
+
+GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
+{
+ return l->tracks;
+}
+
+GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
+{
+ return l->routes;
+}
+
+GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
+{
+ return l->waypoints;
+}
+
+/*
+ * ATM use a case sensitive find
+ * Finds the first one
+ */
+static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
+{
+ if ( wp && wp->name )
+ if ( ! strcmp ( wp->name, name ) )
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Get waypoint by name - not guaranteed to be unique
+ * Finds the first one
+ */
+VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
+{
+ return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
+}
+
+/*
+ * ATM use a case sensitive find
+ * Finds the first one
+ */
+static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
+{
+ if ( trk && trk->name )
+ if ( ! strcmp ( trk->name, name ) )
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Get track by name - not guaranteed to be unique
+ * Finds the first one
+ */
+VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
+{
+ return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
+}
+
+/*
+ * Get route by name - not guaranteed to be unique
+ * Finds the first one
+ */
+VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
+{
+ return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
+}
+
+static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
+{
+ static VikCoord fixme;
+ vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
+ if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
+ maxmin[0].lat = VIK_LATLON(&fixme)->lat;