]>
Commit | Line | Data |
---|---|---|
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 | */ | |
41 | static 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 | */ | |
55 | static 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 | */ | |
79 | static 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 | /* | |
117 | static 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 | |
140 | typedef 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 | ||
150 | typedef 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 | |
154 | static 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 | ||
171 | static 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 | ||
192 | static 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 | ||
210 | static gboolean add_menu_items ( GtkMenu *menu, VikTrwLayer *vtl, VikTrack *trk, gpointer trk_uuid, VikViewport *vvp, GtkWidget *tree_view, gpointer data ) | |
211 | { | |
212 | static menu_array_values values; | |
213 | GtkWidget *item; | |
214 | ||
215 | values[MA_VTL] = vtl; | |
216 | values[MA_TRK] = trk; | |
217 | values[MA_TRK_UUID] = trk_uuid; | |
218 | values[MA_VVP] = vvp; | |
219 | values[MA_TREEVIEW] = tree_view; | |
220 | values[MA_TRKS_LIST] = data; | |
221 | ||
222 | /* | |
223 | item = gtk_image_menu_item_new_with_mnemonic ( _("_Select") ); | |
224 | gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) ); | |
225 | g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_select), values ); | |
226 | gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); | |
227 | gtk_widget_show ( item ); | |
228 | */ | |
229 | ||
230 | // ATM view auto selects, so don't bother with separate select menu entry | |
231 | item = gtk_image_menu_item_new_with_mnemonic ( _("_View") ); | |
232 | gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) ); | |
233 | g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_view), values ); | |
234 | gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); | |
235 | ||
236 | gtk_widget_show ( item ); | |
237 | item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") ); | |
238 | g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_stats), values ); | |
239 | gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); | |
240 | gtk_widget_show ( item ); | |
241 | ||
242 | return TRUE; | |
243 | } | |
244 | ||
245 | static gboolean trw_layer_track_menu_popup ( GtkWidget *tree_view, | |
246 | GdkEventButton *event, | |
247 | gpointer data ) | |
248 | { | |
249 | static GtkTreeIter iter; | |
250 | ||
251 | // Use selected item to get a single iterator ref | |
252 | // This relies on an row being selected as part of the right click | |
253 | GtkTreeSelection *selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW(tree_view) ); | |
254 | if ( gtk_tree_selection_count_selected_rows (selection) != 1 ) | |
255 | return FALSE; | |
256 | ||
257 | GtkTreePath *path; | |
258 | GtkTreeModel *model = gtk_tree_view_get_model ( GTK_TREE_VIEW(tree_view) ); | |
259 | ||
260 | // All this just to get the iter | |
261 | if ( gtk_tree_view_get_path_at_pos ( GTK_TREE_VIEW(tree_view), | |
262 | (gint) event->x, | |
263 | (gint) event->y, | |
264 | &path, NULL, NULL, NULL)) { | |
265 | gtk_tree_model_get_iter_from_string ( model, &iter, gtk_tree_path_to_string (path) ); | |
266 | gtk_tree_path_free ( path ); | |
267 | } | |
268 | else | |
269 | return FALSE; | |
270 | ||
271 | VikTrack *trk; | |
272 | gtk_tree_model_get ( model, &iter, TRK_COL_NUM, &trk, -1 ); | |
273 | if ( !trk ) return FALSE; | |
274 | ||
275 | VikTrwLayer *vtl; | |
276 | gtk_tree_model_get ( model, &iter, TRW_COL_NUM, &vtl, -1 ); | |
277 | if ( !IS_VIK_TRW_LAYER(vtl) ) return FALSE; | |
278 | ||
279 | trku_udata udataU; | |
280 | udataU.trk = trk; | |
281 | udataU.uuid = NULL; | |
282 | ||
283 | gpointer *trkf; | |
284 | if ( trk->is_route ) | |
285 | trkf = g_hash_table_find ( vik_trw_layer_get_routes(vtl), (GHRFunc) trw_layer_track_find_uuid, &udataU ); | |
286 | else | |
287 | trkf = g_hash_table_find ( vik_trw_layer_get_tracks(vtl), (GHRFunc) trw_layer_track_find_uuid, &udataU ); | |
288 | ||
289 | if ( trkf && udataU.uuid ) { | |
290 | VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl))); | |
291 | ||
292 | GtkWidget *menu = gtk_menu_new(); | |
293 | ||
294 | // Originally started to reuse the trw_layer menu items | |
295 | // however these offer too many ways to edit the track data | |
296 | // so without an easy way to distinguish read only operations, | |
297 | // create a very minimal new set of operations | |
298 | add_menu_items ( GTK_MENU(menu), | |
299 | vtl, | |
300 | trk, | |
301 | udataU.uuid, | |
302 | vvp, | |
303 | tree_view, | |
304 | data ); | |
305 | ||
306 | gtk_menu_popup ( GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() ); | |
307 | return TRUE; | |
308 | } | |
309 | return FALSE; | |
310 | } | |
311 | ||
312 | static gboolean trw_layer_track_button_pressed ( GtkWidget *tree_view, | |
313 | GdkEventButton *event, | |
314 | gpointer data ) | |
315 | { | |
316 | // Only on right clicks... | |
317 | if ( ! (event->type == GDK_BUTTON_PRESS && event->button == 3) ) | |
318 | return FALSE; | |
319 | ||
320 | // ATM Force a selection... | |
321 | GtkTreeSelection *selection = gtk_tree_view_get_selection ( GTK_TREE_VIEW(tree_view) ); | |
322 | if ( gtk_tree_selection_count_selected_rows (selection) <= 1 ) { | |
323 | GtkTreePath *path; | |
324 | /* Get tree path for row that was clicked */ | |
325 | if ( gtk_tree_view_get_path_at_pos ( GTK_TREE_VIEW(tree_view), | |
326 | (gint) event->x, | |
327 | (gint) event->y, | |
328 | &path, NULL, NULL, NULL)) { | |
329 | gtk_tree_selection_unselect_all ( selection ); | |
330 | gtk_tree_selection_select_path ( selection, path ); | |
331 | gtk_tree_path_free ( path ); | |
332 | } | |
333 | } | |
334 | return trw_layer_track_menu_popup ( tree_view, event, data ); | |
335 | } | |
336 | ||
337 | /* | |
338 | * Foreach entry we copy the various individual track properties into the tree store | |
339 | * formatting & converting the internal values into something for display | |
340 | */ | |
341 | static void trw_layer_track_list_add ( vik_trw_track_list_t *vtdl, | |
342 | GtkTreeStore *store, | |
343 | vik_units_distance_t dist_units, | |
344 | vik_units_speed_t speed_units, | |
345 | vik_units_height_t height_units ) | |
346 | { | |
347 | GtkTreeIter t_iter; | |
348 | VikTrack *trk = vtdl->trk; | |
349 | VikTrwLayer *vtl = vtdl->vtl; | |
350 | ||
351 | gdouble trk_dist = vik_track_get_length ( trk ); | |
352 | // Store unit converted value | |
353 | switch ( dist_units ) { | |
354 | case VIK_UNITS_DISTANCE_MILES: | |
355 | trk_dist = VIK_METERS_TO_MILES(trk_dist); | |
356 | break; | |
357 | default: | |
358 | trk_dist = trk_dist/1000.0; | |
359 | break; | |
360 | } | |
361 | ||
362 | // Get start date | |
363 | gchar time_buf[32]; | |
364 | time_buf[0] = '\0'; | |
365 | if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) { | |
366 | ||
367 | #if GLIB_CHECK_VERSION(2,26,0) | |
368 | GDateTime* gdt = g_date_time_new_from_unix_utc ( VIK_TRACKPOINT(trk->trackpoints->data)->timestamp ); | |
369 | gchar *time = g_date_time_format ( gdt, TRACK_LIST_DATE_FORMAT ); | |
370 | strncpy ( time_buf, time, sizeof(time_buf) ); | |
371 | g_free ( time ); | |
372 | g_date_time_unref ( gdt); | |
373 | #else | |
374 | GDate* gdate_start = g_date_new (); | |
375 | g_date_set_time_t ( gdate_start, VIK_TRACKPOINT(trk->trackpoints->data)->timestamp ); | |
376 | g_date_strftime ( time_buf, sizeof(time_buf), TRACK_LIST_DATE_FORMAT, gdate_start ); | |
377 | g_date_free ( gdate_start ); | |
378 | #endif | |
379 | } | |
380 | ||
381 | // NB: doesn't include aggegrate visibility | |
382 | gboolean visible = VIK_LAYER(vtl)->visible && trk->visible; | |
383 | visible = visible && (trk->is_route ? vik_trw_layer_get_routes_visibility(vtl) : vik_trw_layer_get_tracks_visibility(vtl)); | |
384 | ||
385 | guint trk_len_time = 0; | |
386 | if ( trk->trackpoints ) { | |
387 | time_t t1, t2; | |
388 | t1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data)->timestamp; | |
389 | t2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data)->timestamp; | |
390 | trk_len_time = (int)round (abs(t2-t1)/60); | |
391 | } | |
392 | ||
393 | gdouble av_speed = 0.0; | |
394 | gdouble max_speed = 0.0; | |
395 | gdouble max_alt = 0.0; | |
396 | ||
397 | av_speed = vik_track_get_average_speed ( trk ); | |
398 | switch (speed_units) { | |
399 | case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: av_speed = VIK_MPS_TO_KPH(av_speed); break; | |
400 | case VIK_UNITS_SPEED_MILES_PER_HOUR: av_speed = VIK_MPS_TO_MPH(av_speed); break; | |
401 | case VIK_UNITS_SPEED_KNOTS: av_speed = VIK_MPS_TO_KNOTS(av_speed); break; | |
402 | default: // VIK_UNITS_SPEED_METRES_PER_SECOND therefore no change | |
403 | break; | |
404 | } | |
405 | ||
406 | max_speed = vik_track_get_max_speed ( trk ); | |
407 | switch (speed_units) { | |
408 | case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: max_speed = VIK_MPS_TO_KPH(max_speed); break; | |
409 | case VIK_UNITS_SPEED_MILES_PER_HOUR: max_speed = VIK_MPS_TO_MPH(max_speed); break; | |
410 | case VIK_UNITS_SPEED_KNOTS: max_speed = VIK_MPS_TO_KNOTS(max_speed); break; | |
411 | default: // VIK_UNITS_SPEED_METRES_PER_SECOND therefore no change | |
412 | break; | |
413 | } | |
414 | ||
415 | // TODO - make this a function to get min / max values? | |
416 | gdouble *altitudes = NULL; | |
417 | altitudes = vik_track_make_elevation_map ( trk, 500 ); | |
418 | if ( altitudes ) { | |
419 | max_alt = -1000; | |
420 | guint i; | |
421 | for ( i=0; i < 500; i++ ) { | |
422 | if ( altitudes[i] != VIK_DEFAULT_ALTITUDE ) { | |
423 | if ( altitudes[i] > max_alt ) | |
424 | max_alt = altitudes[i]; | |
425 | } | |
426 | } | |
427 | } | |
428 | g_free ( altitudes ); | |
429 | ||
430 | switch (height_units) { | |
431 | case VIK_UNITS_HEIGHT_FEET: max_alt = VIK_METERS_TO_FEET(max_alt); break; | |
432 | default: | |
433 | // VIK_UNITS_HEIGHT_METRES: no need to convert | |
434 | break; | |
435 | } | |
436 | ||
437 | gtk_tree_store_append ( store, &t_iter, NULL ); | |
438 | gtk_tree_store_set ( store, &t_iter, | |
439 | 0, VIK_LAYER(vtl)->name, | |
440 | 1, trk->name, | |
441 | 2, time_buf, | |
442 | 3, visible, | |
443 | 4, trk_dist, | |
444 | 5, trk_len_time, | |
445 | 6, av_speed, | |
446 | 7, max_speed, | |
447 | 8, (guint)round(max_alt), | |
448 | TRW_COL_NUM, vtl, | |
449 | TRK_COL_NUM, trk, | |
450 | -1 ); | |
451 | } | |
452 | ||
453 | static GtkTreeViewColumn *my_new_column_text ( const gchar *title, GtkCellRenderer *renderer, GtkWidget *view, gint column_runner ) | |
454 | { | |
455 | GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes ( title, renderer, "text", column_runner, NULL ); | |
456 | gtk_tree_view_column_set_sort_column_id ( column, column_runner ); | |
457 | gtk_tree_view_append_column ( GTK_TREE_VIEW(view), column ); | |
458 | return column; | |
459 | } | |
460 | ||
461 | /** | |
462 | * vik_trw_layer_track_list_internal: | |
463 | * @dialog: The dialog to create the widgets in | |
464 | * @tracks_and_layers: The list of tracks (and it's layer) to be shown | |
465 | * @show_layer_names: Show the layer names that each track belongs to | |
466 | * | |
467 | * Create a table of tracks with corresponding track information | |
468 | * This table does not support being actively updated | |
469 | */ | |
470 | static void vik_trw_layer_track_list_internal ( GtkWidget *dialog, | |
471 | GList *tracks_and_layers, | |
472 | gboolean show_layer_names ) | |
473 | { | |
474 | if ( !tracks_and_layers ) | |
475 | return; | |
476 | ||
477 | // It's simple storing the gdouble values in the tree store as the sort works automatically | |
478 | // Then apply specific cell data formatting (rather default double is to 6 decimal places!) | |
479 | GtkTreeStore *store = gtk_tree_store_new ( TRK_LIST_COLS, | |
480 | G_TYPE_STRING, // 0: Layer Name | |
481 | G_TYPE_STRING, // 1: Track Name | |
482 | G_TYPE_STRING, // 2: Date | |
483 | G_TYPE_BOOLEAN, // 3: Visible | |
484 | G_TYPE_DOUBLE, // 4: Distance | |
485 | G_TYPE_UINT, // 5: Length in time | |
486 | G_TYPE_DOUBLE, // 6: Av. Speed | |
487 | G_TYPE_DOUBLE, // 7: Max Speed | |
488 | G_TYPE_UINT, // 8: Max Height | |
489 | G_TYPE_POINTER, // 9: TrackWaypoint Layer pointer | |
490 | G_TYPE_POINTER ); // 10: Track pointer | |
491 | ||
492 | //gtk_tree_selection_set_select_function ( gtk_tree_view_get_selection (GTK_TREE_VIEW(vt)), vik_treeview_selection_filter, vt, NULL ); | |
493 | ||
494 | vik_units_distance_t dist_units = a_vik_get_units_distance (); | |
495 | vik_units_speed_t speed_units = a_vik_get_units_speed (); | |
496 | vik_units_height_t height_units = a_vik_get_units_height (); | |
497 | ||
498 | //GList *gl = get_tracks_and_layers_cb ( vl, user_data ); | |
499 | //g_list_foreach ( tracks_and_layers, (GFunc) trw_layer_track_list_add, store ); | |
500 | GList *gl = tracks_and_layers; | |
501 | while ( gl ) { | |
502 | trw_layer_track_list_add ( (vik_trw_track_list_t*)gl->data, store, dist_units, speed_units, height_units ); | |
503 | gl = g_list_next ( gl ); | |
504 | } | |
505 | ||
506 | GtkWidget *view = gtk_tree_view_new(); | |
507 | GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); | |
508 | GtkTreeViewColumn *column; | |
509 | GtkTreeViewColumn *sort_by_column; | |
510 | ||
511 | gint column_runner = 0; | |
512 | if ( show_layer_names ) { | |
513 | // Insert column for the layer name when viewing multi layers | |
514 | column = my_new_column_text ( _("Layer"), renderer, view, column_runner++ ); | |
515 | // remember the layer column so we can sort by it later | |
516 | sort_by_column = column; | |
517 | } | |
518 | else | |
519 | column_runner++; | |
520 | ||
521 | column = my_new_column_text ( _("Name"), renderer, view, column_runner++ ); | |
522 | if ( !show_layer_names ) | |
523 | // remember the name column so we can sort by it later | |
524 | sort_by_column = column; | |
525 | ||
526 | column = my_new_column_text ( _("Date"), renderer, view, column_runner++ ); | |
527 | ||
528 | GtkCellRenderer *renderer_toggle = gtk_cell_renderer_toggle_new (); | |
529 | column = gtk_tree_view_column_new_with_attributes ( _("Visible"), renderer_toggle, "active", column_runner, NULL ); | |
530 | gtk_tree_view_column_set_sort_column_id ( column, column_runner ); | |
531 | gtk_tree_view_append_column ( GTK_TREE_VIEW(view), column ); | |
532 | column_runner++; | |
533 | ||
534 | switch ( dist_units ) { | |
535 | case VIK_UNITS_DISTANCE_MILES: | |
536 | column = my_new_column_text ( _("Distance\n(miles)"), renderer, view, column_runner++ ); | |
537 | break; | |
538 | default: | |
539 | column = my_new_column_text ( _("Distance\n(km)"), renderer, view, column_runner++ ); | |
540 | break; | |
541 | } | |
542 | // Apply own formatting of the data | |
543 | gtk_tree_view_column_set_cell_data_func ( column, renderer, format_1f_cell_data_func, GINT_TO_POINTER(column_runner-1), NULL); | |
544 | ||
545 | column = my_new_column_text ( _("Length\n(minutes)"), renderer, view, column_runner++ ); | |
546 | ||
547 | gchar *spd_units = NULL; | |
548 | switch (speed_units) { | |
549 | case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: spd_units = g_strdup (_("km/h")); break; | |
550 | case VIK_UNITS_SPEED_MILES_PER_HOUR: spd_units = g_strdup (_("mph")); break; | |
551 | case VIK_UNITS_SPEED_KNOTS: spd_units = g_strdup (_("knots")); break; | |
552 | // VIK_UNITS_SPEED_METRES_PER_SECOND: | |
553 | default: spd_units = g_strdup (_("m/s")); break; | |
554 | } | |
555 | ||
556 | gchar *title = g_strdup_printf ( _("Av. Speed\n(%s)"), spd_units ); | |
557 | column = my_new_column_text ( title, renderer, view, column_runner++ ); | |
558 | g_free ( title ); | |
559 | 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 | |
560 | ||
561 | title = g_strdup_printf ( _("Max Speed\n(%s)"), spd_units ); | |
562 | column = my_new_column_text ( title, renderer, view, column_runner++ ); | |
563 | 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 | |
564 | ||
565 | g_free ( title ); | |
566 | g_free ( spd_units ); | |
567 | ||
568 | if ( height_units == VIK_UNITS_HEIGHT_FEET ) | |
569 | column = my_new_column_text ( _("Max Height\n(Feet)"), renderer, view, column_runner++ ); | |
570 | else | |
571 | column = my_new_column_text ( _("Max Height\n(Metres)"), renderer, view, column_runner++ ); | |
572 | ||
573 | gtk_tree_view_set_model ( GTK_TREE_VIEW(view), GTK_TREE_MODEL(store) ); | |
574 | gtk_tree_selection_set_mode ( gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), GTK_SELECTION_BROWSE ); // GTK_SELECTION_MULTIPLE | |
575 | gtk_tree_view_set_rules_hint ( GTK_TREE_VIEW(view), TRUE ); | |
576 | ||
577 | g_object_unref(store); | |
578 | ||
579 | GtkWidget *scrolledwindow = gtk_scrolled_window_new ( NULL, NULL ); | |
580 | gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); | |
581 | gtk_container_add ( GTK_CONTAINER(scrolledwindow), view ); | |
582 | ||
583 | g_object_set ( view, "has-tooltip", TRUE, NULL); | |
584 | ||
585 | g_signal_connect ( view, "query-tooltip", G_CALLBACK (trw_layer_track_tooltip_cb), NULL ); | |
586 | //g_signal_connect ( gtk_tree_view_get_selection (GTK_TREE_VIEW(view)), "changed", G_CALLBACK(trw_layer_track_select_cb), view ); | |
587 | ||
588 | g_signal_connect ( view, "popup-menu", G_CALLBACK(trw_layer_track_menu_popup), tracks_and_layers ); | |
589 | g_signal_connect ( view, "button-press-event", G_CALLBACK(trw_layer_track_button_pressed), tracks_and_layers ); | |
590 | ||
591 | gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), scrolledwindow, TRUE, TRUE, 0); | |
592 | ||
593 | // Set ordering of the initial view by one of the name columns | |
594 | gtk_tree_view_column_clicked ( sort_by_column ); | |
595 | ||
596 | // Ensure a reasonable number of items are shown | |
597 | // TODO may wish to impose maximum width, especially on the name fields (+set ellipize?) | |
598 | // TODO may be save window size between invocations. | |
599 | gtk_window_set_default_size ( GTK_WINDOW(dialog), show_layer_names ? 1000 : 800, 400 ); | |
600 | } | |
601 | ||
602 | ||
603 | /** | |
604 | * vik_trw_layer_track_list_show_dialog: | |
605 | * @title: The title for the dialog | |
606 | * @vl: The #VikLayer passed on into get_tracks_and_layers_cb() | |
607 | * @user_data: Data passed on into get_tracks_and_layers_cb() | |
608 | * @get_tracks_and_layers_cb: The function to call to construct items to be analysed | |
609 | * @show_layer_names: Normally only set when called from an aggregate level | |
610 | * | |
611 | * Common method for showing a list of tracks with extended information | |
612 | * | |
613 | */ | |
614 | void vik_trw_layer_track_list_show_dialog ( gchar *title, | |
615 | VikLayer *vl, | |
616 | gpointer user_data, | |
617 | VikTrwlayerGetTracksAndLayersFunc get_tracks_and_layers_cb, | |
618 | gboolean show_layer_names ) | |
619 | { | |
620 | GtkWidget *dialog = gtk_dialog_new_with_buttons ( title, | |
621 | VIK_GTK_WINDOW_FROM_LAYER(vl), | |
622 | GTK_DIALOG_DESTROY_WITH_PARENT, | |
623 | GTK_STOCK_CLOSE, | |
624 | GTK_RESPONSE_CLOSE, | |
625 | NULL ); | |
626 | ||
627 | GList *gl = get_tracks_and_layers_cb ( vl, user_data ); | |
628 | ||
629 | vik_trw_layer_track_list_internal ( dialog, gl, show_layer_names ); | |
630 | ||
631 | // Use response to close the dialog with tidy up | |
632 | g_signal_connect ( G_OBJECT(dialog), "response", G_CALLBACK(track_close_cb), gl ); | |
633 | ||
634 | gtk_widget_show_all ( dialog ); | |
635 | ||
636 | // ATM lock out on dialog run - to prevent list contents being manipulated in other parts of the GUI whilst shown here. | |
637 | gtk_dialog_run (GTK_DIALOG (dialog)); | |
638 | // Unfortunately seems subsequently opening the Track Properties we can't interact with it until this dialog is closed | |
639 | // Thus this dialog is then forcibly closed when opening the properties. | |
640 | ||
641 | // Occassionally the 'View' doesn't update the viewport properly | |
642 | // viewport center + zoom is changed but the viewport isn't updated | |
643 | // not sure why yet.. | |
644 | } |