]>
Commit | Line | Data |
---|---|---|
f93e0210 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 | */ | |
5ab2942c RN |
22 | /* |
23 | * Dependencies in this file can be on anything. | |
24 | * For functions with simple system dependencies put it in util.c | |
25 | */ | |
f93e0210 | 26 | #include <math.h> |
5ab2942c | 27 | #include <glib/gstdio.h> |
f93e0210 | 28 | #include <glib/gi18n.h> |
5ab2942c | 29 | #include <gtk/gtk.h> |
f93e0210 | 30 | |
f93e0210 | 31 | #include "vikutils.h" |
5ab2942c RN |
32 | #include "globals.h" |
33 | #include "download.h" | |
34 | #include "preferences.h" | |
35 | #include "vikmapslayer.h" | |
36 | #include "settings.h" | |
37 | #include "util.h" | |
f93e0210 RN |
38 | |
39 | #define FMT_MAX_NUMBER_CODES 9 | |
40 | ||
41 | /** | |
42 | * vu_trackpoint_formatted_message: | |
43 | * @format_code: String describing the message to generate | |
44 | * @trkpt: The trackpoint for which the message is generated about | |
45 | * @trkpt_prev: A trackpoint (presumed previous) for interpolating values with the other trackpoint (such as speed) | |
46 | * @trk: The track in which the trackpoints reside | |
47 | * | |
48 | * TODO: One day replace this cryptic format code with some kind of tokenizer parsing | |
49 | * thus would make it more user friendly and maybe even GUI controlable. | |
50 | * However for now at least there is some semblance of user control | |
51 | */ | |
52 | gchar* vu_trackpoint_formatted_message ( gchar *format_code, VikTrackpoint *trkpt, VikTrackpoint *trkpt_prev, VikTrack *trk ) | |
53 | { | |
54 | if ( !trkpt ) | |
55 | return NULL; | |
56 | ||
57 | gint len = 0; | |
58 | if ( format_code ) | |
59 | len = strlen ( format_code ); | |
60 | if ( len > FMT_MAX_NUMBER_CODES ) | |
61 | len = FMT_MAX_NUMBER_CODES; | |
62 | ||
63 | gchar* values[FMT_MAX_NUMBER_CODES]; | |
64 | int i; | |
65 | for ( i = 0; i < FMT_MAX_NUMBER_CODES; i++ ) { | |
66 | values[i] = '\0'; | |
67 | } | |
68 | ||
69 | gchar *speed_units_str = NULL; | |
70 | vik_units_speed_t speed_units = a_vik_get_units_speed (); | |
71 | switch (speed_units) { | |
72 | case VIK_UNITS_SPEED_MILES_PER_HOUR: | |
73 | speed_units_str = g_strdup ( _("mph") ); | |
74 | break; | |
75 | case VIK_UNITS_SPEED_METRES_PER_SECOND: | |
76 | speed_units_str = g_strdup ( _("m/s") ); | |
77 | break; | |
78 | case VIK_UNITS_SPEED_KNOTS: | |
79 | speed_units_str = g_strdup ( _("knots") ); | |
80 | break; | |
81 | default: | |
82 | // VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: | |
83 | speed_units_str = g_strdup ( _("km/h") ); | |
84 | break; | |
85 | } | |
86 | ||
87 | gchar *separator = g_strdup ( " | " ); | |
88 | ||
89 | for ( i = 0; i < len; i++ ) { | |
90 | switch ( g_ascii_toupper ( format_code[i] ) ) { | |
91 | case 'G': values[i] = g_strdup ( _("GPSD") ); break; // GPS Preamble | |
92 | case 'K': values[i] = g_strdup ( _("Trkpt") ); break; // Trkpt Preamble | |
93 | ||
94 | case 'S': { | |
95 | gdouble speed = 0.0; | |
96 | gchar *speedtype = NULL; | |
97 | if ( !isnan(trkpt->speed) && trkpt_prev ) { | |
98 | if ( trkpt->has_timestamp && trkpt_prev->has_timestamp ) { | |
99 | if ( trkpt->timestamp == trkpt_prev->timestamp ) { | |
100 | ||
101 | // Work out from previous trackpoint location and time difference | |
102 | speed = vik_coord_diff(&(trkpt->coord), &(trkpt_prev->coord)) / ABS(trkpt->timestamp - trkpt_prev->timestamp); | |
103 | ||
104 | switch (speed_units) { | |
105 | case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: | |
106 | speed = VIK_MPS_TO_KPH(speed); | |
107 | break; | |
108 | case VIK_UNITS_SPEED_MILES_PER_HOUR: | |
109 | speed = VIK_MPS_TO_MPH(speed); | |
110 | break; | |
111 | case VIK_UNITS_SPEED_KNOTS: | |
112 | speed = VIK_MPS_TO_KNOTS(speed); | |
113 | break; | |
114 | default: | |
115 | // VIK_UNITS_SPEED_METRES_PER_SECOND: | |
116 | // Already in m/s so nothing to do | |
117 | break; | |
118 | } | |
119 | speedtype = g_strdup ( "*" ); // Interpolated | |
120 | } | |
121 | else | |
122 | speedtype = g_strdup ( "**" ); | |
123 | } | |
124 | else | |
125 | speedtype = g_strdup ( "**" ); | |
126 | } | |
127 | else { | |
128 | speed = trkpt->speed; | |
129 | speedtype = g_strdup ( "" ); | |
130 | } | |
131 | ||
132 | values[i] = g_strdup_printf ( _("%sSpeed%s %.1f%s"), separator, speedtype, speed, speed_units_str ); | |
133 | g_free ( speedtype ); | |
134 | break; | |
135 | } | |
136 | ||
137 | case 'A': { | |
138 | vik_units_height_t height_units = a_vik_get_units_height (); | |
139 | switch (height_units) { | |
140 | case VIK_UNITS_HEIGHT_FEET: | |
141 | values[i] = g_strdup_printf ( _("%sAlt %dfeet"), separator, (int)round(VIK_METERS_TO_FEET(trkpt->altitude)) ); | |
142 | break; | |
143 | default: | |
144 | //VIK_UNITS_HEIGHT_METRES: | |
145 | values[i] = g_strdup_printf ( _("%sAlt %dm"), separator, (int)round(trkpt->altitude) ); | |
146 | break; | |
147 | } | |
148 | break; | |
149 | } | |
150 | ||
151 | case 'C': { | |
152 | gint heading = isnan(trkpt->course) ? 0 : (gint)round(trkpt->course); | |
153 | values[i] = g_strdup_printf ( _("%sCourse %03d\302\260" ), separator, heading ); | |
154 | break; | |
155 | } | |
156 | ||
157 | case 'P': { | |
158 | if ( trkpt_prev ) { | |
159 | gint diff = (gint) round ( vik_coord_diff ( &(trkpt->coord), &(trkpt_prev->coord) ) ); | |
160 | ||
161 | gchar *dist_units_str = NULL; | |
162 | vik_units_distance_t dist_units = a_vik_get_units_distance (); | |
163 | // expect the difference between track points to be small hence use metres or yards | |
164 | switch (dist_units) { | |
165 | case VIK_UNITS_DISTANCE_MILES: | |
166 | dist_units_str = g_strdup ( _("yards") ); | |
167 | break; | |
168 | default: | |
169 | // VIK_UNITS_DISTANCE_KILOMETRES: | |
170 | dist_units_str = g_strdup ( _("m") ); | |
171 | break; | |
172 | } | |
173 | ||
174 | values[i] = g_strdup_printf ( _("%sDistance diff %d%s"), separator, diff, dist_units_str ); | |
175 | ||
176 | g_free ( dist_units_str ); | |
177 | } | |
178 | break; | |
179 | } | |
180 | ||
181 | case 'T': { | |
182 | gchar tmp[64]; | |
183 | tmp[0] = '\0'; | |
184 | if ( trkpt->has_timestamp ) { | |
185 | // Compact date time format | |
186 | strftime (tmp, sizeof(tmp), "%x %X", localtime(&(trkpt->timestamp))); | |
187 | } | |
188 | else | |
189 | g_snprintf (tmp, sizeof(tmp), "--"); | |
190 | values[i] = g_strdup_printf ( _("%sTime %s"), separator, tmp ); | |
191 | break; | |
192 | } | |
193 | ||
194 | case 'M': { | |
195 | if ( trkpt_prev ) { | |
196 | if ( trkpt->has_timestamp && trkpt_prev->has_timestamp ) { | |
197 | time_t t_diff = trkpt->timestamp - trkpt_prev->timestamp; | |
198 | values[i] = g_strdup_printf ( _("%sTime diff %lds"), separator, t_diff ); | |
199 | } | |
200 | } | |
201 | break; | |
202 | } | |
203 | ||
204 | case 'X': values[i] = g_strdup_printf ( _("%sNo. of Sats %d"), separator, trkpt->nsats ); break; | |
205 | ||
206 | case 'D': { | |
207 | if ( trk ) { | |
bb77b487 | 208 | // Distance from start (along the track) |
f93e0210 RN |
209 | gdouble distd = vik_track_get_length_to_trackpoint (trk, trkpt); |
210 | gchar *dist_units_str = NULL; | |
211 | vik_units_distance_t dist_units = a_vik_get_units_distance (); | |
212 | // expect the difference between track points to be small hence use metres or yards | |
213 | switch (dist_units) { | |
214 | case VIK_UNITS_DISTANCE_MILES: | |
215 | dist_units_str = g_strdup ( _("miles") ); | |
216 | distd = VIK_METERS_TO_MILES(distd); | |
217 | break; | |
218 | default: | |
219 | // VIK_UNITS_DISTANCE_KILOMETRES: | |
220 | dist_units_str = g_strdup ( _("km") ); | |
221 | distd = distd / 1000.0; | |
222 | break; | |
223 | } | |
224 | values[i] = g_strdup_printf ( _("%sDistance along %.2f%s"), separator, distd, dist_units_str ); | |
225 | g_free ( dist_units_str ); | |
226 | } | |
227 | break; | |
228 | } | |
229 | ||
230 | case 'L': { | |
231 | // Location (Lat/Long) | |
232 | gchar *lat = NULL, *lon = NULL; | |
233 | struct LatLon ll; | |
234 | vik_coord_to_latlon (&(trkpt->coord), &ll); | |
235 | a_coords_latlon_to_string ( &ll, &lat, &lon ); | |
236 | values[i] = g_strdup_printf ( "%s%s %s", separator, lat, lon ); | |
237 | g_free ( lat ); | |
238 | g_free ( lon ); | |
239 | break; | |
240 | } | |
241 | ||
242 | case 'N': // Name of track | |
243 | values[i] = g_strdup_printf ( _("%sTrack: %s"), separator, trk->name ); | |
244 | break; | |
245 | ||
b45865b4 RN |
246 | case 'E': // Name of trackpoint if available |
247 | if ( trkpt->name ) | |
248 | values[i] = g_strdup_printf ( "%s%s", separator, trkpt->name ); | |
249 | else | |
250 | values[i] = g_strdup ( "" ); | |
251 | break; | |
252 | ||
f93e0210 RN |
253 | default: |
254 | break; | |
255 | } | |
256 | } | |
257 | ||
258 | g_free ( separator ); | |
259 | g_free ( speed_units_str ); | |
260 | ||
bb77b487 | 261 | gchar *msg = g_strconcat ( values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], NULL ); |
f93e0210 RN |
262 | |
263 | for ( i = 0; i < FMT_MAX_NUMBER_CODES; i++ ) { | |
264 | if ( values[i] != '\0' ) | |
265 | g_free ( values[i] ); | |
266 | } | |
267 | ||
268 | return msg; | |
269 | } | |
5ab2942c RN |
270 | |
271 | typedef struct { | |
272 | GtkWindow *window; // Layer needed for redrawing | |
273 | gchar *version; // Image list | |
274 | } new_version_thread_data; | |
275 | ||
276 | static gboolean new_version_available_message ( new_version_thread_data *nvtd ) | |
277 | { | |
278 | // Only a simple goto website option is offered | |
279 | // Trying to do an installation update is platform specific | |
280 | if ( a_dialog_yes_or_no ( nvtd->window, | |
281 | _("There is a newer version of Viking available: %s\n\nDo you wish to go to Viking's website now?"), nvtd->version ) ) | |
282 | // NB 'VIKING_URL' redirects to the Wiki, here we want to go the main site. | |
283 | open_url ( nvtd->window, "http://sourceforge.net/projects/viking/" ); | |
284 | ||
285 | g_free ( nvtd->version ); | |
286 | g_free ( nvtd ); | |
287 | return FALSE; | |
288 | } | |
289 | ||
290 | #define VIK_SETTINGS_VERSION_CHECKED_DATE "version_checked_date" | |
291 | ||
292 | static void latest_version_thread ( GtkWindow *window ) | |
293 | { | |
294 | // Need to allow a few redirects, as SF file is often served from different server | |
295 | DownloadMapOptions options = { FALSE, FALSE, NULL, 5, NULL, NULL, NULL }; | |
296 | gchar *filename = a_download_uri_to_tmp_file ( "http://sourceforge.net/projects/viking/files/VERSION", &options ); | |
297 | //gchar *filename = g_strdup ( "VERSION" ); | |
298 | if ( !filename ) { | |
299 | return; | |
300 | } | |
301 | ||
302 | GMappedFile *mf = g_mapped_file_new ( filename, FALSE, NULL ); | |
303 | if ( !mf ) | |
304 | return; | |
305 | ||
306 | gchar *text = g_mapped_file_get_contents ( mf ); | |
307 | ||
308 | gint latest_version = viking_version_to_number ( text ); | |
309 | gint my_version = viking_version_to_number ( VIKING_VERSION ); | |
310 | ||
311 | g_debug ( "The lastest version is: %s", text ); | |
312 | ||
313 | if ( my_version < latest_version ) { | |
314 | new_version_thread_data *nvtd = g_malloc ( sizeof(new_version_thread_data) ); | |
315 | nvtd->window = window; | |
316 | nvtd->version = g_strdup ( text ); | |
317 | gdk_threads_add_idle ( (GSourceFunc) new_version_available_message, nvtd ); | |
318 | } | |
319 | else | |
320 | g_debug ( "Running the lastest version: %s", VIKING_VERSION ); | |
321 | ||
322 | g_mapped_file_unref ( mf ); | |
323 | if ( filename ) { | |
324 | g_remove ( filename ); | |
325 | g_free ( filename ); | |
326 | } | |
327 | ||
328 | // Update last checked time | |
329 | GTimeVal time; | |
330 | g_get_current_time ( &time ); | |
331 | a_settings_set_string ( VIK_SETTINGS_VERSION_CHECKED_DATE, g_time_val_to_iso8601(&time) ); | |
332 | } | |
333 | ||
334 | #define VIK_SETTINGS_VERSION_CHECK_PERIOD "version_check_period_days" | |
335 | ||
336 | /** | |
337 | * vu_check_latest_version: | |
338 | * @window: Somewhere where we may need use the display to inform the user about the version status | |
339 | * | |
340 | * Periodically checks the released latest VERSION file on the website to compare with the running version | |
341 | * | |
342 | */ | |
343 | void vu_check_latest_version ( GtkWindow *window ) | |
344 | { | |
345 | if ( ! a_vik_get_check_version () ) | |
346 | return; | |
347 | ||
348 | gboolean do_check = FALSE; | |
349 | ||
350 | gint check_period; | |
351 | if ( ! a_settings_get_integer ( VIK_SETTINGS_VERSION_CHECK_PERIOD, &check_period ) ) { | |
352 | check_period = 14; | |
353 | } | |
354 | ||
355 | // Get last checked date... | |
356 | GDate *gdate_last = g_date_new(); | |
357 | GDate *gdate_now = g_date_new(); | |
358 | GTimeVal time_last; | |
359 | gchar *last_checked_date = NULL; | |
360 | ||
361 | // When no previous date available - set to do the version check | |
362 | if ( a_settings_get_string ( VIK_SETTINGS_VERSION_CHECKED_DATE, &last_checked_date) ) { | |
363 | if ( g_time_val_from_iso8601 ( last_checked_date, &time_last ) ) { | |
364 | g_date_set_time_val ( gdate_last, &time_last ); | |
365 | } | |
366 | else | |
367 | do_check = TRUE; | |
368 | } | |
369 | else | |
370 | do_check = TRUE; | |
371 | ||
372 | GTimeVal time_now; | |
373 | g_get_current_time ( &time_now ); | |
374 | g_date_set_time_val ( gdate_now, &time_now ); | |
375 | ||
376 | if ( ! do_check ) { | |
377 | // Dates available so do the comparison | |
378 | g_date_add_days ( gdate_last, check_period ); | |
379 | if ( g_date_compare ( gdate_last, gdate_now ) < 0 ) | |
380 | do_check = TRUE; | |
381 | } | |
382 | ||
383 | g_date_free ( gdate_last ); | |
384 | g_date_free ( gdate_now ); | |
385 | ||
386 | if ( do_check ) { | |
387 | #if GLIB_CHECK_VERSION (2, 32, 0) | |
388 | g_thread_try_new ( "latest_version_thread", (GThreadFunc)latest_version_thread, window, NULL ); | |
389 | #else | |
390 | g_thread_create ( (GThreadFunc)latest_version_thread, window, FALSE, NULL ); | |
391 | #endif | |
392 | } | |
393 | } | |
394 | ||
395 | /** | |
396 | * vu_set_auto_features_on_first_run: | |
397 | * | |
398 | * Ask the user's opinion to set some of Viking's default behaviour | |
399 | */ | |
400 | void vu_set_auto_features_on_first_run ( void ) | |
401 | { | |
402 | gboolean auto_features = FALSE; | |
403 | if ( a_vik_very_first_run () ) { | |
404 | ||
405 | GtkWidget *win = gtk_window_new ( GTK_WINDOW_TOPLEVEL ); | |
406 | ||
407 | if ( a_dialog_yes_or_no ( GTK_WINDOW(win), | |
408 | _("This appears to be Viking's very first run.\n\nDo you wish to enable automatic internet features?\n\nIndividual settings can be controlled in the Preferences."), NULL ) ) | |
409 | auto_features = TRUE; | |
410 | } | |
411 | ||
412 | if ( auto_features ) { | |
413 | // Set Maps to autodownload | |
414 | // Ensure the default is true | |
415 | maps_layer_set_autodownload_default ( TRUE ); | |
416 | ||
417 | // Simplistic repeat of preference settings | |
418 | // Only the name & type are important for setting a preference via this 'external' way | |
419 | ||
420 | // Enable auto add map + | |
421 | // Enable IP lookup | |
422 | VikLayerParam pref_add_map[] = { { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_STARTUP_NAMESPACE "add_default_map_layer", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, NULL, VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, NULL, NULL, NULL, }, }; | |
423 | VikLayerParam pref_startup_method[] = { { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_STARTUP_NAMESPACE "startup_method", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, NULL, VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, NULL, NULL, NULL}, }; | |
424 | ||
425 | VikLayerParamData vlp_data; | |
426 | vlp_data.b = TRUE; | |
427 | a_preferences_run_setparam ( vlp_data, pref_add_map ); | |
428 | ||
429 | vlp_data.u = VIK_STARTUP_METHOD_AUTO_LOCATION; | |
430 | a_preferences_run_setparam ( vlp_data, pref_startup_method ); | |
431 | ||
432 | // Only on Windows make checking for the latest version on by default | |
433 | // For other systems it's expected a Package manager or similar controls the installation, so leave it off | |
434 | #ifdef WINDOWS | |
435 | VikLayerParam pref_startup_version_check[] = { { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_STARTUP_NAMESPACE "check_version", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, NULL, VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, NULL, }, }; | |
436 | vlp_data.b = TRUE; | |
437 | a_preferences_run_setparam ( vlp_data, pref_startup_version_check ); | |
438 | #endif | |
439 | ||
440 | // Ensure settings are saved for next time | |
441 | a_preferences_save_to_file (); | |
442 | } | |
443 | } |