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