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