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