]> git.street.me.uk Git - andy/viking.git/blame_incremental - src/viktrwlayer_analysis.c
Some spelling fixes in a comment
[andy/viking.git] / src / viktrwlayer_analysis.c
... / ...
CommitLineData
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 *
23 */
24
25#include <math.h>
26#include <time.h>
27#include <string.h>
28#include <glib/gprintf.h>
29#include <glib/gi18n.h>
30
31#include "viking.h"
32#include "viktrwlayer_analysis.h"
33#include "ui_util.h"
34
35// Units of each item are in SI Units
36// (as returned by the appropriate internal viking track functions)
37typedef struct {
38 gdouble min_alt;
39 gdouble max_alt;
40 gdouble elev_gain;
41 gdouble elev_loss;
42 gdouble length;
43 gdouble length_gaps;
44 gdouble max_speed;
45 gulong trackpoints;
46 guint segments;
47 gint duration;
48 time_t start_time;
49 time_t end_time;
50 gint count;
51} track_stats;
52
53// Early incarnations of the code had facilities to print output for multiple files
54// but has been rescoped to work on a single list of tracks for the GUI
55typedef enum {
56 //TS_TRACK,
57 TS_TRACKS,
58 //TS_FILES,
59} track_stat_block;
60static track_stats tracks_stats[1];
61
62// cf with vik_track_get_minmax_alt internals
63#define VIK_VAL_MIN_ALT 25000.0
64#define VIK_VAL_MAX_ALT -5000.0
65
66/**
67 * Reset the specified block
68 * Call this when starting to processing multiple items
69 */
70static void val_reset ( track_stat_block block )
71{
72 tracks_stats[block].min_alt = VIK_VAL_MIN_ALT;
73 tracks_stats[block].max_alt = VIK_VAL_MAX_ALT;
74 tracks_stats[block].elev_gain = 0.0;
75 tracks_stats[block].elev_loss = 0.0;
76 tracks_stats[block].length = 0.0;
77 tracks_stats[block].length_gaps = 0.0;
78 tracks_stats[block].max_speed = 0.0;
79 tracks_stats[block].trackpoints = 0;
80 tracks_stats[block].segments = 0;
81 tracks_stats[block].duration = 0;
82 tracks_stats[block].start_time = 0;
83 tracks_stats[block].end_time = 0;
84 tracks_stats[block].count = 0;
85}
86
87/**
88 * @val_analyse_track:
89 * @trk: The track to be analyse
90 *
91 * Function to collect statistics, using the internal track functions
92 */
93static void val_analyse_track ( VikTrack *trk )
94{
95 //val_reset ( TS_TRACK );
96 gdouble min_alt;
97 gdouble max_alt;
98 gdouble up;
99 gdouble down;
100
101 gdouble length = 0.0;
102 gdouble length_gaps = 0.0;
103 gdouble max_speed = 0.0;
104 gulong trackpoints = 0;
105 guint segments = 0;
106
107 tracks_stats[TS_TRACKS].count++;
108
109 trackpoints = vik_track_get_tp_count (trk);
110 segments = vik_track_get_segment_count (trk);
111 length = vik_track_get_length (trk);
112 length_gaps = vik_track_get_length_including_gaps (trk);
113 max_speed = vik_track_get_max_speed (trk);
114
115 int ii;
116 for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) {
117 tracks_stats[ii].trackpoints += trackpoints;
118 tracks_stats[ii].segments += segments;
119 tracks_stats[ii].length += length;
120 tracks_stats[ii].length_gaps += length_gaps;
121 if ( max_speed > tracks_stats[ii].max_speed )
122 tracks_stats[ii].max_speed = max_speed;
123 }
124
125 if ( vik_track_get_minmax_alt (trk, &min_alt, &max_alt) ) {
126 for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) {
127 if ( min_alt < tracks_stats[ii].min_alt )
128 tracks_stats[ii].min_alt = min_alt;
129 if ( max_alt > tracks_stats[ii].max_alt )
130 tracks_stats[ii].max_alt = max_alt;
131 }
132 }
133
134 vik_track_get_total_elevation_gain (trk, &up, &down );
135
136 for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) {
137 tracks_stats[ii].elev_gain += up;
138 tracks_stats[ii].elev_loss += down;
139 }
140
141 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->timestamp ) {
142 time_t t1, t2;
143 t1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data)->timestamp;
144 t2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data)->timestamp;
145
146 // Assume never actually have a track with a time of 0 (1st Jan 1970)
147 for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) {
148 if ( tracks_stats[ii].start_time == 0)
149 tracks_stats[ii].start_time = t1;
150 if ( tracks_stats[ii].end_time == 0)
151 tracks_stats[ii].end_time = t2;
152 }
153
154 // Initialize to the first value
155 for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) {
156 if (t1 < tracks_stats[ii].start_time)
157 tracks_stats[ii].start_time = t1;
158 if (t2 > tracks_stats[ii].end_time)
159 tracks_stats[ii].end_time = t2;
160 }
161
162 for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) {
163 tracks_stats[ii].duration = tracks_stats[ii].duration + (int)(t2-t1);
164 }
165 }
166}
167
168// Could use GtkGrids but that is Gtk3+
169static GtkWidget *create_table (int cnt, char *labels[], GtkWidget *contents[])
170{
171 GtkTable *table;
172 int i;
173
174 table = GTK_TABLE(gtk_table_new (cnt, 2, FALSE));
175 gtk_table_set_col_spacing (table, 0, 10);
176 for (i=0; i<cnt; i++) {
177 GtkWidget *label;
178 label = gtk_label_new(NULL);
179 gtk_misc_set_alignment ( GTK_MISC(label), 1, 0.5 ); // Position text centrally in vertical plane
180 // All text labels are set to be in bold
181 char *markup = g_markup_printf_escaped ("<b>%s:</b>", _(labels[i]) );
182 gtk_label_set_markup ( GTK_LABEL(label), markup );
183 g_free ( markup );
184 gtk_table_attach ( table, label, 0, 1, i, i+1, GTK_FILL, GTK_EXPAND, 4, 2 );
185 if (GTK_IS_MISC(contents[i])) {
186 gtk_misc_set_alignment ( GTK_MISC(contents[i]), 0, 0.5 );
187 }
188 gtk_table_attach_defaults ( table, contents[i], 1, 2, i, i+1 );
189 }
190 return GTK_WIDGET (table);
191}
192
193static gchar *label_texts[] = {
194 N_("Number of Tracks"),
195 N_("Date Range"),
196 N_("Total Length"),
197 N_("Average Length"),
198 N_("Max Speed"),
199 N_("Avg. Speed"),
200 N_("Minimum Altitude"),
201 N_("Maximum Altitude"),
202 N_("Total Elevation Gain/Loss"),
203 N_("Avg. Elevation Gain/Loss"),
204 N_("Total Duration"),
205 N_("Avg. Duration"),
206};
207
208/**
209 * create_layout:
210 *
211 * Returns a widget to hold the stats information in a table grid layout
212 */
213static GtkWidget *create_layout ( GtkWidget *content[] )
214{
215 int cnt = 0;
216 for ( cnt = 0; cnt < G_N_ELEMENTS(label_texts); cnt++ )
217 content[cnt] = ui_label_new_selectable ( NULL );
218
219 return create_table (cnt, label_texts, content);
220}
221
222/**
223 * table_output:
224 *
225 * Update the given widgets table with the values from the track stats
226 */
227static void table_output ( track_stats ts, GtkWidget *content[] )
228{
229 int cnt = 0;
230
231 gchar tmp_buf[64];
232 g_snprintf ( tmp_buf, sizeof(tmp_buf), "%d", ts.count );
233 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
234
235 if ( ts.count == 0 ) {
236 // Blank all other fields
237 g_snprintf ( tmp_buf, sizeof(tmp_buf), "--" );
238 for ( cnt = 1; cnt < G_N_ELEMENTS(label_texts); cnt++ )
239 gtk_label_set_text ( GTK_LABEL(content[cnt]), tmp_buf );
240 return;
241 }
242
243 // Check for potential date range
244 // Test if the same day by comparing the date string of the timestamp
245 GDate* gdate_start = g_date_new ();
246 g_date_set_time_t ( gdate_start, ts.start_time );
247 gchar time_start[32];
248 g_date_strftime ( time_start, sizeof(time_start), "%x", gdate_start );
249 g_date_free ( gdate_start );
250
251 GDate* gdate_end = g_date_new ();
252 g_date_set_time_t ( gdate_end, ts.end_time );
253 gchar time_end[32];
254 g_date_strftime ( time_end, sizeof(time_end), "%x", gdate_end );
255 g_date_free ( gdate_end );
256
257 if ( ts.start_time == ts.end_time )
258 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("No Data") );
259 else if ( strncmp(time_start, time_end, 32) )
260 g_snprintf ( tmp_buf, sizeof(tmp_buf), "%s --> %s", time_start, time_end );
261 else
262 g_snprintf ( tmp_buf, sizeof(tmp_buf), "%s", time_start );
263
264 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
265
266 switch (a_vik_get_units_distance ()) {
267 case VIK_UNITS_DISTANCE_MILES:
268 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.1f miles"), VIK_METERS_TO_MILES(ts.length) );
269 break;
270 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
271 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.1f NM"), VIK_METERS_TO_NAUTICAL_MILES(ts.length) );
272 break;
273 default:
274 //VIK_UNITS_DISTANCE_KILOMETRES
275 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.1f km"), ts.length/1000.0 );
276 break;
277 }
278 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
279
280 switch (a_vik_get_units_distance ()) {
281 case VIK_UNITS_DISTANCE_MILES:
282 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.2f miles"), (VIK_METERS_TO_MILES(ts.length)/ts.count) );
283 break;
284 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
285 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.2f NM"), (VIK_METERS_TO_NAUTICAL_MILES(ts.length)/ts.count) );
286 break;
287 default:
288 //VIK_UNITS_DISTANCE_KILOMETRES
289 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.2f km"), ts.length/(1000.0*ts.count) );
290 break;
291 }
292 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
293
294 // I'm sure this could be cleaner...
295 g_snprintf ( tmp_buf, sizeof(tmp_buf), "--" );
296 switch (a_vik_get_units_speed()) {
297 case VIK_UNITS_SPEED_MILES_PER_HOUR:
298 if ( ts.max_speed > 0 )
299 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.1f mph"), (double)VIK_MPS_TO_MPH(ts.max_speed) );
300 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
301 if ( ts.duration > 0 )
302 g_snprintf ( tmp_buf, sizeof(tmp_buf), ("%.1f mph"), (double)VIK_MPS_TO_MPH(ts.length/ts.duration) );
303 else
304 g_snprintf ( tmp_buf, sizeof(tmp_buf), "--" );
305 break;
306 case VIK_UNITS_SPEED_METRES_PER_SECOND:
307 if ( ts.max_speed > 0 )
308 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.2f m/s"), (double)ts.max_speed );
309 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
310 if ( ts.duration > 0 )
311 g_snprintf ( tmp_buf, sizeof(tmp_buf), ("%.2f m/s"), (double)(ts.length/ts.duration) );
312 else
313 g_snprintf ( tmp_buf, sizeof(tmp_buf), "--" );
314 break;
315 case VIK_UNITS_SPEED_KNOTS:
316 if ( ts.max_speed > 0 )
317 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.2f knots\n"), (double)VIK_MPS_TO_KNOTS(ts.max_speed) );
318 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
319 if ( ts.duration > 0 )
320 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.2f knots"), (double)VIK_MPS_TO_KNOTS(ts.length/ts.duration) );
321 else
322 g_snprintf ( tmp_buf, sizeof(tmp_buf), "--" );
323 break;
324 default:
325 //VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
326 if ( ts.max_speed > 0 )
327 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.2f km/h"), (double)VIK_MPS_TO_KPH(ts.max_speed) );
328 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
329 if ( ts.duration > 0 )
330 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%.2f km/h"), (double)VIK_MPS_TO_KPH(ts.length/ts.duration) );
331 else
332 g_snprintf ( tmp_buf, sizeof(tmp_buf), "--" );
333 break;
334 }
335 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
336
337 switch ( a_vik_get_units_height() ) {
338 // Note always round off height value output since sub unit accuracy is overkill
339 case VIK_UNITS_HEIGHT_FEET:
340 if ( ts.min_alt != VIK_VAL_MIN_ALT )
341 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%d feet"), (int)round(VIK_METERS_TO_FEET(ts.min_alt)) );
342 else
343 g_snprintf ( tmp_buf, sizeof(tmp_buf), "--" );
344 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
345
346 if ( ts.max_alt != VIK_VAL_MAX_ALT )
347 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%d feet"), (int)round(VIK_METERS_TO_FEET(ts.max_alt)) );
348 else
349 g_snprintf ( tmp_buf, sizeof(tmp_buf), "--" );
350 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
351
352 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%d feet / %d feet"), (int)round(VIK_METERS_TO_FEET(ts.elev_gain)), (int)round(VIK_METERS_TO_FEET(ts.elev_loss)) );
353 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
354 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%d feet / %d feet"), (int)round(VIK_METERS_TO_FEET(ts.elev_gain/ts.count)), (int)round(VIK_METERS_TO_FEET(ts.elev_loss/ts.count)) );
355 break;
356 default:
357 //VIK_UNITS_HEIGHT_METRES
358 if ( ts.min_alt != VIK_VAL_MIN_ALT )
359 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%d m"), (int)round(ts.min_alt) );
360 else
361 g_snprintf ( tmp_buf, sizeof(tmp_buf), "--" );
362 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
363
364 if ( ts.max_alt != VIK_VAL_MAX_ALT )
365 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%d m"), (int)round(ts.max_alt) );
366 else
367 g_snprintf ( tmp_buf, sizeof(tmp_buf), "--" );
368 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
369
370 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%d m / %d m"), (int)round(ts.elev_gain), (int)round(ts.elev_loss) );
371 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
372 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%d m / %d m"), (int)round(ts.elev_gain/ts.count), (int)round(ts.elev_loss/ts.count) );
373 break;
374 }
375 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
376
377 gint hours;
378 gint minutes;
379 gint days;
380 // Total Duration
381 days = (gint)(ts.duration / (60*60*24));
382 hours = (gint)floor((ts.duration - (days*60*60*24)) / (60*60));
383 minutes = (gint)((ts.duration - (days*60*60*24) - (hours*60*60)) / 60);
384 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%d:%02d:%02d days:hrs:mins"), days, hours, minutes );
385 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
386
387 // Average Duration
388 gint avg_dur = ts.duration / ts.count;
389 hours = (gint)floor(avg_dur / (60*60));
390 minutes = (gint)((avg_dur - (hours*60*60)) / 60);
391 g_snprintf ( tmp_buf, sizeof(tmp_buf), _("%d:%02d hrs:mins"), hours, minutes );
392 gtk_label_set_text ( GTK_LABEL(content[cnt++]), tmp_buf );
393}
394
395/**
396 * val_analyse_item_maybe:
397 * @vtlist: A track and the associated layer to consider for analysis
398 * @data: Whether to include invisible items
399 *
400 * Analyse this particular track
401 * considering whether it should be included depending on it's visibility
402 */
403static void val_analyse_item_maybe ( vik_trw_track_list_t *vtlist, const gpointer data )
404{
405 gboolean include_invisible = GPOINTER_TO_INT(data);
406 VikTrack *trk = vtlist->trk;
407 VikTrwLayer *vtl = vtlist->vtl;
408
409 // Safety first - items shouldn't be deleted...
410 if ( !IS_VIK_TRW_LAYER(vtl) ) return;
411 if ( !trk ) return;
412
413 if ( !include_invisible ) {
414 // Skip invisible layers or sublayers
415 if ( !VIK_LAYER(vtl)->visible ||
416 (trk->is_route && !vik_trw_layer_get_routes_visibility(vtl)) ||
417 (!trk->is_route && !vik_trw_layer_get_tracks_visibility(vtl)) )
418 return;
419
420 // Skip invisible tracks
421 if ( !trk->visible )
422 return;
423 }
424
425 val_analyse_track ( trk );
426}
427
428/**
429 * val_analyse:
430 * @widgets: The widget layout
431 * @tracks_and_layers: A list of #vik_trw_track_list_t
432 * @include_invisible: Whether to include invisible layers and tracks
433 *
434 * Analyse each item in the @tracks_and_layers list
435 *
436 */
437void val_analyse ( GtkWidget *widgets[], GList *tracks_and_layers, gboolean include_invisible )
438{
439 val_reset ( TS_TRACKS );
440
441 GList *gl = g_list_first ( tracks_and_layers );
442 if ( gl ) {
443 g_list_foreach ( gl, (GFunc) val_analyse_item_maybe, GINT_TO_POINTER(include_invisible) );
444 }
445
446 table_output ( tracks_stats[TS_TRACKS], widgets );
447}
448
449typedef struct {
450 GtkWidget **widgets;
451 GtkWidget *layout;
452 GtkWidget *check_button;
453 GList *tracks_and_layers;
454 VikLayer *vl;
455 gpointer user_data;
456 VikTrwlayerGetTracksAndLayersFunc get_tracks_and_layers_cb;
457 VikTrwlayerAnalyseCloseFunc on_close_cb;
458} analyse_cb_t;
459
460static void include_invisible_toggled_cb ( GtkToggleButton *togglebutton, analyse_cb_t *acb )
461{
462 gboolean value = FALSE;
463 if ( gtk_toggle_button_get_active ( togglebutton ) )
464 value = TRUE;
465
466 // Delete old list of items
467 if ( acb->tracks_and_layers ) {
468 g_list_foreach ( acb->tracks_and_layers, (GFunc) g_free, NULL );
469 g_list_free ( acb->tracks_and_layers );
470 }
471
472 // Get the latest list of items to analyse
473 acb->tracks_and_layers = acb->get_tracks_and_layers_cb ( acb->vl, acb->user_data );
474
475 val_analyse ( acb->widgets, acb->tracks_and_layers, value );
476 gtk_widget_show_all ( acb->layout );
477}
478
479#define VIK_SETTINGS_ANALYSIS_DO_INVISIBLE "track_analysis_do_invisible"
480
481/**
482 * analyse_close:
483 *
484 * Multi stage closure - as we need to clear allocations made here
485 * before passing on to the callee so they know then the dialog is closed too
486 */
487static void analyse_close ( GtkWidget *dialog, gint resp, analyse_cb_t *data )
488{
489 // Save current invisible value for next time
490 gboolean do_invisible = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(data->check_button) );
491 a_settings_set_boolean ( VIK_SETTINGS_ANALYSIS_DO_INVISIBLE, do_invisible );
492
493 //g_free ( data->layout );
494 g_free ( data->widgets );
495 g_list_foreach ( data->tracks_and_layers, (GFunc) g_free, NULL );
496 g_list_free ( data->tracks_and_layers );
497
498 if ( data->on_close_cb )
499 data->on_close_cb ( dialog, resp, data->vl );
500
501 g_free ( data );
502}
503
504/**
505 * vik_trw_layer_analyse_this:
506 * @window: A window from which the dialog will be derived
507 * @name: The name to be shown
508 * @vl: The #VikLayer passed on into get_tracks_and_layers_cb()
509 * @user_data: Data passed on into get_tracks_and_layers_cb()
510 * @get_tracks_and_layers_cb: The function to call to construct items to be analysed
511 *
512 * Display a dialog with stats across many tracks
513 *
514 * Returns: The dialog that is created to display the analyse information
515 */
516GtkWidget* vik_trw_layer_analyse_this ( GtkWindow *window,
517 const gchar *name,
518 VikLayer *vl,
519 gpointer user_data,
520 VikTrwlayerGetTracksAndLayersFunc get_tracks_and_layers_cb,
521 VikTrwlayerAnalyseCloseFunc on_close_cb )
522{
523 //VikWindow *vw = VIK_WINDOW(window);
524
525 GtkWidget *dialog;
526 dialog = gtk_dialog_new_with_buttons ( _("Statistics"),
527 window,
528 GTK_DIALOG_DESTROY_WITH_PARENT,
529 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
530 NULL );
531
532 GtkWidget *name_l = gtk_label_new ( NULL );
533 gchar *myname = g_markup_printf_escaped ( "<b>%s</b>", name );
534 gtk_label_set_markup ( GTK_LABEL(name_l), myname );
535 g_free ( myname );
536
537 GtkWidget *content = gtk_dialog_get_content_area ( GTK_DIALOG(dialog) );
538 gtk_box_pack_start ( GTK_BOX(content), name_l, FALSE, FALSE, 10);
539
540 // Get previous value (if any) from the settings
541 gboolean include_invisible;
542 if ( ! a_settings_get_boolean ( VIK_SETTINGS_ANALYSIS_DO_INVISIBLE, &include_invisible ) )
543 include_invisible = TRUE;
544
545 analyse_cb_t *acb = g_malloc (sizeof(analyse_cb_t));
546 acb->vl = vl;
547 acb->user_data = user_data;
548 acb->get_tracks_and_layers_cb = get_tracks_and_layers_cb;
549 acb->on_close_cb = on_close_cb;
550 acb->tracks_and_layers = get_tracks_and_layers_cb ( vl, user_data );
551 acb->widgets = g_malloc ( sizeof(GtkWidget*) * G_N_ELEMENTS(label_texts) );
552 acb->layout = create_layout ( acb->widgets );
553
554 gtk_box_pack_start ( GTK_BOX(content), acb->layout, FALSE, FALSE, 0 );
555
556 // Analysis seems reasonably quick
557 // unless you have really large numbers of tracks (i.e. many many thousands or a really slow computer)
558 // One day might store stats in the track itself....
559 val_analyse ( acb->widgets, acb->tracks_and_layers, include_invisible );
560
561 GtkWidget *cb = gtk_check_button_new_with_label ( _("Include Invisible Items") );
562 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(cb), include_invisible );
563 gtk_box_pack_start ( GTK_BOX(content), cb, FALSE, FALSE, 10);
564 acb->check_button = cb;
565
566 gtk_widget_show_all ( dialog );
567
568 g_signal_connect ( G_OBJECT(cb), "toggled", G_CALLBACK(include_invisible_toggled_cb), acb );
569 g_signal_connect ( G_OBJECT(dialog), "response", G_CALLBACK(analyse_close), acb );
570
571 return dialog;
572}