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