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