]> git.street.me.uk Git - andy/viking.git/blob - src/vikgpslayer.c
Add Refresh to consider reloading a Mapnik Rendering configuration.
[andy/viking.git] / src / vikgpslayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2006-2008, Quy Tonthat <qtonthat@gmail.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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <stdlib.h>
28 #ifdef HAVE_MATH_H
29 #include <math.h>
30 #endif
31 #include "viking.h"
32 #include "icons/icons.h"
33 #include "babel.h"
34
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #ifdef HAVE_STRING_H
39 #include <string.h>
40 #endif
41 #include <glib/gstdio.h>
42 #include <glib/gprintf.h>
43 #include <glib/gi18n.h>
44 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
45 #include <gps.h>
46 #include "vikutils.h"
47 #endif
48
49 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp);
50 static void vik_gps_layer_realize ( VikGpsLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter );
51 static void vik_gps_layer_free ( VikGpsLayer *val );
52 static void vik_gps_layer_draw ( VikGpsLayer *val, VikViewport *vp );
53 static VikGpsLayer *vik_gps_layer_new ( VikViewport *vp );
54
55 static void gps_layer_marshall( VikGpsLayer *val, guint8 **data, gint *len );
56 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
57 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
58 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation );
59
60 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl );
61
62 static void gps_layer_change_coord_mode ( VikGpsLayer *val, VikCoordMode mode );
63 static void gps_layer_add_menu_items( VikGpsLayer *vtl, GtkMenu *menu, gpointer vlp );
64
65 static void gps_upload_cb( gpointer layer_and_vlp[2] );
66 static void gps_download_cb( gpointer layer_and_vlp[2] );
67 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] );
68 static void gps_empty_download_cb( gpointer layer_and_vlp[2] );
69 static void gps_empty_all_cb( gpointer layer_and_vlp[2] );
70 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
71 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] );
72 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2] );
73 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp);
74 static void rt_gpsd_disconnect(VikGpsLayer *vgl);
75 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed);
76 #endif
77
78 // Shouldn't need to use these much any more as the protocol is now saved as a string.
79 // They are kept for compatibility loading old .vik files
80 typedef enum {GARMIN_P = 0, MAGELLAN_P, DELORME_P, NAVILINK_P, OLD_NUM_PROTOCOLS} vik_gps_proto;
81 static gchar * protocols_args[]   = {"garmin", "magellan", "delbin", "navilink", NULL};
82 #ifdef WINDOWS
83 static gchar * params_ports[] = {"com1", "usb:", NULL};
84 #else
85 static gchar * params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
86 #endif
87 /* NUM_PORTS not actually used */
88 /* #define NUM_PORTS (sizeof(params_ports)/sizeof(params_ports[0]) - 1) */
89 /* Compatibility with previous versions */
90 #ifdef WINDOWS
91 static gchar * old_params_ports[] = {"com1", "usb:", NULL};
92 #else
93 static gchar * old_params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
94 #endif
95 #define OLD_NUM_PORTS (sizeof(old_params_ports)/sizeof(old_params_ports[0]) - 1)
96
97 typedef struct {
98   GMutex *mutex;
99   vik_gps_dir direction;
100   gchar *port;
101   gboolean ok;
102   gint total_count;
103   gint count;
104   VikTrwLayer *vtl;
105   VikTrack *track;
106   gchar *cmd_args;
107   gchar *window_title;
108   GtkWidget *dialog;
109   GtkWidget *status_label;
110   GtkWidget *gps_label;
111   GtkWidget *ver_label;
112   GtkWidget *id_label;
113   GtkWidget *wp_label;
114   GtkWidget *trk_label;
115   GtkWidget *rte_label;
116   GtkWidget *progress_label;
117   vik_gps_xfer_type progress_type;
118   VikViewport *vvp;
119 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
120   gboolean realtime_tracking;
121 #endif
122 } GpsSession;
123 static void gps_session_delete(GpsSession *sess);
124
125 static gchar *params_groups[] = {
126   N_("Data Mode"),
127 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
128   N_("Realtime Tracking Mode"),
129 #endif
130 };
131
132 enum {GROUP_DATA_MODE, GROUP_REALTIME_MODE};
133
134
135 static VikLayerParamData gps_protocol_default ( void )
136 {
137   VikLayerParamData data;
138   data.s = g_strdup ( "garmin" );
139   return data;
140 }
141
142 static VikLayerParamData gps_port_default ( void )
143 {
144   VikLayerParamData data;
145   data.s = g_strdup ( "usb:" );
146 #ifndef WINDOWS
147   /* Attempt to auto set default USB serial port entry */
148   /* Ordered to make lowest device favourite if available */
149   if (g_access ("/dev/ttyUSB1", R_OK) == 0) {
150         if ( data.s )
151           g_free ( (gchar *)data.s );
152     data.s = g_strdup ("/dev/ttyUSB1");
153   }
154   if (g_access ("/dev/ttyUSB0", R_OK) == 0) {
155         if ( data.s )
156           g_free ( (gchar *)data.s );
157     data.s = g_strdup ("/dev/ttyUSB0");
158   }
159 #endif
160   return data;
161 }
162
163 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
164 static gchar *params_vehicle_position[] = {
165   N_("Keep vehicle at center"),
166   N_("Keep vehicle on screen"),
167   N_("Disable"),
168   NULL
169 };
170 enum {
171   VEHICLE_POSITION_CENTERED = 0,
172   VEHICLE_POSITION_ON_SCREEN,
173   VEHICLE_POSITION_NONE,
174 };
175
176 static VikLayerParamData moving_map_method_default ( void ) { return VIK_LPD_UINT ( VEHICLE_POSITION_ON_SCREEN ); }
177
178 static VikLayerParamData gpsd_host_default ( void )
179 {
180   VikLayerParamData data;
181   data.s = g_strdup ( "localhost" );
182   return data;
183 }
184
185 static VikLayerParamData gpsd_port_default ( void )
186 {
187   VikLayerParamData data;
188   data.s = g_strdup ( DEFAULT_GPSD_PORT );
189   return data;
190 }
191
192 static VikLayerParamData gpsd_retry_interval_default ( void )
193 {
194   VikLayerParamData data;
195   data.s = g_strdup ( "10" );
196   return data;
197 }
198
199 #endif
200
201 static VikLayerParam gps_layer_params[] = {
202  //     NB gps_layer_inst_init() is performed after parameter registeration
203  //  thus to give the protocols some potential values use the old static list
204  // TODO: find another way to use gps_layer_inst_init()?
205   { VIK_LAYER_GPS, "gps_protocol", VIK_LAYER_PARAM_STRING, GROUP_DATA_MODE, N_("GPS Protocol:"), VIK_LAYER_WIDGET_COMBOBOX, protocols_args, NULL, NULL, gps_protocol_default, NULL, NULL }, // List reassigned at runtime
206   { VIK_LAYER_GPS, "gps_port", VIK_LAYER_PARAM_STRING, GROUP_DATA_MODE, N_("Serial Port:"), VIK_LAYER_WIDGET_COMBOBOX, params_ports, NULL, NULL, gps_port_default, NULL, NULL },
207   { VIK_LAYER_GPS, "gps_download_tracks", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Tracks:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
208   { VIK_LAYER_GPS, "gps_upload_tracks", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Tracks:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
209   { VIK_LAYER_GPS, "gps_download_routes", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Routes:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
210   { VIK_LAYER_GPS, "gps_upload_routes", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Routes:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
211   { VIK_LAYER_GPS, "gps_download_waypoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Waypoints:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
212   { VIK_LAYER_GPS, "gps_upload_waypoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Waypoints:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
213 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
214   { VIK_LAYER_GPS, "record_tracking", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Recording tracks"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
215   { VIK_LAYER_GPS, "center_start_tracking", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Jump to current position on start"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
216   { VIK_LAYER_GPS, "moving_map_method", VIK_LAYER_PARAM_UINT, GROUP_REALTIME_MODE, N_("Moving Map Method:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_vehicle_position, NULL, NULL, moving_map_method_default, NULL, NULL },
217   { VIK_LAYER_GPS, "realtime_update_statusbar", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Update Statusbar:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, N_("Display information in the statusbar on GPS updates"), vik_lpd_true_default, NULL, NULL },
218   { VIK_LAYER_GPS, "gpsd_host", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Host:"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, gpsd_host_default, NULL, NULL },
219   { VIK_LAYER_GPS, "gpsd_port", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Port:"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, gpsd_port_default, NULL, NULL },
220   { VIK_LAYER_GPS, "gpsd_retry_interval", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Retry Interval (seconds):"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, gpsd_retry_interval_default, NULL, NULL },
221 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
222 };
223 enum {
224   PARAM_PROTOCOL=0, PARAM_PORT,
225   PARAM_DOWNLOAD_TRACKS, PARAM_UPLOAD_TRACKS,
226   PARAM_DOWNLOAD_ROUTES, PARAM_UPLOAD_ROUTES,
227   PARAM_DOWNLOAD_WAYPOINTS, PARAM_UPLOAD_WAYPOINTS,
228 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
229   PARAM_REALTIME_REC,
230   PARAM_REALTIME_CENTER_START,
231   PARAM_VEHICLE_POSITION,
232   PARAM_REALTIME_UPDATE_STATUSBAR,
233   PARAM_GPSD_HOST,
234   PARAM_GPSD_PORT,
235   PARAM_GPSD_RETRY_INTERVAL,
236 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
237   NUM_PARAMS};
238
239 VikLayerInterface vik_gps_layer_interface = {
240   "GPS",
241   N_("GPS"),
242   NULL,
243   &vikgpslayer_pixbuf,
244
245   NULL,
246   0,
247
248   gps_layer_params,
249   NUM_PARAMS,
250   params_groups,
251   sizeof(params_groups)/sizeof(params_groups[0]),
252
253   VIK_MENU_ITEM_ALL,
254
255   (VikLayerFuncCreate)                  vik_gps_layer_create,
256   (VikLayerFuncRealize)                 vik_gps_layer_realize,
257   (VikLayerFuncPostRead)                NULL,
258   (VikLayerFuncFree)                    vik_gps_layer_free,
259
260   (VikLayerFuncProperties)              NULL,
261   (VikLayerFuncDraw)                    vik_gps_layer_draw,
262   (VikLayerFuncChangeCoordMode)         gps_layer_change_coord_mode,
263
264   (VikLayerFuncSetMenuItemsSelection)   NULL,
265   (VikLayerFuncGetMenuItemsSelection)   NULL,
266
267   (VikLayerFuncAddMenuItems)            gps_layer_add_menu_items,
268   (VikLayerFuncSublayerAddMenuItems)    NULL,
269
270   (VikLayerFuncSublayerRenameRequest)   NULL,
271   (VikLayerFuncSublayerToggleVisible)   NULL,
272   (VikLayerFuncSublayerTooltip)         NULL,
273   (VikLayerFuncLayerTooltip)            gps_layer_tooltip,
274   (VikLayerFuncLayerSelected)           NULL,
275
276   (VikLayerFuncMarshall)                gps_layer_marshall,
277   (VikLayerFuncUnmarshall)              gps_layer_unmarshall,
278
279   (VikLayerFuncSetParam)                gps_layer_set_param,
280   (VikLayerFuncGetParam)                gps_layer_get_param,
281   (VikLayerFuncChangeParam)             NULL,
282
283   (VikLayerFuncReadFileData)            NULL,
284   (VikLayerFuncWriteFileData)           NULL,
285
286   (VikLayerFuncDeleteItem)              NULL,
287   (VikLayerFuncCutItem)                 NULL,
288   (VikLayerFuncCopyItem)                NULL,
289   (VikLayerFuncPasteItem)               NULL,
290   (VikLayerFuncFreeCopiedItem)          NULL,
291   (VikLayerFuncDragDropRequest)         NULL,
292
293   (VikLayerFuncSelectClick)             NULL,
294   (VikLayerFuncSelectMove)              NULL,
295   (VikLayerFuncSelectRelease)           NULL,
296   (VikLayerFuncSelectedViewportMenu)    NULL,
297 };
298
299 enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
300 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
301   TRW_REALTIME,
302 #endif
303   NUM_TRW};
304 static gchar * trw_names[] = {
305   N_("GPS Download"), N_("GPS Upload"),
306 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
307   N_("GPS Realtime Tracking"),
308 #endif
309 };
310
311 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
312 typedef struct {
313   struct gps_data_t gpsd;
314   VikGpsLayer *vgl;
315 } VglGpsd;
316
317 typedef struct {
318   struct gps_fix_t fix;
319   gint satellites_used;
320   gboolean dirty;   /* needs to be saved */
321 } GpsFix;
322 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
323
324 struct _VikGpsLayer {
325   VikLayer vl;
326   VikTrwLayer * trw_children[NUM_TRW];
327   GList * children;     /* used only for writing file */
328   int cur_read_child;   /* used only for reading file */
329 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
330   VglGpsd *vgpsd;
331   gboolean realtime_tracking;  /* set/reset only by the callback */
332   gboolean first_realtime_trackpoint;
333   GpsFix realtime_fix;
334   GpsFix last_fix;
335
336   VikTrack *realtime_track;
337
338   GIOChannel *realtime_io_channel;
339   guint realtime_io_watch_id;
340   guint realtime_retry_timer;
341   GdkGC *realtime_track_gc;
342   GdkGC *realtime_track_bg_gc;
343   GdkGC *realtime_track_pt_gc;
344   GdkGC *realtime_track_pt1_gc;
345   GdkGC *realtime_track_pt2_gc;
346
347   /* params */
348   gchar *gpsd_host;
349   gchar *gpsd_port;
350   gint gpsd_retry_interval;
351   gboolean realtime_record;
352   gboolean realtime_jump_to_start;
353   guint vehicle_position;
354   gboolean realtime_update_statusbar;
355   VikTrackpoint *trkpt;
356   VikTrackpoint *trkpt_prev;
357 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
358   gchar *protocol;
359   gchar *serial_port;
360   gboolean download_tracks;
361   gboolean download_routes;
362   gboolean download_waypoints;
363   gboolean upload_tracks;
364   gboolean upload_routes;
365   gboolean upload_waypoints;
366 };
367
368 /**
369  * Overwrite the static setup with dynamically generated GPS Babel device list
370  */
371 static void gps_layer_inst_init ( VikGpsLayer *self )
372 {
373   gint new_proto = 0;
374   // +1 for luck (i.e the NULL terminator)
375   gchar **new_protocols = g_malloc(1 + g_list_length(a_babel_device_list)*sizeof(gpointer));
376
377   GList *gl = g_list_first ( a_babel_device_list );
378   while ( gl ) {
379     // should be using label property but use name for now
380     //  thus don't need to mess around converting label to name later on
381     new_protocols[new_proto++] = ((BabelDevice*)gl->data)->name;
382     gl = g_list_next ( gl );
383   }
384   new_protocols[new_proto] = NULL;
385
386   vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
387 }
388
389 GType vik_gps_layer_get_type ()
390 {
391   static GType val_type = 0;
392
393   if (!val_type)
394   {
395     static const GTypeInfo val_info =
396     {
397       sizeof (VikGpsLayerClass),
398       NULL, /* base_init */
399       NULL, /* base_finalize */
400       NULL, /* class init */
401       NULL, /* class_finalize */
402       NULL, /* class_data */
403       sizeof (VikGpsLayer),
404       0,
405       (GInstanceInitFunc) gps_layer_inst_init,
406     };
407     val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
408   }
409
410   return val_type;
411 }
412
413 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
414 {
415   int i;
416
417   VikGpsLayer *rv = vik_gps_layer_new (vp);
418   vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
419
420   for (i = 0; i < NUM_TRW; i++) {
421     rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, FALSE ));
422     vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
423   }
424   return rv;
425 }
426
427 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
428 {
429   return vgl->protocol;
430 }
431
432 /* "Copy" */
433 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
434 {
435   VikLayer *child_layer;
436   guint8 *ld; 
437   gint ll;
438   GByteArray* b = g_byte_array_new ();
439   gint len;
440   gint i;
441
442 #define alm_append(obj, sz)     \
443   len = (sz);                   \
444   g_byte_array_append ( b, (guint8 *)&len, sizeof(len) );       \
445   g_byte_array_append ( b, (guint8 *)(obj), len );
446
447   vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
448   alm_append(ld, ll);
449   g_free(ld);
450
451   for (i = 0; i < NUM_TRW; i++) {
452     child_layer = VIK_LAYER(vgl->trw_children[i]);
453     vik_layer_marshall(child_layer, &ld, &ll);
454     if (ld) {
455       alm_append(ld, ll);
456       g_free(ld);
457     }
458   }
459   *data = b->data;
460   *datalen = b->len;
461   g_byte_array_free(b, FALSE);
462 #undef alm_append
463 }
464
465 /* "Paste" */
466 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
467 {
468 #define alm_size (*(gint *)data)
469 #define alm_next \
470   len -= sizeof(gint) + alm_size; \
471   data += sizeof(gint) + alm_size;
472   
473   VikGpsLayer *rv = vik_gps_layer_new(vvp);
474   VikLayer *child_layer;
475   gint i;
476
477   vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
478   alm_next;
479
480   i = 0;
481   while (len>0 && i < NUM_TRW) {
482     child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
483     if (child_layer) {
484       rv->trw_children[i++] = (VikTrwLayer *)child_layer;
485       // NB no need to attach signal update handler here
486       //  as this will always be performed later on in vik_gps_layer_realize()
487     }
488     alm_next;
489   }
490   //  g_print("gps_layer_unmarshall ended with len=%d\n", len);
491   g_assert(len == 0);
492   return rv;
493 #undef alm_size
494 #undef alm_next
495 }
496
497 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
498 {
499   switch ( id )
500   {
501     case PARAM_PROTOCOL:
502       if (data.s) {
503         g_free(vgl->protocol);
504         // Backwards Compatibility: previous versions <v1.4 stored protocol as an array index
505         int index = data.s[0] - '0';
506         if (data.s[0] != '\0' &&
507             g_ascii_isdigit (data.s[0]) &&
508             data.s[1] == '\0' &&
509             index < OLD_NUM_PROTOCOLS)
510           // It is a single digit: activate compatibility
511           vgl->protocol = g_strdup(protocols_args[index]);
512         else
513           vgl->protocol = g_strdup(data.s);
514         g_debug("%s: %s", __FUNCTION__, vgl->protocol);
515       }
516       else
517         g_warning(_("Unknown GPS Protocol"));
518       break;
519     case PARAM_PORT:
520       if (data.s) {
521         g_free(vgl->serial_port);
522         // Backwards Compatibility: previous versions <v0.9.91 stored serial_port as an array index
523         int index = data.s[0] - '0';
524         if (data.s[0] != '\0' &&
525             g_ascii_isdigit (data.s[0]) &&
526             data.s[1] == '\0' &&
527             index < OLD_NUM_PORTS)
528           /* It is a single digit: activate compatibility */
529           vgl->serial_port = g_strdup(old_params_ports[index]);
530         else
531           vgl->serial_port = g_strdup(data.s);
532         g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
533       }
534       else
535         g_warning(_("Unknown serial port device"));
536       break;
537     case PARAM_DOWNLOAD_TRACKS:
538       vgl->download_tracks = data.b;
539       break;
540     case PARAM_UPLOAD_TRACKS:
541       vgl->upload_tracks = data.b;
542       break;
543     case PARAM_DOWNLOAD_ROUTES:
544       vgl->download_routes = data.b;
545       break;
546     case PARAM_UPLOAD_ROUTES:
547       vgl->upload_routes = data.b;
548       break;
549     case PARAM_DOWNLOAD_WAYPOINTS:
550       vgl->download_waypoints = data.b;
551       break;
552     case PARAM_UPLOAD_WAYPOINTS:
553       vgl->upload_waypoints = data.b;
554       break;
555 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
556     case PARAM_GPSD_HOST:
557       if (data.s) {
558         if (vgl->gpsd_host)
559           g_free(vgl->gpsd_host);
560         vgl->gpsd_host = g_strdup(data.s);
561       }
562       break;
563     case PARAM_GPSD_PORT:
564       if (data.s) {
565         if (vgl->gpsd_port)
566           g_free(vgl->gpsd_port);
567         vgl->gpsd_port = g_strdup(data.s);
568       }
569       break;
570     case PARAM_GPSD_RETRY_INTERVAL:
571       vgl->gpsd_retry_interval = strtol(data.s, NULL, 10);
572       break;
573     case PARAM_REALTIME_REC:
574       vgl->realtime_record = data.b;
575       break;
576     case PARAM_REALTIME_CENTER_START:
577       vgl->realtime_jump_to_start = data.b;
578       break;
579     case PARAM_VEHICLE_POSITION:
580       vgl->vehicle_position = data.u;
581       break;
582     case PARAM_REALTIME_UPDATE_STATUSBAR:
583       vgl->realtime_update_statusbar = data.b;
584       break;
585 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
586     default:
587       g_warning("gps_layer_set_param(): unknown parameter");
588   }
589
590   return TRUE;
591 }
592
593 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
594 {
595   VikLayerParamData rv;
596   switch ( id )
597   {
598     case PARAM_PROTOCOL:
599       rv.s = vgl->protocol;
600       g_debug("%s: %s", __FUNCTION__, rv.s);
601       break;
602     case PARAM_PORT:
603       rv.s = vgl->serial_port;
604       g_debug("%s: %s", __FUNCTION__, rv.s);
605       break;
606     case PARAM_DOWNLOAD_TRACKS:
607       rv.b = vgl->download_tracks;
608       break;
609     case PARAM_UPLOAD_TRACKS:
610       rv.b = vgl->upload_tracks;
611       break;
612     case PARAM_DOWNLOAD_ROUTES:
613       rv.b = vgl->download_routes;
614       break;
615     case PARAM_UPLOAD_ROUTES:
616       rv.b = vgl->upload_routes;
617       break;
618     case PARAM_DOWNLOAD_WAYPOINTS:
619       rv.b = vgl->download_waypoints;
620       break;
621     case PARAM_UPLOAD_WAYPOINTS:
622       rv.b = vgl->upload_waypoints;
623       break;
624 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
625     case PARAM_GPSD_HOST:
626       rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
627       break;
628     case PARAM_GPSD_PORT:
629       rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
630       break;
631     case PARAM_GPSD_RETRY_INTERVAL:
632       rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
633       break;
634     case PARAM_REALTIME_REC:
635       rv.b = vgl->realtime_record;
636       break;
637     case PARAM_REALTIME_CENTER_START:
638       rv.b = vgl->realtime_jump_to_start;
639       break;
640     case PARAM_VEHICLE_POSITION:
641       rv.u = vgl->vehicle_position;
642       break;
643     case PARAM_REALTIME_UPDATE_STATUSBAR:
644       rv.u = vgl->realtime_update_statusbar;
645       break;
646 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
647     default:
648       g_warning(_("%s: unknown parameter"), __FUNCTION__);
649   }
650
651   return rv;
652 }
653
654 VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
655 {
656   gint i;
657   VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
658   vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GPS );
659   for (i = 0; i < NUM_TRW; i++) {
660     vgl->trw_children[i] = NULL;
661   }
662   vgl->children = NULL;
663   vgl->cur_read_child = 0;
664
665 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
666   vgl->first_realtime_trackpoint = FALSE;
667   vgl->trkpt = NULL;
668   vgl->trkpt_prev = NULL;
669   vgl->vgpsd = NULL;
670   vgl->realtime_io_channel = NULL;
671   vgl->realtime_io_watch_id = 0;
672   vgl->realtime_retry_timer = 0;
673   if ( vp ) {
674     vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
675     vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
676     vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
677     vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
678     vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
679   }
680   vgl->realtime_track = NULL;
681 #endif // VIK_CONFIG_REALTIME_GPS_TRACKING
682
683   vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
684
685   return vgl;
686 }
687
688 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
689 {
690   gint i;
691   VikLayer *vl;
692   VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
693
694   for (i = 0; i < NUM_TRW; i++) {
695     vl = VIK_LAYER(vgl->trw_children[i]);
696     if (vl == trigger) {
697       if ( vik_viewport_get_half_drawn ( vp ) ) {
698         vik_viewport_set_half_drawn ( vp, FALSE );
699         vik_viewport_snapshot_load( vp );
700       } else {
701         vik_viewport_snapshot_save( vp );
702       }
703     }
704     if (!vik_viewport_get_half_drawn(vp))
705       vik_layer_draw ( vl, vp );
706   }
707 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
708   if (vgl->realtime_tracking) {
709     if (VIK_LAYER(vgl) == trigger) {
710       if ( vik_viewport_get_half_drawn ( vp ) ) {
711         vik_viewport_set_half_drawn ( vp, FALSE );
712         vik_viewport_snapshot_load( vp );
713       } else {
714         vik_viewport_snapshot_save( vp );
715       }
716     }
717     if (!vik_viewport_get_half_drawn(vp))
718       realtime_tracking_draw(vgl, vp);
719   }
720 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
721 }
722
723 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
724 {
725   gint i;
726   for (i = 0; i < NUM_TRW; i++) {
727     vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
728   }
729 }
730
731 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
732 {
733   static gpointer pass_along[2];
734   GtkWidget *item;
735   pass_along[0] = vgl;
736   pass_along[1] = vlp;
737
738   item = gtk_menu_item_new();
739   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
740   gtk_widget_show ( item );
741
742   /* Now with icons */
743   item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS") );
744   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
745   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
746   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
747   gtk_widget_show ( item );
748
749   item = gtk_image_menu_item_new_with_mnemonic ( _("Download from _GPS") );
750   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
751   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
752   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
753   gtk_widget_show ( item );
754
755 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
756   item = gtk_image_menu_item_new_with_mnemonic ( vgl->realtime_tracking  ?
757                                                  "_Stop Realtime Tracking" :
758                                                  "_Start Realtime Tracking" );
759   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, vgl->realtime_tracking ?
760                                   gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_MENU) :
761                                   gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU) );
762   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
763   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
764   gtk_widget_show ( item );
765
766   item = gtk_menu_item_new();
767   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
768   gtk_widget_show ( item );
769
770   item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _Realtime") );
771   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
772   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_realtime_cb), pass_along );
773   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
774   gtk_widget_show ( item );
775 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
776
777   item = gtk_image_menu_item_new_with_mnemonic ( _("E_mpty Upload") );
778   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
779   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
780   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
781   gtk_widget_show ( item );
782
783   item = gtk_image_menu_item_new_with_mnemonic ( _("_Empty Download") );
784   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
785   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_cb), pass_along );
786   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
787   gtk_widget_show ( item );
788
789   item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _All") );
790   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
791   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
792   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
793   gtk_widget_show ( item );
794
795 }
796
797 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
798 {
799   guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, vgl);
800   if ( number_handlers != 1 ) {
801     g_critical(_("Unexpected number of disconnected handlers: %d"), number_handlers);
802   }
803 }
804
805 static void vik_gps_layer_free ( VikGpsLayer *vgl )
806 {
807   gint i;
808   for (i = 0; i < NUM_TRW; i++) {
809     if (vgl->vl.realized)
810       disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
811     g_object_unref(vgl->trw_children[i]);
812   }
813 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
814   rt_gpsd_disconnect(vgl);
815   if (vgl->realtime_track_gc != NULL)
816     g_object_unref(vgl->realtime_track_gc);
817   if (vgl->realtime_track_bg_gc != NULL)
818     g_object_unref(vgl->realtime_track_bg_gc);
819   if (vgl->realtime_track_pt1_gc != NULL)
820     g_object_unref(vgl->realtime_track_pt1_gc);
821   if (vgl->realtime_track_pt2_gc != NULL)
822     g_object_unref(vgl->realtime_track_pt2_gc);
823 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
824 }
825
826 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
827 {
828   GtkTreeIter iter;
829   int ix;
830
831   // TODO set to garmin by default
832   //if (a_babel_device_list)
833   // device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
834   // Need to access uibuild widgets somehow....
835
836   for (ix = 0; ix < NUM_TRW; ix++) {
837     VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
838     vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
839         _(trw_names[ix]), vgl, TRUE,
840         trw, trw->type, trw->type );
841     if ( ! trw->visible )
842       vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
843     vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
844     g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
845   }
846 }
847
848 const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
849 {
850   int i;
851
852   if (vgl->children == NULL) {
853     for (i = NUM_TRW - 1; i >= 0; i--)
854       vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
855   }
856   return vgl->children;
857 }
858
859 VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
860 {
861   g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
862
863   VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
864   if (++(vgl->cur_read_child) >= NUM_TRW)
865     vgl->cur_read_child = 0;
866   return(vtl);
867 }
868
869 gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
870 {
871   if ( vgl->trw_children[0] )
872     return FALSE;
873   return TRUE;
874 }
875
876 static void gps_session_delete(GpsSession *sess)
877 {
878   vik_mutex_free(sess->mutex);
879   g_free(sess->cmd_args);
880   g_free(sess);
881 }
882
883 static void set_total_count(gint cnt, GpsSession *sess)
884 {
885   gchar s[128];
886   gdk_threads_enter();
887   g_mutex_lock(sess->mutex);
888   if (sess->ok) {
889     const gchar *tmp_str;
890     if (sess->direction == GPS_DOWN)
891     {
892       switch (sess->progress_type) {
893       case WPT: tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); sess->total_count = cnt; break;
894       case TRK: tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); sess->total_count = cnt; break;
895       default:
896         {
897           // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints
898           gint mycnt = (cnt / 2) + 1;
899           tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt); break;
900           sess->total_count = mycnt;
901         }
902       }
903     }
904     else
905     {
906       switch (sess->progress_type) {
907       case WPT: tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", cnt); break;
908       case TRK: tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", cnt); break;
909       default: tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", cnt); break;
910       }
911     }
912
913     g_snprintf(s, 128, tmp_str, cnt);
914     gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
915     gtk_widget_show ( sess->progress_label );
916     sess->total_count = cnt;
917   }
918   g_mutex_unlock(sess->mutex);
919   gdk_threads_leave();
920 }
921
922 static void set_current_count(gint cnt, GpsSession *sess)
923 {
924   gchar s[128];
925   const gchar *tmp_str;
926
927   gdk_threads_enter();
928   g_mutex_lock(sess->mutex);
929   if (sess->ok) {
930     if (cnt < sess->total_count) {
931       if (sess->direction == GPS_DOWN)
932       {
933         switch (sess->progress_type) {
934         case WPT: tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->total_count); break;
935         case TRK: tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->total_count); break;
936         default: tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->total_count); break;
937         }
938       }
939       else {
940         switch (sess->progress_type) {
941         case WPT: tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->total_count); break;
942         case TRK: tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->total_count); break;
943         default: tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->total_count); break;
944         }
945       }
946       g_snprintf(s, 128, tmp_str, cnt, sess->total_count);
947     } else {
948       if (sess->direction == GPS_DOWN)
949       {
950         switch (sess->progress_type) {
951         case WPT: tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", cnt); break;
952         case TRK: tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", cnt); break;
953         default: tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", cnt); break;
954         }
955       }
956       else {
957         switch (sess->progress_type) {
958         case WPT: tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", cnt); break;
959         case TRK: tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", cnt); break;
960         default: tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", cnt); break;
961         }
962       }
963       g_snprintf(s, 128, tmp_str, cnt);
964     }     
965     gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
966   }
967   g_mutex_unlock(sess->mutex);
968   gdk_threads_leave();
969 }
970
971 static void set_gps_info(const gchar *info, GpsSession *sess)
972 {
973   gchar s[256];
974   gdk_threads_enter();
975   g_mutex_lock(sess->mutex);
976   if (sess->ok) {
977     g_snprintf(s, 256, _("GPS Device: %s"), info);
978     gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
979   }
980   g_mutex_unlock(sess->mutex);
981   gdk_threads_leave();
982 }
983
984 /*
985  * Common processing for GPS Device information
986  * It doesn't matter whether we're uploading or downloading
987  */
988 static void process_line_for_gps_info ( const gchar *line, GpsSession *sess )
989 {
990   if (strstr(line, "PRDDAT")) {
991     gchar **tokens = g_strsplit(line, " ", 0);
992     gchar info[128];
993     int ilen = 0;
994     int i;
995     int n_tokens = 0;
996
997     while (tokens[n_tokens])
998       n_tokens++;
999
1000     // I'm not entirely clear what information this is trying to get...
1001     //  Obviously trying to decipher some kind of text/naming scheme
1002     //  Anyway this will be superceded if there is 'Unit:' information
1003     if (n_tokens > 8) {
1004       for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
1005         guint ch;
1006         sscanf(tokens[i], "%x", &ch);
1007         info[ilen++] = ch;
1008       }
1009       info[ilen++] = 0;
1010       set_gps_info(info, sess);
1011     }
1012     g_strfreev(tokens);
1013   }
1014
1015   /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
1016   if (strstr(line, "Unit:")) {
1017     gchar **tokens = g_strsplit(line, "\t", 0);
1018     int n_tokens = 0;
1019     while (tokens[n_tokens])
1020       n_tokens++;
1021
1022     if (n_tokens > 1) {
1023       set_gps_info(tokens[1], sess);
1024     }
1025     g_strfreev(tokens);
1026   }
1027 }
1028
1029 static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1030 {
1031   gchar *line;
1032
1033   gdk_threads_enter ();
1034   g_mutex_lock(sess->mutex);
1035   if (!sess->ok) {
1036     g_mutex_unlock(sess->mutex);
1037     gps_session_delete(sess);
1038     gdk_threads_leave();
1039     g_thread_exit ( NULL );
1040   }
1041   g_mutex_unlock(sess->mutex);
1042   gdk_threads_leave ();
1043
1044   switch(c) {
1045   case BABEL_DIAG_OUTPUT:
1046     line = (gchar *)data;
1047
1048     gdk_threads_enter();
1049     g_mutex_lock(sess->mutex);
1050     if (sess->ok) {
1051       gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1052     }
1053     g_mutex_unlock(sess->mutex);
1054     gdk_threads_leave();
1055
1056     /* tells us the type of items that will follow */
1057     if (strstr(line, "Xfer Wpt")) {
1058       sess->progress_label = sess->wp_label;
1059       sess->progress_type = WPT;
1060     }
1061     if (strstr(line, "Xfer Trk")) {
1062       sess->progress_label = sess->trk_label;
1063       sess->progress_type = TRK;
1064     }
1065     if (strstr(line, "Xfer Rte")) {
1066       sess->progress_label = sess->rte_label;
1067       sess->progress_type = RTE;
1068     }
1069
1070     process_line_for_gps_info ( line, sess );
1071
1072     if (strstr(line, "RECORD")) {
1073       int lsb, msb, cnt;
1074
1075       if (strlen(line) > 20) {
1076         sscanf(line+17, "%x", &lsb); 
1077         sscanf(line+20, "%x", &msb);
1078         cnt = lsb + msb * 256;
1079         set_total_count(cnt, sess);
1080         sess->count = 0;
1081       }
1082     }
1083     if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1084       sess->count++;
1085       set_current_count(sess->count, sess);
1086     }
1087     break;
1088   case BABEL_DONE:
1089     break;
1090   default:
1091     break;
1092   }
1093
1094 }
1095
1096 static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1097 {
1098   gchar *line;
1099   static int cnt = 0;
1100
1101   gdk_threads_enter ();
1102   g_mutex_lock(sess->mutex);
1103   if (!sess->ok) {
1104     g_mutex_unlock(sess->mutex);
1105     gps_session_delete(sess);
1106     gdk_threads_leave();
1107     g_thread_exit ( NULL );
1108   }
1109   g_mutex_unlock(sess->mutex);
1110   gdk_threads_leave ();
1111
1112   switch(c) {
1113   case BABEL_DIAG_OUTPUT:
1114     line = (gchar *)data;
1115
1116     gdk_threads_enter();
1117     g_mutex_lock(sess->mutex);
1118     if (sess->ok) {
1119       gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1120     }
1121     g_mutex_unlock(sess->mutex);
1122     gdk_threads_leave();
1123
1124     process_line_for_gps_info ( line, sess );
1125
1126     if (strstr(line, "RECORD")) { 
1127       int lsb, msb;
1128
1129       if (strlen(line) > 20) {
1130         sscanf(line+17, "%x", &lsb); 
1131         sscanf(line+20, "%x", &msb);
1132         cnt = lsb + msb * 256;
1133         /* set_total_count(cnt, sess); */
1134         sess->count = 0;
1135       }
1136     }
1137     if ( strstr(line, "WPTDAT")) {
1138       if (sess->count == 0) {
1139         sess->progress_label = sess->wp_label;
1140         sess->progress_type = WPT;
1141         set_total_count(cnt, sess);
1142       }
1143       sess->count++;
1144       set_current_count(sess->count, sess);
1145     }
1146     if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1147        if (sess->count == 0) {
1148          sess->progress_label = sess->rte_label;
1149          sess->progress_type = RTE;
1150          // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints
1151          // Anyway since we're uploading - we should know how many points we're going to put!
1152          cnt = (cnt / 2) + 1;
1153          set_total_count(cnt, sess);
1154        }
1155        sess->count++;
1156        set_current_count(sess->count, sess);
1157     }
1158     if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
1159       if (sess->count == 0) {
1160         sess->progress_label = sess->trk_label;
1161         sess->progress_type = TRK;
1162         set_total_count(cnt, sess);
1163       }
1164       sess->count++;
1165       set_current_count(sess->count, sess);
1166     }
1167     break;
1168   case BABEL_DONE:
1169     break;
1170   default:
1171     break;
1172   }
1173
1174 }
1175
1176 static void gps_comm_thread(GpsSession *sess)
1177 {
1178   gboolean result;
1179
1180   if (sess->direction == GPS_DOWN)
1181     result = a_babel_convert_from (sess->vtl, sess->cmd_args, sess->port,
1182         (BabelStatusFunc) gps_download_progress_func, sess, NULL);
1183   else {
1184     result = a_babel_convert_to (sess->vtl, sess->track, sess->cmd_args, sess->port,
1185         (BabelStatusFunc) gps_upload_progress_func, sess);
1186   }
1187
1188   if (!result) {
1189     gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: couldn't find gpsbabel.") );
1190   } 
1191   else {
1192     g_mutex_lock(sess->mutex);
1193     if (sess->ok) {
1194       gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
1195       gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1196       gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
1197
1198       /* Do not change the view if we are following the current GPS position */
1199 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1200       if (!sess->realtime_tracking)
1201 #endif
1202       {
1203         if ( sess->vvp && sess->direction == GPS_DOWN ) {
1204           /* View the data available */
1205           vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp) ;
1206           vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update from background thread
1207         }
1208       }
1209     } else {
1210       /* canceled */
1211     }
1212     g_mutex_unlock(sess->mutex);
1213   }
1214
1215   g_mutex_lock(sess->mutex);
1216   if (sess->ok) {
1217     sess->ok = FALSE;
1218     g_mutex_unlock(sess->mutex);
1219   }
1220   else {
1221     g_mutex_unlock(sess->mutex);
1222     gps_session_delete(sess);
1223   }
1224   g_thread_exit(NULL);
1225 }
1226
1227 /**
1228  * vik_gps_comm:
1229  * @vtl: The TrackWaypoint layer to operate on
1230  * @track: Operate on a particular track when specified
1231  * @dir: The direction of the transfer
1232  * @protocol: The GPS device communication protocol
1233  * @port: The GPS serial port
1234  * @tracking: If tracking then viewport display update will be skipped
1235  * @vvp: A viewport is required as the display may get updated
1236  * @vlp: A layers panel is needed for uploading as the items maybe modified
1237  * @do_tracks: Whether tracks shoud be processed
1238  * @do_waypoints: Whether waypoints shoud be processed
1239  * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
1240  *
1241  * Talk to a GPS Device using a thread which updates a dialog with the progress
1242  */
1243 gint vik_gps_comm ( VikTrwLayer *vtl,
1244                     VikTrack *track,
1245                     vik_gps_dir dir,
1246                     gchar *protocol,
1247                     gchar *port,
1248                     gboolean tracking,
1249                     VikViewport *vvp,
1250                     VikLayersPanel *vlp,
1251                     gboolean do_tracks,
1252                     gboolean do_routes,
1253                     gboolean do_waypoints,
1254                     gboolean turn_off )
1255 {
1256   GpsSession *sess = g_malloc(sizeof(GpsSession));
1257   char *tracks = NULL;
1258   char *routes = NULL;
1259   char *waypoints = NULL;
1260
1261   sess->mutex = vik_mutex_new();
1262   sess->direction = dir;
1263   sess->vtl = vtl;
1264   sess->track = track;
1265   sess->port = g_strdup(port);
1266   sess->ok = TRUE;
1267   sess->window_title = (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload");
1268   sess->vvp = vvp;
1269
1270   // This must be done inside the main thread as the uniquify causes screen updates
1271   //  (originally performed this nearer the point of upload in the thread)
1272   if ( dir == GPS_UP ) {
1273     // Enforce unique names in the layer upload to the GPS device
1274     // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1275     // Thus this maintains the older code in built restriction
1276     if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1277       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1278                                   _("Warning - GPS Upload items may overwrite each other") );
1279   }
1280
1281 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1282   sess->realtime_tracking = tracking;
1283 #endif
1284
1285   if (do_tracks)
1286     tracks = "-t";
1287   else
1288     tracks = "";
1289   if (do_routes)
1290     routes = "-r";
1291   else
1292     routes = "";
1293   if (do_waypoints)
1294     waypoints = "-w";
1295   else
1296     waypoints = "";
1297
1298   sess->cmd_args = g_strdup_printf("-D 9 %s %s %s -%c %s",
1299                                    tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
1300   tracks = NULL;
1301   waypoints = NULL;
1302
1303   // Only create dialog if we're going to do some transferring
1304   if ( do_tracks || do_waypoints || do_routes ) {
1305     sess->dialog = gtk_dialog_new_with_buttons ( "", VIK_GTK_WINDOW_FROM_LAYER(vtl), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL );
1306     gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1307                                         GTK_RESPONSE_ACCEPT, FALSE );
1308     gtk_window_set_title ( GTK_WINDOW(sess->dialog), sess->window_title );
1309
1310     sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
1311     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
1312     gtk_widget_show_all(sess->status_label);
1313
1314     sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1315     sess->ver_label = gtk_label_new ("");
1316     sess->id_label = gtk_label_new ("");
1317     sess->wp_label = gtk_label_new ("");
1318     sess->trk_label = gtk_label_new ("");
1319     sess->rte_label = gtk_label_new ("");
1320
1321     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1322     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wp_label, FALSE, FALSE, 5 );
1323     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1324     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
1325
1326     gtk_widget_show_all(sess->dialog);
1327
1328     sess->progress_label = sess->wp_label;
1329     sess->total_count = -1;
1330
1331     // Starting gps read/write thread
1332 #if GLIB_CHECK_VERSION (2, 32, 0)
1333     g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1334 #else
1335     g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1336 #endif
1337
1338     gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1339     gtk_dialog_run(GTK_DIALOG(sess->dialog));
1340
1341     gtk_widget_destroy(sess->dialog);
1342   }
1343   else {
1344     if ( !turn_off )
1345       a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1346   }
1347
1348   g_mutex_lock(sess->mutex);
1349
1350   if (sess->ok) {
1351     sess->ok = FALSE;   /* tell thread to stop */
1352     g_mutex_unlock(sess->mutex);
1353   }
1354   else {
1355     if ( turn_off ) {
1356       // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1357       gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
1358       gboolean result = a_babel_convert_from (NULL, (const char*)device_off, (const char*)port, NULL, NULL, NULL);
1359       if ( !result )
1360         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1361       g_free ( device_off );
1362     }
1363     g_mutex_unlock(sess->mutex);
1364     gps_session_delete(sess);
1365   }
1366
1367   return 0;
1368 }
1369
1370 static void gps_upload_cb( gpointer layer_and_vlp[2] )
1371 {
1372   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1373   VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
1374   VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
1375   VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1376   VikViewport *vvp = vik_window_viewport(vw);
1377   vik_gps_comm(vtl, NULL, GPS_UP, vgl->protocol, vgl->serial_port, FALSE, vvp, vlp, vgl->upload_tracks, vgl->upload_routes, vgl->upload_waypoints, FALSE);
1378 }
1379
1380 static void gps_download_cb( gpointer layer_and_vlp[2] )
1381 {
1382   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1383   VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
1384   VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1385   VikViewport *vvp = vik_window_viewport(vw);
1386 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1387   vik_gps_comm(vtl, NULL, GPS_DOWN, vgl->protocol, vgl->serial_port, vgl->realtime_tracking, vvp, NULL, vgl->download_tracks, vgl->download_routes, vgl->download_waypoints, FALSE);
1388 #else
1389   vik_gps_comm(vtl, NULL, GPS_DOWN, vgl->protocol, vgl->serial_port, FALSE, vvp, NULL, vgl->download_tracks, vgl->download_routes, vgl->download_waypoints, FALSE);
1390 #endif
1391 }
1392
1393 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1394 {
1395   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1396   // Get confirmation from the user
1397   if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1398                               _("Are you sure you want to delete GPS Upload data?"),
1399                               NULL ) )
1400     return;
1401   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1402   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1403   vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1404 }
1405
1406 static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1407 {
1408   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1409   // Get confirmation from the user
1410   if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1411                               _("Are you sure you want to delete GPS Download data?"),
1412                               NULL ) )
1413     return;
1414   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1415   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1416   vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1417 }
1418
1419 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1420 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1421 {
1422   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1423   // Get confirmation from the user
1424   if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1425                               _("Are you sure you want to delete GPS Realtime data?"),
1426                               NULL ) )
1427     return;
1428   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1429   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1430 }
1431 #endif
1432
1433 static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1434 {
1435   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1436   // Get confirmation from the user
1437   if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1438                               _("Are you sure you want to delete All GPS data?"),
1439                               NULL ) )
1440     return;
1441   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1442   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1443   vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1444   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1445   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1446   vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1447 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1448   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1449   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1450 #endif
1451 }
1452
1453 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1454 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1455 {
1456   struct LatLon ll;
1457   VikCoord nw, se;
1458   struct LatLon lnw, lse;
1459   vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1460   vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1461   vik_coord_to_latlon ( &nw, &lnw );
1462   vik_coord_to_latlon ( &se, &lse );
1463   if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1464        vgl->realtime_fix.fix.latitude < lnw.lat &&
1465        vgl->realtime_fix.fix.longitude > lnw.lon &&
1466        vgl->realtime_fix.fix.longitude < lse.lon &&
1467        !isnan (vgl->realtime_fix.fix.track) ) {
1468     VikCoord gps;
1469     gint x, y;
1470     gint half_back_x, half_back_y;
1471     gint half_back_bg_x, half_back_bg_y;
1472     gint pt_x, pt_y;
1473     gint ptbg_x;
1474     gint side1_x, side1_y, side2_x, side2_y;
1475     gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1476
1477     ll.lat = vgl->realtime_fix.fix.latitude;
1478     ll.lon = vgl->realtime_fix.fix.longitude;
1479     vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
1480     vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1481
1482     gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1483     gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
1484
1485     half_back_y = y+8*heading_cos;
1486     half_back_x = x-8*heading_sin;
1487     half_back_bg_y = y+10*heading_cos;
1488     half_back_bg_x = x-10*heading_sin;
1489
1490     pt_y = half_back_y-24*heading_cos;
1491     pt_x = half_back_x+24*heading_sin;
1492     //ptbg_y = half_back_bg_y-28*heading_cos;
1493     ptbg_x = half_back_bg_x+28*heading_sin;
1494
1495     side1_y = half_back_y+9*heading_sin;
1496     side1_x = half_back_x+9*heading_cos;
1497     side1bg_y = half_back_bg_y+11*heading_sin;
1498     side1bg_x = half_back_bg_x+11*heading_cos;
1499
1500     side2_y = half_back_y-9*heading_sin;
1501     side2_x = half_back_x-9*heading_cos;
1502     side2bg_y = half_back_bg_y-11*heading_sin;
1503     side2bg_x = half_back_bg_x-11*heading_cos;
1504
1505      GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1506      GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1507
1508      vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1509      vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
1510      vik_viewport_draw_rectangle ( vp,
1511          (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1512          TRUE, x-2, y-2, 4, 4 );
1513      //vgl->realtime_track_pt_gc = (vgl->realtime_track_pt_gc == vgl->realtime_track_pt1_gc) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc;
1514   }
1515 }
1516
1517 static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1518 {
1519     struct LatLon ll;
1520     GList *last_tp;
1521
1522     /* Note that fix.time is a double, but it should not affect the precision
1523        for most GPS */
1524     time_t cur_timestamp = vgl->realtime_fix.fix.time;
1525     time_t last_timestamp = vgl->last_fix.fix.time;
1526
1527     if (cur_timestamp < last_timestamp) {
1528       return NULL;
1529     }
1530
1531     if (vgl->realtime_record && vgl->realtime_fix.dirty) {
1532       gboolean replace = FALSE;
1533       int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1534       int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
1535       int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1536       int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1537       if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1538           (vgl->realtime_fix.fix.mode > MODE_2D) &&
1539           (vgl->last_fix.fix.mode <= MODE_2D) &&
1540           ((cur_timestamp - last_timestamp) < 2)) {
1541         g_free(last_tp->data);
1542         vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1543         replace = TRUE;
1544       }
1545       if (replace ||
1546           ((cur_timestamp != last_timestamp) &&
1547           ((forced || 
1548             ((heading < last_heading) && (heading < (last_heading - 3))) || 
1549             ((heading > last_heading) && (heading > (last_heading + 3))) ||
1550             ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
1551         /* TODO: check for new segments */
1552         VikTrackpoint *tp = vik_trackpoint_new();
1553         tp->newsegment = FALSE;
1554         tp->has_timestamp = TRUE;
1555         tp->timestamp = vgl->realtime_fix.fix.time;
1556         tp->altitude = alt;
1557         /* speed only available for 3D fix. Check for NAN when use this speed */
1558         tp->speed = vgl->realtime_fix.fix.speed;  
1559         tp->course = vgl->realtime_fix.fix.track;
1560         tp->nsats = vgl->realtime_fix.satellites_used;
1561         tp->fix_mode = vgl->realtime_fix.fix.mode;
1562
1563         ll.lat = vgl->realtime_fix.fix.latitude;
1564         ll.lon = vgl->realtime_fix.fix.longitude;
1565         vik_coord_load_from_latlon(&tp->coord,
1566              vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1567
1568         vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
1569         vgl->realtime_fix.dirty = FALSE;
1570         vgl->realtime_fix.satellites_used = 0;
1571         vgl->last_fix = vgl->realtime_fix;
1572         return tp;
1573       }
1574     }
1575     return NULL;
1576 }
1577
1578 #define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1579
1580 static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1581 {
1582   gchar *statusbar_format_code = NULL;
1583   gboolean need2free = FALSE;
1584   if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1585     // Otherwise use default
1586     statusbar_format_code = g_strdup ( "GSA" );
1587     need2free = TRUE;
1588   }
1589
1590   gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track, vgl->last_fix.fix.climb );
1591   vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1592   g_free ( msg );
1593
1594   if ( need2free )
1595     g_free ( statusbar_format_code );
1596
1597 }
1598
1599 static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1600 {
1601   gboolean update_all = FALSE;
1602   VikGpsLayer *vgl = vgpsd->vgl;
1603
1604   if (!vgl->realtime_tracking) {
1605     g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1606     return;
1607   }
1608
1609   if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1610       !isnan(vgpsd->gpsd.fix.latitude) &&
1611       !isnan(vgpsd->gpsd.fix.longitude)) {
1612
1613     VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1614     VikViewport *vvp = vik_window_viewport(vw);
1615     vgl->realtime_fix.fix = vgpsd->gpsd.fix;
1616     vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
1617     vgl->realtime_fix.dirty = TRUE;
1618
1619     struct LatLon ll;
1620     VikCoord vehicle_coord;
1621
1622     ll.lat = vgl->realtime_fix.fix.latitude;
1623     ll.lon = vgl->realtime_fix.fix.longitude;
1624     vik_coord_load_from_latlon(&vehicle_coord,
1625            vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1626
1627     if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
1628         (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
1629       vik_viewport_set_center_coord(vvp, &vehicle_coord, FALSE);
1630       update_all = TRUE;
1631     }
1632     else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
1633       const int hdiv = 6;
1634       const int vdiv = 6;
1635       const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1636       gint width = vik_viewport_get_width(vvp);
1637       gint height = vik_viewport_get_height(vvp);
1638       gint vx, vy;
1639
1640       vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
1641       update_all = TRUE;
1642       if (vx < (width/hdiv))
1643         vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1644       else if (vx > (width - width/hdiv))
1645         vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1646       else if (vy < (height/vdiv))
1647         vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1648       else if (vy > (height - height/vdiv))
1649         vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1650       else
1651         update_all = FALSE;
1652     }
1653
1654     vgl->first_realtime_trackpoint = FALSE;
1655
1656     vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1657
1658     if ( vgl->trkpt ) {
1659       if ( vgl->realtime_update_statusbar )
1660         update_statusbar ( vgl, vw );
1661       vgl->trkpt_prev = vgl->trkpt;
1662     }
1663
1664     vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
1665   }
1666 }
1667
1668 static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1669 {
1670   VikGpsLayer *vgl = data;
1671   if (condition == G_IO_IN) {
1672 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1673     if (!gps_poll(&vgl->vgpsd->gpsd)) {
1674 #elif GPSD_API_MAJOR_VERSION == 5
1675     if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1676       // Reuse old function to perform operations on the new GPS data
1677       gpsd_raw_hook(vgl->vgpsd, NULL);
1678 #else
1679       // Broken compile
1680 #endif
1681       return TRUE;
1682     }
1683     else {
1684       g_warning("Disconnected from gpsd. Trying to reconnect");
1685       rt_gpsd_disconnect(vgl);
1686       rt_gpsd_connect(vgl, FALSE);
1687     }
1688   }
1689   return FALSE; /* no further calling */
1690 }
1691
1692 static gchar *make_track_name(VikTrwLayer *vtl)
1693 {
1694   const gchar basename[] = "REALTIME";
1695   const gint bufsize = sizeof(basename) + 5;
1696   gchar *name = g_malloc(bufsize);
1697   strcpy(name, basename);
1698   gint i = 2;
1699
1700   while (vik_trw_layer_get_track(vtl, name) != NULL) {
1701     g_snprintf(name, bufsize, "%s#%d", basename, i);
1702     i++;
1703   }
1704   return(name);
1705
1706 }
1707
1708 static gboolean rt_gpsd_try_connect(gpointer *data)
1709 {
1710   VikGpsLayer *vgl = (VikGpsLayer *)data;
1711 #if GPSD_API_MAJOR_VERSION == 3
1712   struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1713
1714   if (gpsd == NULL) {
1715 #elif GPSD_API_MAJOR_VERSION == 4
1716   vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1717
1718   if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
1719 #elif GPSD_API_MAJOR_VERSION == 5
1720   vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1721   if (gps_open(vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd) != 0) {
1722 #else
1723 // Delibrately break compilation...
1724 #endif
1725     g_warning("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1726                      vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
1727     return TRUE;   /* keep timer running */
1728   }
1729
1730 #if GPSD_API_MAJOR_VERSION == 3
1731   vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1732 #endif
1733   vgl->vgpsd->vgl = vgl;
1734
1735   vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1736   /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1737   vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1738   vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1739
1740   if (vgl->realtime_record) {
1741     VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1742     vgl->realtime_track = vik_track_new();
1743     vgl->realtime_track->visible = TRUE;
1744     vik_trw_layer_add_track(vtl, make_track_name(vtl), vgl->realtime_track);
1745   }
1746
1747 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1748   gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1749 #endif
1750
1751   vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1752   vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1753                     G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
1754
1755 #if GPSD_API_MAJOR_VERSION == 3
1756   gps_query(&vgl->vgpsd->gpsd, "w+x");
1757 #endif
1758 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
1759   gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL);
1760 #endif
1761
1762   return FALSE;  /* no longer called by timeout */
1763 }
1764
1765 static gboolean rt_ask_retry(VikGpsLayer *vgl)
1766 {
1767   GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER(vgl),
1768                           GTK_DIALOG_DESTROY_WITH_PARENT,
1769                           GTK_MESSAGE_QUESTION,
1770                           GTK_BUTTONS_YES_NO,
1771                           "Failed to connect to gpsd at %s (port %s)\n"
1772                           "Should Viking keep trying (every %d seconds)?",
1773                           vgl->gpsd_host, vgl->gpsd_port,
1774                           vgl->gpsd_retry_interval);
1775
1776   gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1777   gtk_widget_destroy(dialog);
1778   return (res == GTK_RESPONSE_YES);
1779 }
1780
1781 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1782 {
1783   vgl->realtime_retry_timer = 0;
1784   if (rt_gpsd_try_connect((gpointer *)vgl)) {
1785     if (vgl->gpsd_retry_interval <= 0) {
1786       g_warning("Failed to connect to gpsd but will not retry because retry intervel was set to %d (which is 0 or negative)", vgl->gpsd_retry_interval);
1787       return FALSE;
1788     }
1789     else if (ask_if_failed && !rt_ask_retry(vgl))
1790       return FALSE;
1791     else
1792       vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1793         (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1794   }
1795   return TRUE;
1796 }
1797
1798 static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1799 {
1800   if (vgl->realtime_retry_timer) {
1801     g_source_remove(vgl->realtime_retry_timer);
1802     vgl->realtime_retry_timer = 0;
1803   }
1804   if (vgl->realtime_io_watch_id) {
1805     g_source_remove(vgl->realtime_io_watch_id);
1806     vgl->realtime_io_watch_id = 0;
1807   }
1808   if (vgl->realtime_io_channel) {
1809     GError *error = NULL;
1810     g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
1811     vgl->realtime_io_channel = NULL;
1812   }
1813   if (vgl->vgpsd) {
1814 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
1815     gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
1816 #endif
1817     gps_close(&vgl->vgpsd->gpsd);
1818 #if GPSD_API_MAJOR_VERSION == 3
1819     free(vgl->vgpsd);
1820 #elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
1821     g_free(vgl->vgpsd);
1822 #endif
1823     vgl->vgpsd = NULL;
1824   }
1825
1826   if (vgl->realtime_record && vgl->realtime_track) {
1827     if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
1828       vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
1829     vgl->realtime_track = NULL;
1830   }
1831 }
1832
1833 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
1834 {
1835   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1836   vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
1837
1838   /* Make sure we are still in the boat with libgps */
1839   g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
1840
1841   if (vgl->realtime_tracking) {
1842     vgl->first_realtime_trackpoint = TRUE;
1843     if (!rt_gpsd_connect(vgl, TRUE)) {
1844       vgl->first_realtime_trackpoint = FALSE;
1845       vgl->realtime_tracking = FALSE;
1846       vgl->trkpt = NULL;
1847     }
1848   }
1849   else {  /* stop realtime tracking */
1850     vgl->first_realtime_trackpoint = FALSE;
1851     vgl->trkpt = NULL;
1852     rt_gpsd_disconnect(vgl);
1853   }
1854 }
1855 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */