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