]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer_tracklist.c
Allow configuration of the date format displayed on Track and Waypoint lists.
[andy/viking.git] / src / viktrwlayer_tracklist.c
CommitLineData
260d6f45
RN
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2/*
3 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 *
5 * Copyright (C) 2013, Rob Norris <rw_norris@hotmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22#include <math.h>
23#include <string.h>
24#include <stdlib.h>
25#include <stdio.h>
26#include <glib.h>
27#include <glib/gstdio.h>
28#include <glib/gi18n.h>
29
30#include "viking.h"
31#include "viktrwlayer_tracklist.h"
32#include "viktrwlayer_propwin.h"
33
34// Long formatted date+basic time - listing this way ensures the string comparison sort works - so no local type format %x or %c here!
35#define TRACK_LIST_DATE_FORMAT "%Y-%m-%d %H:%M"
36
37/**
38 * track_close_cb:
39 *
40 */
41static void track_close_cb ( GtkWidget *dialog, gint resp, GList *data )
42{
43 g_list_foreach ( data, (GFunc) g_free, NULL );
44 g_list_free ( data );
45
46 gtk_widget_destroy (dialog);
47}
48
49/**
50 * format_1f_cell_data_func:
51 *
52 * General purpose column double formatting
53 *
54 */
55static void format_1f_cell_data_func ( GtkTreeViewColumn *col,
56 GtkCellRenderer *renderer,
57 GtkTreeModel *model,
58 GtkTreeIter *iter,
59 gpointer user_data )
60{
61 gdouble value;
62 gchar buf[20];
63 gint column = GPOINTER_TO_INT (user_data);
64 gtk_tree_model_get ( model, iter, column, &value, -1 );
65 g_snprintf ( buf, sizeof(buf), "%.1f", value );
66 g_object_set ( renderer, "text", buf, NULL );
67}
68
69#define TRK_LIST_COLS 11
70#define TRK_COL_NUM TRK_LIST_COLS-1
71#define TRW_COL_NUM TRK_COL_NUM-1
72
73/*
74 * trw_layer_track_tooltip_cb:
75 *
76 * Show a tooltip when the mouse is over a track list entry.
77 * The tooltip contains the comment or description.
78 */
79static gboolean trw_layer_track_tooltip_cb ( GtkWidget *widget,
80 gint x,
81 gint y,
82 gboolean keyboard_tip,
83 GtkTooltip *tooltip,
84 gpointer data )
85{
86 GtkTreeIter iter;
87 GtkTreePath *path = NULL;
88 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
89 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
90
91 if ( !gtk_tree_view_get_tooltip_context ( tree_view, &x, &y,
92 keyboard_tip,
93 &model, &path, &iter ) )
94 return FALSE;
95
96 VikTrack *trk;
97 gtk_tree_model_get ( model, &iter, TRK_COL_NUM, &trk, -1 );
98 if ( !trk ) return FALSE;
99
100 gboolean tooltip_set = TRUE;
101 if ( trk->comment )
102 gtk_tooltip_set_text ( tooltip, trk->comment );
103 else if ( trk->description )
104 gtk_tooltip_set_text ( tooltip, trk->description );
105 else
106 tooltip_set = FALSE;
107
108 if ( tooltip_set )
109 gtk_tree_view_set_tooltip_row ( tree_view, tooltip, path );
110
111 gtk_tree_path_free ( path );
112
113 return tooltip_set;
114}
115
116/*
117static void trw_layer_track_select_cb ( GtkTreeSelection *selection, gpointer data )
118{
119 GtkTreeIter iter;
120 if ( !gtk_tree_selection_get_selected (selection, NULL, &iter) )
121 return;
122
123 GtkTreeView *tree_view = GTK_TREE_VIEW ( data );
124 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
125
126 VikTrack *trk;
127 gtk_tree_model_get ( model, &iter, TRK_COL_NUM, &trk, -1 );
128 if ( !trk ) return;
129
130 VikTrwLayer *vtl;
131 gtk_tree_model_get ( model, &iter, TRW_COL_NUM, &vtl, -1 );
132 if ( !IS_VIK_TRW_LAYER(vtl) ) return;
133
134 //vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->track_iters, uuid ), TRUE );
135}
136*/
137
138// A slightly better way of defining the menu callback information
139// This should be much easier to extend/rework compared to the current trw_layer menus
140typedef enum {
141 MA_VTL = 0,
142 MA_TRK,
143 MA_TRK_UUID,
144 MA_VVP,
145 MA_TREEVIEW,
146 MA_TRKS_LIST,
147 MA_LAST
148} menu_array_index;
149
150typedef gpointer menu_array_values[MA_LAST];
151
152// Instead of hooking automatically on treeview item selection
153// This is performed on demand via the specific menu request
154static void trw_layer_track_select ( menu_array_values values )
155{
156 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
157 VikTrack *trk = VIK_TRACK(values[MA_TRK]);
158
159 if ( values[MA_TRK_UUID] ) {
160 GtkTreeIter *iter = NULL;
161 if ( trk->is_route )
162 iter = g_hash_table_lookup ( vik_trw_layer_get_routes_iters(vtl), values[MA_TRK_UUID] );
163 else
164 iter = g_hash_table_lookup ( vik_trw_layer_get_tracks_iters(vtl), values[MA_TRK_UUID] );
165
166 if ( iter )
167 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter, TRUE );
168 }
169}
170
171static void trw_layer_track_stats ( menu_array_values values )
172{
173 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
174 VikTrack *trk = VIK_TRACK(values[MA_TRK]);
175 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
176
177 if ( trk && trk->name ) {
178 // Kill off this dialog to allow interaction with properties window
179 // since the properties also allows track manipulations it won't cause conflicts here.
180 GtkWidget *gw = gtk_widget_get_toplevel ( values[MA_TREEVIEW] );
181 track_close_cb ( gw, 0, values[MA_TRKS_LIST] );
182
183 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
184 vtl,
185 trk,
186 NULL, // vlp
187 vvp,
188 TRUE );
189 }
190}
191
192static void trw_layer_track_view ( menu_array_values values )
193{
194 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
195 VikTrack *trk = VIK_TRACK(values[MA_TRK]);
196 VikViewport *vvp = VIK_VIEWPORT(values[MA_VVP]);
197
198 // TODO create common function to convert between LatLon[2] and LatLonBBox or even change LatLonBBox to be 2 LatLons!
199 struct LatLon maxmin[2];
200 maxmin[0].lat = trk->bbox.north;
201 maxmin[1].lat = trk->bbox.south;
202 maxmin[0].lon = trk->bbox.east;
203 maxmin[1].lon = trk->bbox.west;
204
205 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
206
207 trw_layer_track_select (values);
208}
209
23a7ae3f
RN
210typedef struct {
211 gboolean has_layer_names;
212 GString *str;
213} copy_data_t;
214
215static void copy_selection (GtkTreeModel *model,
216 GtkTreePath *path,
217 GtkTreeIter *iter,
218 gpointer data)
219{
220 copy_data_t *cd = (copy_data_t*) data;
221
222 gchar* layername; gtk_tree_model_get ( model, iter, 0, &layername, -1 );
223 gchar* name; gtk_tree_model_get ( model, iter, 1, &name, -1 );
224 gchar* date; gtk_tree_model_get ( model, iter, 2, &date, -1 );
225 gdouble d1; gtk_tree_model_get ( model, iter, 4, &d1, -1 );
226 guint d2; gtk_tree_model_get ( model, iter, 5, &d2, -1 );
227 gdouble d3; gtk_tree_model_get ( model, iter, 6, &d3, -1 );
228 gdouble d4; gtk_tree_model_get ( model, iter, 7, &d4, -1 );
229 gint d5; gtk_tree_model_get ( model, iter, 8, &d5, -1 );
230 gchar sep = '\t'; // Could make this configurable - but simply always make it a tab character for now
231 // NB Even if the columns have been reordered - this copies it out only in the original default order
232 // if col 0 is displayed then also copy the layername
233 if ( cd->has_layer_names )
234 g_string_append_printf ( cd->str, "%s%c%s%c%s%c%.1f%c%d%c%.1f%c%.1f%c%d\n", layername, sep, name, sep, date, sep, d1, sep, d2, sep, d3, sep, d4, sep, d5 );
235 else
236 g_string_append_printf ( cd->str, "%s%c%s%c%.1f%c%d%c%.1f%c%.1f%c%d\n", name, sep, date, sep, d1, sep, d2, sep, d3, sep, d4, sep, d5 );
237 g_free ( layername );
238 g_free ( name );
239 g_free ( date );
240}
241
242static void trw_layer_copy_selected ( GtkWidget *tree_view )
243{
244 GtkTreeSelection *selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW(tree_view) );
245 // NB GTK3 has gtk_tree_view_get_n_columns() but we're GTK2 ATM
246 GList *gl = gtk_tree_view_get_columns ( GTK_TREE_VIEW(tree_view) );
247 guint count = g_list_length ( gl );
248 g_list_free ( gl );
249 copy_data_t cd;
250 cd.has_layer_names = (count > TRK_LIST_COLS-3);
251 // Or use gtk_tree_view_column_get_visible()?
252 cd.str = g_string_new ( NULL );
253 gtk_tree_selection_selected_foreach ( selection, copy_selection, &cd );
254
255 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, cd.str->str, NULL );
256
257 g_string_free ( cd.str, TRUE );
258}
259
260static void add_copy_menu_item ( GtkMenu *menu, GtkWidget *tree_view )
261{
262 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic ( _("_Copy Data") );
263 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU) );
264 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_selected), tree_view );
265 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
266 gtk_widget_show ( item );
267}
268
260d6f45
RN
269static gboolean add_menu_items ( GtkMenu *menu, VikTrwLayer *vtl, VikTrack *trk, gpointer trk_uuid, VikViewport *vvp, GtkWidget *tree_view, gpointer data )
270{
271 static menu_array_values values;
272 GtkWidget *item;
273
274 values[MA_VTL] = vtl;
275 values[MA_TRK] = trk;
276 values[MA_TRK_UUID] = trk_uuid;
277 values[MA_VVP] = vvp;
278 values[MA_TREEVIEW] = tree_view;
279 values[MA_TRKS_LIST] = data;
280
281 /*
282 item = gtk_image_menu_item_new_with_mnemonic ( _("_Select") );
283 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
284 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_select), values );
285 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
286 gtk_widget_show ( item );
287 */
288
289 // ATM view auto selects, so don't bother with separate select menu entry
290 item = gtk_image_menu_item_new_with_mnemonic ( _("_View") );
291 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
292 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_view), values );
293 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
260d6f45 294 gtk_widget_show ( item );
985bcc53
RN
295
296 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
260d6f45
RN
297 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_stats), values );
298 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
299 gtk_widget_show ( item );
300
23a7ae3f
RN
301 add_copy_menu_item ( menu, tree_view );
302
303 return TRUE;
304}
305
306static gboolean trw_layer_track_menu_popup_multi ( GtkWidget *tree_view,
307 GdkEventButton *event,
308 gpointer data )
309{
310 GtkWidget *menu = gtk_menu_new();
311
312 add_copy_menu_item ( GTK_MENU(menu), tree_view );
313
314 gtk_menu_popup ( GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
315
260d6f45
RN
316 return TRUE;
317}
318
319static gboolean trw_layer_track_menu_popup ( GtkWidget *tree_view,
320 GdkEventButton *event,
321 gpointer data )
322{
323 static GtkTreeIter iter;
324
325 // Use selected item to get a single iterator ref
326 // This relies on an row being selected as part of the right click
327 GtkTreeSelection *selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW(tree_view) );
328 if ( gtk_tree_selection_count_selected_rows (selection) != 1 )
23a7ae3f 329 return trw_layer_track_menu_popup_multi ( tree_view, event, data );
260d6f45
RN
330
331 GtkTreePath *path;
332 GtkTreeModel *model = gtk_tree_view_get_model ( GTK_TREE_VIEW(tree_view) );
333
334 // All this just to get the iter
335 if ( gtk_tree_view_get_path_at_pos ( GTK_TREE_VIEW(tree_view),
336 (gint) event->x,
337 (gint) event->y,
338 &path, NULL, NULL, NULL)) {
339 gtk_tree_model_get_iter_from_string ( model, &iter, gtk_tree_path_to_string (path) );
340 gtk_tree_path_free ( path );
341 }
342 else
343 return FALSE;
344
345 VikTrack *trk;
346 gtk_tree_model_get ( model, &iter, TRK_COL_NUM, &trk, -1 );
347 if ( !trk ) return FALSE;
348
349 VikTrwLayer *vtl;
350 gtk_tree_model_get ( model, &iter, TRW_COL_NUM, &vtl, -1 );
351 if ( !IS_VIK_TRW_LAYER(vtl) ) return FALSE;
352
353 trku_udata udataU;
354 udataU.trk = trk;
355 udataU.uuid = NULL;
356
357 gpointer *trkf;
358 if ( trk->is_route )
359 trkf = g_hash_table_find ( vik_trw_layer_get_routes(vtl), (GHRFunc) trw_layer_track_find_uuid, &udataU );
360 else
361 trkf = g_hash_table_find ( vik_trw_layer_get_tracks(vtl), (GHRFunc) trw_layer_track_find_uuid, &udataU );
362
363 if ( trkf && udataU.uuid ) {
364 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
365
366 GtkWidget *menu = gtk_menu_new();
367
368 // Originally started to reuse the trw_layer menu items
369 // however these offer too many ways to edit the track data
370 // so without an easy way to distinguish read only operations,
371 // create a very minimal new set of operations
372 add_menu_items ( GTK_MENU(menu),
373 vtl,
374 trk,
375 udataU.uuid,
376 vvp,
985bcc53 377 tree_view,
260d6f45
RN
378 data );
379
380 gtk_menu_popup ( GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
381 return TRUE;
382 }
383 return FALSE;
384}
385
386static gboolean trw_layer_track_button_pressed ( GtkWidget *tree_view,
387 GdkEventButton *event,
388 gpointer data )
389{
390 // Only on right clicks...
391 if ( ! (event->type == GDK_BUTTON_PRESS && event->button == 3) )
392 return FALSE;
393
394 // ATM Force a selection...
395 GtkTreeSelection *selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW(tree_view) );
396 if ( gtk_tree_selection_count_selected_rows (selection) <= 1 ) {
397 GtkTreePath *path;
398 /* Get tree path for row that was clicked */
399 if ( gtk_tree_view_get_path_at_pos ( GTK_TREE_VIEW(tree_view),
400 (gint) event->x,
401 (gint) event->y,
402 &path, NULL, NULL, NULL)) {
403 gtk_tree_selection_unselect_all ( selection );
404 gtk_tree_selection_select_path ( selection, path );
405 gtk_tree_path_free ( path );
406 }
407 }
408 return trw_layer_track_menu_popup ( tree_view, event, data );
409}
410
411/*
412 * Foreach entry we copy the various individual track properties into the tree store
413 * formatting & converting the internal values into something for display
414 */
415static void trw_layer_track_list_add ( vik_trw_track_list_t *vtdl,
416 GtkTreeStore *store,
417 vik_units_distance_t dist_units,
418 vik_units_speed_t speed_units,
ffdfa365
RN
419 vik_units_height_t height_units,
420 const gchar* date_format )
260d6f45
RN
421{
422 GtkTreeIter t_iter;
423 VikTrack *trk = vtdl->trk;
424 VikTrwLayer *vtl = vtdl->vtl;
425
426 gdouble trk_dist = vik_track_get_length ( trk );
427 // Store unit converted value
428 switch ( dist_units ) {
429 case VIK_UNITS_DISTANCE_MILES:
430 trk_dist = VIK_METERS_TO_MILES(trk_dist);
431 break;
432 default:
433 trk_dist = trk_dist/1000.0;
434 break;
435 }
436
437 // Get start date
438 gchar time_buf[32];
439 time_buf[0] = '\0';
440 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
441
442#if GLIB_CHECK_VERSION(2,26,0)
443 GDateTime* gdt = g_date_time_new_from_unix_utc ( VIK_TRACKPOINT(trk->trackpoints->data)->timestamp );
ffdfa365 444 gchar *time = g_date_time_format ( gdt, date_format );
97dd3dee 445 g_strlcpy ( time_buf, time, sizeof(time_buf) );
260d6f45
RN
446 g_free ( time );
447 g_date_time_unref ( gdt);
448#else
449 GDate* gdate_start = g_date_new ();
450 g_date_set_time_t ( gdate_start, VIK_TRACKPOINT(trk->trackpoints->data)->timestamp );
ffdfa365 451 g_date_strftime ( time_buf, sizeof(time_buf), date_format, gdate_start );
260d6f45
RN
452 g_date_free ( gdate_start );
453#endif
454 }
455
456 // NB: doesn't include aggegrate visibility
457 gboolean visible = VIK_LAYER(vtl)->visible && trk->visible;
458 visible = visible && (trk->is_route ? vik_trw_layer_get_routes_visibility(vtl) : vik_trw_layer_get_tracks_visibility(vtl));
459
39b57e75 460 guint trk_len_time = 0; // In minutes
260d6f45
RN
461 if ( trk->trackpoints ) {
462 time_t t1, t2;
463 t1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data)->timestamp;
464 t2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data)->timestamp;
39b57e75 465 trk_len_time = (int)round(labs(t2-t1)/60.0);
260d6f45
RN
466 }
467
468 gdouble av_speed = 0.0;
469 gdouble max_speed = 0.0;
470 gdouble max_alt = 0.0;
471
472 av_speed = vik_track_get_average_speed ( trk );
473 switch (speed_units) {
474 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: av_speed = VIK_MPS_TO_KPH(av_speed); break;
475 case VIK_UNITS_SPEED_MILES_PER_HOUR: av_speed = VIK_MPS_TO_MPH(av_speed); break;
476 case VIK_UNITS_SPEED_KNOTS: av_speed = VIK_MPS_TO_KNOTS(av_speed); break;
477 default: // VIK_UNITS_SPEED_METRES_PER_SECOND therefore no change
478 break;
479 }
480
481 max_speed = vik_track_get_max_speed ( trk );
482 switch (speed_units) {
483 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: max_speed = VIK_MPS_TO_KPH(max_speed); break;
484 case VIK_UNITS_SPEED_MILES_PER_HOUR: max_speed = VIK_MPS_TO_MPH(max_speed); break;
485 case VIK_UNITS_SPEED_KNOTS: max_speed = VIK_MPS_TO_KNOTS(max_speed); break;
486 default: // VIK_UNITS_SPEED_METRES_PER_SECOND therefore no change
487 break;
488 }
489
490 // TODO - make this a function to get min / max values?
491 gdouble *altitudes = NULL;
492 altitudes = vik_track_make_elevation_map ( trk, 500 );
493 if ( altitudes ) {
494 max_alt = -1000;
495 guint i;
496 for ( i=0; i < 500; i++ ) {
497 if ( altitudes[i] != VIK_DEFAULT_ALTITUDE ) {
498 if ( altitudes[i] > max_alt )
499 max_alt = altitudes[i];
500 }
501 }
502 }
503 g_free ( altitudes );
504
505 switch (height_units) {
506 case VIK_UNITS_HEIGHT_FEET: max_alt = VIK_METERS_TO_FEET(max_alt); break;
507 default:
508 // VIK_UNITS_HEIGHT_METRES: no need to convert
509 break;
510 }
511
512 gtk_tree_store_append ( store, &t_iter, NULL );
513 gtk_tree_store_set ( store, &t_iter,
514 0, VIK_LAYER(vtl)->name,
515 1, trk->name,
516 2, time_buf,
517 3, visible,
518 4, trk_dist,
519 5, trk_len_time,
520 6, av_speed,
521 7, max_speed,
4a287810 522 8, (gint)round(max_alt),
260d6f45
RN
523 TRW_COL_NUM, vtl,
524 TRK_COL_NUM, trk,
525 -1 );
526}
527
528static GtkTreeViewColumn *my_new_column_text ( const gchar *title, GtkCellRenderer *renderer, GtkWidget *view, gint column_runner )
529{
530 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes ( title, renderer, "text", column_runner, NULL );
531 gtk_tree_view_column_set_sort_column_id ( column, column_runner );
532 gtk_tree_view_append_column ( GTK_TREE_VIEW(view), column );
e7b235ff
RN
533 gtk_tree_view_column_set_reorderable ( column, TRUE );
534 gtk_tree_view_column_set_resizable ( column, TRUE );
260d6f45
RN
535 return column;
536}
537
538/**
539 * vik_trw_layer_track_list_internal:
540 * @dialog: The dialog to create the widgets in
541 * @tracks_and_layers: The list of tracks (and it's layer) to be shown
542 * @show_layer_names: Show the layer names that each track belongs to
543 *
544 * Create a table of tracks with corresponding track information
545 * This table does not support being actively updated
546 */
547static void vik_trw_layer_track_list_internal ( GtkWidget *dialog,
548 GList *tracks_and_layers,
549 gboolean show_layer_names )
550{
551 if ( !tracks_and_layers )
552 return;
553
554 // It's simple storing the gdouble values in the tree store as the sort works automatically
555 // Then apply specific cell data formatting (rather default double is to 6 decimal places!)
556 GtkTreeStore *store = gtk_tree_store_new ( TRK_LIST_COLS,
557 G_TYPE_STRING, // 0: Layer Name
558 G_TYPE_STRING, // 1: Track Name
559 G_TYPE_STRING, // 2: Date
560 G_TYPE_BOOLEAN, // 3: Visible
561 G_TYPE_DOUBLE, // 4: Distance
562 G_TYPE_UINT, // 5: Length in time
563 G_TYPE_DOUBLE, // 6: Av. Speed
564 G_TYPE_DOUBLE, // 7: Max Speed
4a287810 565 G_TYPE_INT, // 8: Max Height
260d6f45
RN
566 G_TYPE_POINTER, // 9: TrackWaypoint Layer pointer
567 G_TYPE_POINTER ); // 10: Track pointer
568
569 //gtk_tree_selection_set_select_function ( gtk_tree_view_get_selection (GTK_TREE_VIEW(vt)), vik_treeview_selection_filter, vt, NULL );
570
571 vik_units_distance_t dist_units = a_vik_get_units_distance ();
572 vik_units_speed_t speed_units = a_vik_get_units_speed ();
573 vik_units_height_t height_units = a_vik_get_units_height ();
574
575 //GList *gl = get_tracks_and_layers_cb ( vl, user_data );
576 //g_list_foreach ( tracks_and_layers, (GFunc) trw_layer_track_list_add, store );
ffdfa365
RN
577 gchar *date_format = NULL;
578 if ( !a_settings_get_string ( VIK_SETTINGS_LIST_DATE_FORMAT, &date_format ) )
579 date_format = g_strdup ( TRACK_LIST_DATE_FORMAT );
580
260d6f45
RN
581 GList *gl = tracks_and_layers;
582 while ( gl ) {
ffdfa365 583 trw_layer_track_list_add ( (vik_trw_track_list_t*)gl->data, store, dist_units, speed_units, height_units, date_format );
260d6f45
RN
584 gl = g_list_next ( gl );
585 }
ffdfa365 586 g_free ( date_format );
260d6f45
RN
587
588 GtkWidget *view = gtk_tree_view_new();
589 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
e7b235ff
RN
590 g_object_set (G_OBJECT (renderer),
591 "xalign", 0.0,
592 "ellipsize", PANGO_ELLIPSIZE_END,
593 NULL);
594
260d6f45
RN
595 GtkTreeViewColumn *column;
596 GtkTreeViewColumn *sort_by_column;
597
598 gint column_runner = 0;
599 if ( show_layer_names ) {
600 // Insert column for the layer name when viewing multi layers
601 column = my_new_column_text ( _("Layer"), renderer, view, column_runner++ );
e7b235ff 602 gtk_tree_view_column_set_expand ( column, TRUE );
260d6f45
RN
603 // remember the layer column so we can sort by it later
604 sort_by_column = column;
605 }
606 else
607 column_runner++;
608
609 column = my_new_column_text ( _("Name"), renderer, view, column_runner++ );
e7b235ff 610 gtk_tree_view_column_set_expand ( column, TRUE );
260d6f45
RN
611 if ( !show_layer_names )
612 // remember the name column so we can sort by it later
613 sort_by_column = column;
614
615 column = my_new_column_text ( _("Date"), renderer, view, column_runner++ );
e7b235ff 616 gtk_tree_view_column_set_expand ( column, TRUE );
260d6f45
RN
617
618 GtkCellRenderer *renderer_toggle = gtk_cell_renderer_toggle_new ();
619 column = gtk_tree_view_column_new_with_attributes ( _("Visible"), renderer_toggle, "active", column_runner, NULL );
e7b235ff 620 gtk_tree_view_column_set_reorderable ( column, TRUE );
260d6f45
RN
621 gtk_tree_view_column_set_sort_column_id ( column, column_runner );
622 gtk_tree_view_append_column ( GTK_TREE_VIEW(view), column );
623 column_runner++;
624
625 switch ( dist_units ) {
626 case VIK_UNITS_DISTANCE_MILES:
627 column = my_new_column_text ( _("Distance\n(miles)"), renderer, view, column_runner++ );
628 break;
629 default:
630 column = my_new_column_text ( _("Distance\n(km)"), renderer, view, column_runner++ );
631 break;
632 }
633 // Apply own formatting of the data
634 gtk_tree_view_column_set_cell_data_func ( column, renderer, format_1f_cell_data_func, GINT_TO_POINTER(column_runner-1), NULL);
635
5263679f 636 (void)my_new_column_text ( _("Length\n(minutes)"), renderer, view, column_runner++ );
260d6f45
RN
637
638 gchar *spd_units = NULL;
639 switch (speed_units) {
640 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: spd_units = g_strdup (_("km/h")); break;
641 case VIK_UNITS_SPEED_MILES_PER_HOUR: spd_units = g_strdup (_("mph")); break;
642 case VIK_UNITS_SPEED_KNOTS: spd_units = g_strdup (_("knots")); break;
643 // VIK_UNITS_SPEED_METRES_PER_SECOND:
644 default: spd_units = g_strdup (_("m/s")); break;
645 }
646
647 gchar *title = g_strdup_printf ( _("Av. Speed\n(%s)"), spd_units );
648 column = my_new_column_text ( title, renderer, view, column_runner++ );
649 g_free ( title );
650 gtk_tree_view_column_set_cell_data_func ( column, renderer, format_1f_cell_data_func, GINT_TO_POINTER(column_runner-1), NULL); // Apply own formatting of the data
651
652 title = g_strdup_printf ( _("Max Speed\n(%s)"), spd_units );
653 column = my_new_column_text ( title, renderer, view, column_runner++ );
654 gtk_tree_view_column_set_cell_data_func ( column, renderer, format_1f_cell_data_func, GINT_TO_POINTER(column_runner-1), NULL); // Apply own formatting of the data
655
656 g_free ( title );
657 g_free ( spd_units );
658
659 if ( height_units == VIK_UNITS_HEIGHT_FEET )
5263679f 660 (void)my_new_column_text ( _("Max Height\n(Feet)"), renderer, view, column_runner++ );
260d6f45 661 else
5263679f 662 (void)my_new_column_text ( _("Max Height\n(Metres)"), renderer, view, column_runner++ );
260d6f45
RN
663
664 gtk_tree_view_set_model ( GTK_TREE_VIEW(view), GTK_TREE_MODEL(store) );
23a7ae3f 665 gtk_tree_selection_set_mode ( gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), GTK_SELECTION_MULTIPLE );
260d6f45
RN
666 gtk_tree_view_set_rules_hint ( GTK_TREE_VIEW(view), TRUE );
667
668 g_object_unref(store);
669
670 GtkWidget *scrolledwindow = gtk_scrolled_window_new ( NULL, NULL );
671 gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
672 gtk_container_add ( GTK_CONTAINER(scrolledwindow), view );
673
674 g_object_set ( view, "has-tooltip", TRUE, NULL);
675
676 g_signal_connect ( view, "query-tooltip", G_CALLBACK (trw_layer_track_tooltip_cb), NULL );
677 //g_signal_connect ( gtk_tree_view_get_selection (GTK_TREE_VIEW(view)), "changed", G_CALLBACK(trw_layer_track_select_cb), view );
678
679 g_signal_connect ( view, "popup-menu", G_CALLBACK(trw_layer_track_menu_popup), tracks_and_layers );
680 g_signal_connect ( view, "button-press-event", G_CALLBACK(trw_layer_track_button_pressed), tracks_and_layers );
681
682 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), scrolledwindow, TRUE, TRUE, 0);
683
684 // Set ordering of the initial view by one of the name columns
685 gtk_tree_view_column_clicked ( sort_by_column );
686
687 // Ensure a reasonable number of items are shown
e7b235ff
RN
688 // TODO: may be save window size, column order, sorted by between invocations.
689 // Gtk too stupid to work out best size so need to tell it.
690 gtk_window_set_default_size ( GTK_WINDOW(dialog), show_layer_names ? 900 : 700, 400 );
260d6f45
RN
691}
692
693
694/**
695 * vik_trw_layer_track_list_show_dialog:
696 * @title: The title for the dialog
697 * @vl: The #VikLayer passed on into get_tracks_and_layers_cb()
698 * @user_data: Data passed on into get_tracks_and_layers_cb()
699 * @get_tracks_and_layers_cb: The function to call to construct items to be analysed
700 * @show_layer_names: Normally only set when called from an aggregate level
701 *
702 * Common method for showing a list of tracks with extended information
703 *
704 */
705void vik_trw_layer_track_list_show_dialog ( gchar *title,
706 VikLayer *vl,
707 gpointer user_data,
708 VikTrwlayerGetTracksAndLayersFunc get_tracks_and_layers_cb,
709 gboolean show_layer_names )
710{
711 GtkWidget *dialog = gtk_dialog_new_with_buttons ( title,
712 VIK_GTK_WINDOW_FROM_LAYER(vl),
713 GTK_DIALOG_DESTROY_WITH_PARENT,
714 GTK_STOCK_CLOSE,
715 GTK_RESPONSE_CLOSE,
716 NULL );
717
718 GList *gl = get_tracks_and_layers_cb ( vl, user_data );
719
720 vik_trw_layer_track_list_internal ( dialog, gl, show_layer_names );
721
722 // Use response to close the dialog with tidy up
723 g_signal_connect ( G_OBJECT(dialog), "response", G_CALLBACK(track_close_cb), gl );
724
725 gtk_widget_show_all ( dialog );
e7b235ff
RN
726 // Yes - set the size *AGAIN* - this time widgets are expanded nicely
727 gtk_window_resize ( GTK_WINDOW(dialog), show_layer_names ? 1000 : 800, 400 );
260d6f45
RN
728
729 // ATM lock out on dialog run - to prevent list contents being manipulated in other parts of the GUI whilst shown here.
730 gtk_dialog_run (GTK_DIALOG (dialog));
731 // Unfortunately seems subsequently opening the Track Properties we can't interact with it until this dialog is closed
732 // Thus this dialog is then forcibly closed when opening the properties.
733
734 // Occassionally the 'View' doesn't update the viewport properly
735 // viewport center + zoom is changed but the viewport isn't updated
736 // not sure why yet..
737}