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