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