]> git.street.me.uk Git - andy/viking.git/blame - src/vikgpslayer.c
Enable a clear icon on entry boxes used for search like input.
[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;
17acdaec 106 gchar *babelargs;
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)
1a90b108 377 gchar **new_protocols = g_malloc_n(1 + g_list_length(a_babel_device_list), sizeof(gpointer));
5f955595
RN
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);
17acdaec 881 g_free(sess->babelargs);
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;
086a7eef 901 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt);
0d2b891f 902 sess->total_count = mycnt;
086a7eef 903 break;
0d2b891f
RN
904 }
905 }
97634600 906 }
0d2b891f 907 else
97634600 908 {
0d2b891f
RN
909 switch (sess->progress_type) {
910 case WPT: tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", cnt); break;
911 case TRK: tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", cnt); break;
912 default: tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", cnt); break;
913 }
97634600
GB
914 }
915
916 g_snprintf(s, 128, tmp_str, cnt);
b364d6bc
QT
917 gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
918 gtk_widget_show ( sess->progress_label );
919 sess->total_count = cnt;
920 }
921 g_mutex_unlock(sess->mutex);
922 gdk_threads_leave();
923}
924
925static void set_current_count(gint cnt, GpsSession *sess)
926{
927 gchar s[128];
97634600 928 const gchar *tmp_str;
b364d6bc
QT
929
930 gdk_threads_enter();
931 g_mutex_lock(sess->mutex);
932 if (sess->ok) {
933 if (cnt < sess->total_count) {
97634600
GB
934 if (sess->direction == GPS_DOWN)
935 {
0d2b891f
RN
936 switch (sess->progress_type) {
937 case WPT: tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->total_count); break;
938 case TRK: tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->total_count); break;
939 default: tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->total_count); break;
940 }
97634600
GB
941 }
942 else {
0d2b891f
RN
943 switch (sess->progress_type) {
944 case WPT: tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->total_count); break;
945 case TRK: tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->total_count); break;
946 default: tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->total_count); break;
947 }
97634600
GB
948 }
949 g_snprintf(s, 128, tmp_str, cnt, sess->total_count);
b364d6bc 950 } else {
97634600
GB
951 if (sess->direction == GPS_DOWN)
952 {
0d2b891f
RN
953 switch (sess->progress_type) {
954 case WPT: tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", cnt); break;
955 case TRK: tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", cnt); break;
956 default: tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", cnt); break;
957 }
97634600
GB
958 }
959 else {
0d2b891f
RN
960 switch (sess->progress_type) {
961 case WPT: tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", cnt); break;
962 case TRK: tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", cnt); break;
963 default: tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", cnt); break;
964 }
97634600
GB
965 }
966 g_snprintf(s, 128, tmp_str, cnt);
b364d6bc
QT
967 }
968 gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
969 }
970 g_mutex_unlock(sess->mutex);
971 gdk_threads_leave();
972}
973
974static void set_gps_info(const gchar *info, GpsSession *sess)
975{
976 gchar s[256];
977 gdk_threads_enter();
978 g_mutex_lock(sess->mutex);
979 if (sess->ok) {
97634600 980 g_snprintf(s, 256, _("GPS Device: %s"), info);
b364d6bc
QT
981 gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
982 }
983 g_mutex_unlock(sess->mutex);
984 gdk_threads_leave();
985}
986
c011c859
RN
987/*
988 * Common processing for GPS Device information
989 * It doesn't matter whether we're uploading or downloading
990 */
991static void process_line_for_gps_info ( const gchar *line, GpsSession *sess )
992{
993 if (strstr(line, "PRDDAT")) {
994 gchar **tokens = g_strsplit(line, " ", 0);
995 gchar info[128];
996 int ilen = 0;
997 int i;
998 int n_tokens = 0;
999
1000 while (tokens[n_tokens])
1001 n_tokens++;
1002
1003 // I'm not entirely clear what information this is trying to get...
1004 // Obviously trying to decipher some kind of text/naming scheme
1005 // Anyway this will be superceded if there is 'Unit:' information
1006 if (n_tokens > 8) {
1007 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
1008 guint ch;
1009 sscanf(tokens[i], "%x", &ch);
1010 info[ilen++] = ch;
1011 }
1012 info[ilen++] = 0;
1013 set_gps_info(info, sess);
1014 }
1015 g_strfreev(tokens);
1016 }
1017
1018 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
1019 if (strstr(line, "Unit:")) {
1020 gchar **tokens = g_strsplit(line, "\t", 0);
1021 int n_tokens = 0;
1022 while (tokens[n_tokens])
1023 n_tokens++;
1024
1025 if (n_tokens > 1) {
1026 set_gps_info(tokens[1], sess);
1027 }
1028 g_strfreev(tokens);
1029 }
1030}
1031
b364d6bc
QT
1032static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1033{
1034 gchar *line;
1035
1036 gdk_threads_enter ();
1037 g_mutex_lock(sess->mutex);
1038 if (!sess->ok) {
1039 g_mutex_unlock(sess->mutex);
1040 gps_session_delete(sess);
1041 gdk_threads_leave();
1042 g_thread_exit ( NULL );
1043 }
1044 g_mutex_unlock(sess->mutex);
1045 gdk_threads_leave ();
1046
1047 switch(c) {
1048 case BABEL_DIAG_OUTPUT:
1049 line = (gchar *)data;
1050
8d0586c6
RN
1051 gdk_threads_enter();
1052 g_mutex_lock(sess->mutex);
1053 if (sess->ok) {
1054 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1055 }
1056 g_mutex_unlock(sess->mutex);
1057 gdk_threads_leave();
1058
0d2b891f
RN
1059 /* tells us the type of items that will follow */
1060 if (strstr(line, "Xfer Wpt")) {
b364d6bc 1061 sess->progress_label = sess->wp_label;
0d2b891f 1062 sess->progress_type = WPT;
b364d6bc 1063 }
0d2b891f 1064 if (strstr(line, "Xfer Trk")) {
b364d6bc 1065 sess->progress_label = sess->trk_label;
0d2b891f 1066 sess->progress_type = TRK;
b364d6bc 1067 }
0d2b891f
RN
1068 if (strstr(line, "Xfer Rte")) {
1069 sess->progress_label = sess->rte_label;
1070 sess->progress_type = RTE;
1071 }
1072
c011c859
RN
1073 process_line_for_gps_info ( line, sess );
1074
0d2b891f 1075 if (strstr(line, "RECORD")) {
b364d6bc
QT
1076 int lsb, msb, cnt;
1077
c83b5ad9
QT
1078 if (strlen(line) > 20) {
1079 sscanf(line+17, "%x", &lsb);
1080 sscanf(line+20, "%x", &msb);
1081 cnt = lsb + msb * 256;
1082 set_total_count(cnt, sess);
1083 sess->count = 0;
1084 }
b364d6bc 1085 }
0d2b891f 1086 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
b364d6bc
QT
1087 sess->count++;
1088 set_current_count(sess->count, sess);
1089 }
1090 break;
1091 case BABEL_DONE:
1092 break;
1093 default:
1094 break;
1095 }
1096
1097}
1098
1099static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1100{
1101 gchar *line;
1102 static int cnt = 0;
1103
1104 gdk_threads_enter ();
1105 g_mutex_lock(sess->mutex);
1106 if (!sess->ok) {
1107 g_mutex_unlock(sess->mutex);
1108 gps_session_delete(sess);
1109 gdk_threads_leave();
1110 g_thread_exit ( NULL );
1111 }
1112 g_mutex_unlock(sess->mutex);
1113 gdk_threads_leave ();
1114
1115 switch(c) {
1116 case BABEL_DIAG_OUTPUT:
1117 line = (gchar *)data;
1118
8d0586c6
RN
1119 gdk_threads_enter();
1120 g_mutex_lock(sess->mutex);
1121 if (sess->ok) {
1122 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1123 }
1124 g_mutex_unlock(sess->mutex);
1125 gdk_threads_leave();
1126
c011c859
RN
1127 process_line_for_gps_info ( line, sess );
1128
b364d6bc
QT
1129 if (strstr(line, "RECORD")) {
1130 int lsb, msb;
1131
c83b5ad9
QT
1132 if (strlen(line) > 20) {
1133 sscanf(line+17, "%x", &lsb);
1134 sscanf(line+20, "%x", &msb);
1135 cnt = lsb + msb * 256;
1136 /* set_total_count(cnt, sess); */
1137 sess->count = 0;
1138 }
b364d6bc
QT
1139 }
1140 if ( strstr(line, "WPTDAT")) {
1141 if (sess->count == 0) {
1142 sess->progress_label = sess->wp_label;
0d2b891f 1143 sess->progress_type = WPT;
b364d6bc
QT
1144 set_total_count(cnt, sess);
1145 }
1146 sess->count++;
1147 set_current_count(sess->count, sess);
0d2b891f
RN
1148 }
1149 if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1150 if (sess->count == 0) {
1151 sess->progress_label = sess->rte_label;
1152 sess->progress_type = RTE;
1153 // 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
1154 // Anyway since we're uploading - we should know how many points we're going to put!
1155 cnt = (cnt / 2) + 1;
1156 set_total_count(cnt, sess);
1157 }
1158 sess->count++;
1159 set_current_count(sess->count, sess);
b364d6bc
QT
1160 }
1161 if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
1162 if (sess->count == 0) {
1163 sess->progress_label = sess->trk_label;
0d2b891f 1164 sess->progress_type = TRK;
b364d6bc
QT
1165 set_total_count(cnt, sess);
1166 }
1167 sess->count++;
1168 set_current_count(sess->count, sess);
1169 }
1170 break;
1171 case BABEL_DONE:
1172 break;
1173 default:
1174 break;
1175 }
1176
1177}
1178
1179static void gps_comm_thread(GpsSession *sess)
1180{
1181 gboolean result;
1182
17acdaec
RN
1183 if (sess->direction == GPS_DOWN) {
1184 ProcessOptions po = { sess->babelargs, sess->port, NULL, NULL, NULL, NULL };
1185 result = a_babel_convert_from (sess->vtl, &po, (BabelStatusFunc) gps_download_progress_func, sess, NULL);
1186 }
10aa2b84 1187 else {
17acdaec 1188 result = a_babel_convert_to (sess->vtl, sess->track, sess->babelargs, sess->port,
9181183a 1189 (BabelStatusFunc) gps_upload_progress_func, sess);
10aa2b84 1190 }
b364d6bc 1191
b364d6bc 1192 if (!result) {
4c77d5e0 1193 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: couldn't find gpsbabel.") );
b364d6bc
QT
1194 }
1195 else {
1196 g_mutex_lock(sess->mutex);
1197 if (sess->ok) {
4c77d5e0 1198 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
b364d6bc
QT
1199 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1200 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
b870a512
RN
1201
1202 /* Do not change the view if we are following the current GPS position */
728db5b5 1203#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b870a512
RN
1204 if (!sess->realtime_tracking)
1205#endif
1206 {
990404e8
RN
1207 if ( sess->vvp && sess->direction == GPS_DOWN ) {
1208 vik_layer_post_read ( VIK_LAYER(sess->vtl), sess->vvp, TRUE );
1209 /* View the data available */
1210 vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp ) ;
1211 vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update from background thread
1212 }
b870a512 1213 }
b364d6bc
QT
1214 } else {
1215 /* canceled */
1216 }
1217 g_mutex_unlock(sess->mutex);
1218 }
1219
1220 g_mutex_lock(sess->mutex);
1221 if (sess->ok) {
1222 sess->ok = FALSE;
1223 g_mutex_unlock(sess->mutex);
1224 }
1225 else {
1226 g_mutex_unlock(sess->mutex);
1227 gps_session_delete(sess);
1228 }
b364d6bc
QT
1229 g_thread_exit(NULL);
1230}
1231
e4b02b41
RN
1232/**
1233 * vik_gps_comm:
eebf8102
RN
1234 * @vtl: The TrackWaypoint layer to operate on
1235 * @track: Operate on a particular track when specified
1236 * @dir: The direction of the transfer
1237 * @protocol: The GPS device communication protocol
1238 * @port: The GPS serial port
1239 * @tracking: If tracking then viewport display update will be skipped
1240 * @vvp: A viewport is required as the display may get updated
1241 * @vlp: A layers panel is needed for uploading as the items maybe modified
1242 * @do_tracks: Whether tracks shoud be processed
1243 * @do_waypoints: Whether waypoints shoud be processed
1244 * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
e4b02b41
RN
1245 *
1246 * Talk to a GPS Device using a thread which updates a dialog with the progress
1247 */
1248gint vik_gps_comm ( VikTrwLayer *vtl,
778f41c5 1249 VikTrack *track,
e4b02b41
RN
1250 vik_gps_dir dir,
1251 gchar *protocol,
1252 gchar *port,
1253 gboolean tracking,
1254 VikViewport *vvp,
1255 VikLayersPanel *vlp,
1256 gboolean do_tracks,
0d2b891f 1257 gboolean do_routes,
eebf8102
RN
1258 gboolean do_waypoints,
1259 gboolean turn_off )
e4b02b41 1260{
b364d6bc 1261 GpsSession *sess = g_malloc(sizeof(GpsSession));
d6c58ab9 1262 char *tracks = NULL;
0d2b891f 1263 char *routes = NULL;
d6c58ab9 1264 char *waypoints = NULL;
b364d6bc 1265
fc6640a9 1266 sess->mutex = vik_mutex_new();
b364d6bc
QT
1267 sess->direction = dir;
1268 sess->vtl = vtl;
778f41c5 1269 sess->track = track;
b364d6bc
QT
1270 sess->port = g_strdup(port);
1271 sess->ok = TRUE;
4c77d5e0 1272 sess->window_title = (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload");
b870a512 1273 sess->vvp = vvp;
bc950037
RN
1274
1275 // This must be done inside the main thread as the uniquify causes screen updates
1276 // (originally performed this nearer the point of upload in the thread)
1277 if ( dir == GPS_UP ) {
1278 // Enforce unique names in the layer upload to the GPS device
1279 // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1280 // Thus this maintains the older code in built restriction
1281 if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1282 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1283 _("Warning - GPS Upload items may overwrite each other") );
1284 }
1285
728db5b5 1286#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b870a512
RN
1287 sess->realtime_tracking = tracking;
1288#endif
d6c58ab9
RN
1289
1290 if (do_tracks)
1291 tracks = "-t";
1292 else
1293 tracks = "";
0d2b891f
RN
1294 if (do_routes)
1295 routes = "-r";
1296 else
1297 routes = "";
d6c58ab9
RN
1298 if (do_waypoints)
1299 waypoints = "-w";
1300 else
1301 waypoints = "";
1302
17acdaec 1303 sess->babelargs = g_strdup_printf("-D 9 %s %s %s -%c %s",
0d2b891f 1304 tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
d6c58ab9
RN
1305 tracks = NULL;
1306 waypoints = NULL;
1307
e5f73681 1308 // Only create dialog if we're going to do some transferring
0d2b891f 1309 if ( do_tracks || do_waypoints || do_routes ) {
e5f73681
RN
1310 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 );
1311 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1312 GTK_RESPONSE_ACCEPT, FALSE );
1313 gtk_window_set_title ( GTK_WINDOW(sess->dialog), sess->window_title );
b364d6bc 1314
e5f73681 1315 sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
9b082b39 1316 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
e5f73681 1317 gtk_widget_show_all(sess->status_label);
b364d6bc 1318
e5f73681
RN
1319 sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1320 sess->ver_label = gtk_label_new ("");
1321 sess->id_label = gtk_label_new ("");
1322 sess->wp_label = gtk_label_new ("");
1323 sess->trk_label = gtk_label_new ("");
0d2b891f 1324 sess->rte_label = gtk_label_new ("");
b364d6bc 1325
9b082b39
RN
1326 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1327 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wp_label, FALSE, FALSE, 5 );
1328 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1329 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
b364d6bc 1330
e5f73681 1331 gtk_widget_show_all(sess->dialog);
b364d6bc 1332
e5f73681
RN
1333 sess->progress_label = sess->wp_label;
1334 sess->total_count = -1;
b364d6bc 1335
e5f73681 1336 // Starting gps read/write thread
b832ea69
RN
1337#if GLIB_CHECK_VERSION (2, 32, 0)
1338 g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1339#else
1340 g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1341#endif
b364d6bc 1342
e5f73681
RN
1343 gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1344 gtk_dialog_run(GTK_DIALOG(sess->dialog));
b364d6bc 1345
e5f73681
RN
1346 gtk_widget_destroy(sess->dialog);
1347 }
1348 else {
1349 if ( !turn_off )
1350 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1351 }
b364d6bc
QT
1352
1353 g_mutex_lock(sess->mutex);
e5f73681 1354
b364d6bc
QT
1355 if (sess->ok) {
1356 sess->ok = FALSE; /* tell thread to stop */
1357 g_mutex_unlock(sess->mutex);
1358 }
1359 else {
eebf8102
RN
1360 if ( turn_off ) {
1361 // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1362 gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
17acdaec
RN
1363 ProcessOptions po = { device_off, port, NULL, NULL, NULL, NULL };
1364 gboolean result = a_babel_convert_from (NULL, &po, NULL, NULL, NULL);
eebf8102
RN
1365 if ( !result )
1366 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1367 g_free ( device_off );
1368 }
b364d6bc
QT
1369 g_mutex_unlock(sess->mutex);
1370 gps_session_delete(sess);
1371 }
1372
b364d6bc
QT
1373 return 0;
1374}
1375
1376static void gps_upload_cb( gpointer layer_and_vlp[2] )
1377{
1378 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
10aa2b84 1379 VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
b364d6bc 1380 VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
2eb89de4
RN
1381 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1382 VikViewport *vvp = vik_window_viewport(vw);
0d2b891f 1383 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
1384}
1385
1386static void gps_download_cb( gpointer layer_and_vlp[2] )
1387{
1388 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1389 VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
b870a512
RN
1390 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1391 VikViewport *vvp = vik_window_viewport(vw);
728db5b5 1392#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
0d2b891f 1393 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 1394#else
0d2b891f 1395 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 1396#endif
b364d6bc 1397}
700b0908
QT
1398
1399static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1400{
1401 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
a6a4825c
RN
1402 // Get confirmation from the user
1403 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1404 _("Are you sure you want to delete GPS Upload data?"),
1405 NULL ) )
1406 return;
700b0908
QT
1407 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1408 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
0d2b891f 1409 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
700b0908
QT
1410}
1411
1412static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1413{
1414 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
a6a4825c
RN
1415 // Get confirmation from the user
1416 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1417 _("Are you sure you want to delete GPS Download data?"),
1418 NULL ) )
1419 return;
700b0908
QT
1420 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1421 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
0d2b891f 1422 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
700b0908
QT
1423}
1424
728db5b5 1425#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
22d1e285
RN
1426static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1427{
1428 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
a6a4825c
RN
1429 // Get confirmation from the user
1430 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1431 _("Are you sure you want to delete GPS Realtime data?"),
1432 NULL ) )
1433 return;
22d1e285
RN
1434 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1435 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1436}
1437#endif
1438
700b0908
QT
1439static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1440{
1441 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
a6a4825c
RN
1442 // Get confirmation from the user
1443 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1444 _("Are you sure you want to delete All GPS data?"),
1445 NULL ) )
1446 return;
700b0908
QT
1447 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1448 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
0d2b891f 1449 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
700b0908
QT
1450 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1451 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
0d2b891f 1452 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
728db5b5 1453#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
dfef116b
RN
1454 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1455 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1456#endif
700b0908 1457}
b87d3952 1458
728db5b5 1459#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b87d3952
QT
1460static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1461{
c4e61875 1462 struct LatLon ll;
b87d3952
QT
1463 VikCoord nw, se;
1464 struct LatLon lnw, lse;
1465 vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1466 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1467 vik_coord_to_latlon ( &nw, &lnw );
1468 vik_coord_to_latlon ( &se, &lse );
c4e61875
QT
1469 if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1470 vgl->realtime_fix.fix.latitude < lnw.lat &&
1471 vgl->realtime_fix.fix.longitude > lnw.lon &&
7888d76f
RN
1472 vgl->realtime_fix.fix.longitude < lse.lon &&
1473 !isnan (vgl->realtime_fix.fix.track) ) {
b87d3952
QT
1474 VikCoord gps;
1475 gint x, y;
1476 gint half_back_x, half_back_y;
1477 gint half_back_bg_x, half_back_bg_y;
1478 gint pt_x, pt_y;
8b15d148 1479 gint ptbg_x;
b87d3952
QT
1480 gint side1_x, side1_y, side2_x, side2_y;
1481 gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1482
c4e61875
QT
1483 ll.lat = vgl->realtime_fix.fix.latitude;
1484 ll.lon = vgl->realtime_fix.fix.longitude;
1485 vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
b87d3952
QT
1486 vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1487
0da53bd9
GB
1488 gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1489 gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
b87d3952
QT
1490
1491 half_back_y = y+8*heading_cos;
1492 half_back_x = x-8*heading_sin;
1493 half_back_bg_y = y+10*heading_cos;
1494 half_back_bg_x = x-10*heading_sin;
1495
1496 pt_y = half_back_y-24*heading_cos;
1497 pt_x = half_back_x+24*heading_sin;
8b15d148 1498 //ptbg_y = half_back_bg_y-28*heading_cos;
b87d3952
QT
1499 ptbg_x = half_back_bg_x+28*heading_sin;
1500
1501 side1_y = half_back_y+9*heading_sin;
1502 side1_x = half_back_x+9*heading_cos;
1503 side1bg_y = half_back_bg_y+11*heading_sin;
1504 side1bg_x = half_back_bg_x+11*heading_cos;
1505
1506 side2_y = half_back_y-9*heading_sin;
1507 side2_x = half_back_x-9*heading_cos;
1508 side2bg_y = half_back_bg_y-11*heading_sin;
1509 side2bg_x = half_back_bg_x-11*heading_cos;
1510
1511 GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1512 GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1513
1514 vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1515 vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
c4e61875
QT
1516 vik_viewport_draw_rectangle ( vp,
1517 (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1518 TRUE, x-2, y-2, 4, 4 );
1519 //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
1520 }
1521}
1522
8b5838b2 1523static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
b87d3952 1524{
c4e61875
QT
1525 struct LatLon ll;
1526 GList *last_tp;
97cab2d5
QT
1527
1528 /* Note that fix.time is a double, but it should not affect the precision
1529 for most GPS */
1530 time_t cur_timestamp = vgl->realtime_fix.fix.time;
1531 time_t last_timestamp = vgl->last_fix.fix.time;
1532
1533 if (cur_timestamp < last_timestamp) {
8b5838b2 1534 return NULL;
97cab2d5
QT
1535 }
1536
b87d3952 1537 if (vgl->realtime_record && vgl->realtime_fix.dirty) {
c4e61875 1538 gboolean replace = FALSE;
7888d76f
RN
1539 int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1540 int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
c4e61875
QT
1541 int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1542 int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1543 if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1544 (vgl->realtime_fix.fix.mode > MODE_2D) &&
1545 (vgl->last_fix.fix.mode <= MODE_2D) &&
97cab2d5 1546 ((cur_timestamp - last_timestamp) < 2)) {
c4e61875
QT
1547 g_free(last_tp->data);
1548 vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1549 replace = TRUE;
1550 }
97cab2d5
QT
1551 if (replace ||
1552 ((cur_timestamp != last_timestamp) &&
1553 ((forced ||
1554 ((heading < last_heading) && (heading < (last_heading - 3))) ||
1555 ((heading > last_heading) && (heading > (last_heading + 3))) ||
1556 ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
b87d3952
QT
1557 /* TODO: check for new segments */
1558 VikTrackpoint *tp = vik_trackpoint_new();
1559 tp->newsegment = FALSE;
b87d3952 1560 tp->has_timestamp = TRUE;
c4e61875 1561 tp->timestamp = vgl->realtime_fix.fix.time;
a2817d3c
QT
1562 tp->altitude = alt;
1563 /* speed only available for 3D fix. Check for NAN when use this speed */
1564 tp->speed = vgl->realtime_fix.fix.speed;
1565 tp->course = vgl->realtime_fix.fix.track;
1566 tp->nsats = vgl->realtime_fix.satellites_used;
1567 tp->fix_mode = vgl->realtime_fix.fix.mode;
c4e61875
QT
1568
1569 ll.lat = vgl->realtime_fix.fix.latitude;
1570 ll.lon = vgl->realtime_fix.fix.longitude;
b87d3952 1571 vik_coord_load_from_latlon(&tp->coord,
c4e61875 1572 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
b87d3952 1573
9bc95d58 1574 vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
b87d3952 1575 vgl->realtime_fix.dirty = FALSE;
a2817d3c 1576 vgl->realtime_fix.satellites_used = 0;
b87d3952 1577 vgl->last_fix = vgl->realtime_fix;
8b5838b2 1578 return tp;
b87d3952
QT
1579 }
1580 }
8b5838b2
RN
1581 return NULL;
1582}
1583
1584#define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1585
1586static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1587{
1588 gchar *statusbar_format_code = NULL;
1589 gboolean need2free = FALSE;
1590 if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1591 // Otherwise use default
1592 statusbar_format_code = g_strdup ( "GSA" );
1593 need2free = TRUE;
1594 }
1595
fee4e142 1596 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track, vgl->last_fix.fix.climb );
8b5838b2
RN
1597 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1598 g_free ( msg );
1599
1600 if ( need2free )
1601 g_free ( statusbar_format_code );
b87d3952
QT
1602
1603}
1604
c5bc70c3 1605static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
b87d3952 1606{
c4e61875 1607 gboolean update_all = FALSE;
b87d3952
QT
1608 VikGpsLayer *vgl = vgpsd->vgl;
1609
c4e61875
QT
1610 if (!vgl->realtime_tracking) {
1611 g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
b87d3952 1612 return;
b87d3952
QT
1613 }
1614
b87d3952
QT
1615 if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1616 !isnan(vgpsd->gpsd.fix.latitude) &&
7888d76f 1617 !isnan(vgpsd->gpsd.fix.longitude)) {
dc3a1898
QT
1618
1619 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1620 VikViewport *vvp = vik_window_viewport(vw);
c4e61875 1621 vgl->realtime_fix.fix = vgpsd->gpsd.fix;
a2817d3c 1622 vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
b87d3952
QT
1623 vgl->realtime_fix.dirty = TRUE;
1624
dc3a1898
QT
1625 struct LatLon ll;
1626 VikCoord vehicle_coord;
c4e61875 1627
dc3a1898
QT
1628 ll.lat = vgl->realtime_fix.fix.latitude;
1629 ll.lon = vgl->realtime_fix.fix.longitude;
1630 vik_coord_load_from_latlon(&vehicle_coord,
1631 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1632
1633 if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
c5bc70c3 1634 (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
be5554c5 1635 vik_viewport_set_center_coord(vvp, &vehicle_coord, FALSE);
dc3a1898
QT
1636 update_all = TRUE;
1637 }
a00e1e36 1638 else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
dc3a1898
QT
1639 const int hdiv = 6;
1640 const int vdiv = 6;
1641 const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1642 gint width = vik_viewport_get_width(vvp);
1643 gint height = vik_viewport_get_height(vvp);
1644 gint vx, vy;
1645
1646 vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
c4e61875 1647 update_all = TRUE;
dc3a1898
QT
1648 if (vx < (width/hdiv))
1649 vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1650 else if (vx > (width - width/hdiv))
1651 vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1652 else if (vy < (height/vdiv))
1653 vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1654 else if (vy > (height - height/vdiv))
1655 vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1656 else
1657 update_all = FALSE;
c4e61875
QT
1658 }
1659
c5bc70c3 1660 vgl->first_realtime_trackpoint = FALSE;
8b5838b2
RN
1661
1662 vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1663
1664 if ( vgl->trkpt ) {
1665 if ( vgl->realtime_update_statusbar )
1666 update_statusbar ( vgl, vw );
1667 vgl->trkpt_prev = vgl->trkpt;
1668 }
b87d3952 1669
da121f9b 1670 vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
b87d3952
QT
1671 }
1672}
1673
1674static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1675{
b87d3952 1676 VikGpsLayer *vgl = data;
c5bc70c3 1677 if (condition == G_IO_IN) {
bff27ab1
RN
1678#if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1679 if (!gps_poll(&vgl->vgpsd->gpsd)) {
25043ca2 1680#elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
bff27ab1
RN
1681 if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1682 // Reuse old function to perform operations on the new GPS data
1683 gpsd_raw_hook(vgl->vgpsd, NULL);
1684#else
1685 // Broken compile
1686#endif
c5bc70c3 1687 return TRUE;
bff27ab1 1688 }
c5bc70c3 1689 else {
24953b45 1690 g_warning("Disconnected from gpsd. Trying to reconnect");
c5bc70c3
QT
1691 rt_gpsd_disconnect(vgl);
1692 rt_gpsd_connect(vgl, FALSE);
1693 }
1694 }
1695 return FALSE; /* no further calling */
b87d3952
QT
1696}
1697
1698static gchar *make_track_name(VikTrwLayer *vtl)
1699{
1700 const gchar basename[] = "REALTIME";
1701 const gint bufsize = sizeof(basename) + 5;
1702 gchar *name = g_malloc(bufsize);
1703 strcpy(name, basename);
1704 gint i = 2;
1705
1706 while (vik_trw_layer_get_track(vtl, name) != NULL) {
1707 g_snprintf(name, bufsize, "%s#%d", basename, i);
1708 i++;
1709 }
1710 return(name);
1711
1712}
1713
c5bc70c3
QT
1714static gboolean rt_gpsd_try_connect(gpointer *data)
1715{
1716 VikGpsLayer *vgl = (VikGpsLayer *)data;
728db5b5 1717#if GPSD_API_MAJOR_VERSION == 3
c5bc70c3
QT
1718 struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1719
1720 if (gpsd == NULL) {
728db5b5 1721#elif GPSD_API_MAJOR_VERSION == 4
a263d172
GB
1722 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1723
1724 if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
25043ca2 1725#elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
bff27ab1
RN
1726 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1727 if (gps_open(vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd) != 0) {
728db5b5 1728#else
bff27ab1 1729// Delibrately break compilation...
a263d172 1730#endif
24953b45
QT
1731 g_warning("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1732 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
c5bc70c3
QT
1733 return TRUE; /* keep timer running */
1734 }
1735
728db5b5 1736#if GPSD_API_MAJOR_VERSION == 3
a263d172
GB
1737 vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1738#endif
c5bc70c3
QT
1739 vgl->vgpsd->vgl = vgl;
1740
1741 vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1742 /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1743 vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1744 vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1745
1746 if (vgl->realtime_record) {
1747 VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1748 vgl->realtime_track = vik_track_new();
1749 vgl->realtime_track->visible = TRUE;
ce4bd1cf 1750 vik_trw_layer_add_track(vtl, make_track_name(vtl), vgl->realtime_track);
c5bc70c3
QT
1751 }
1752
bff27ab1 1753#if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
c5bc70c3 1754 gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
bff27ab1
RN
1755#endif
1756
c5bc70c3
QT
1757 vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1758 vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1759 G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
728db5b5
RN
1760
1761#if GPSD_API_MAJOR_VERSION == 3
bec818c4
GB
1762 gps_query(&vgl->vgpsd->gpsd, "w+x");
1763#endif
25043ca2 1764#if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
728db5b5
RN
1765 gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL);
1766#endif
1767
c5bc70c3
QT
1768 return FALSE; /* no longer called by timeout */
1769}
1770
1771static gboolean rt_ask_retry(VikGpsLayer *vgl)
1772{
1773 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER(vgl),
1774 GTK_DIALOG_DESTROY_WITH_PARENT,
1775 GTK_MESSAGE_QUESTION,
1776 GTK_BUTTONS_YES_NO,
1777 "Failed to connect to gpsd at %s (port %s)\n"
1778 "Should Viking keep trying (every %d seconds)?",
1779 vgl->gpsd_host, vgl->gpsd_port,
1780 vgl->gpsd_retry_interval);
1781
1782 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1783 gtk_widget_destroy(dialog);
1784 return (res == GTK_RESPONSE_YES);
1785}
1786
1787static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1788{
1789 vgl->realtime_retry_timer = 0;
1790 if (rt_gpsd_try_connect((gpointer *)vgl)) {
1791 if (vgl->gpsd_retry_interval <= 0) {
1792 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);
1793 return FALSE;
1794 }
1795 else if (ask_if_failed && !rt_ask_retry(vgl))
1796 return FALSE;
1797 else
1798 vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1799 (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1800 }
1801 return TRUE;
1802}
1803
1804static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1805{
c5bc70c3
QT
1806 if (vgl->realtime_retry_timer) {
1807 g_source_remove(vgl->realtime_retry_timer);
1808 vgl->realtime_retry_timer = 0;
1809 }
bff27ab1
RN
1810 if (vgl->realtime_io_watch_id) {
1811 g_source_remove(vgl->realtime_io_watch_id);
1812 vgl->realtime_io_watch_id = 0;
1813 }
c5bc70c3
QT
1814 if (vgl->realtime_io_channel) {
1815 GError *error = NULL;
1816 g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
1817 vgl->realtime_io_channel = NULL;
1818 }
1819 if (vgl->vgpsd) {
25043ca2 1820#if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
bff27ab1
RN
1821 gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
1822#endif
c5bc70c3 1823 gps_close(&vgl->vgpsd->gpsd);
728db5b5 1824#if GPSD_API_MAJOR_VERSION == 3
a263d172 1825 free(vgl->vgpsd);
25043ca2 1826#elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
728db5b5 1827 g_free(vgl->vgpsd);
a263d172 1828#endif
c5bc70c3
QT
1829 vgl->vgpsd = NULL;
1830 }
1831
1832 if (vgl->realtime_record && vgl->realtime_track) {
c5bc70c3 1833 if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
ce4bd1cf 1834 vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
c5bc70c3
QT
1835 vgl->realtime_track = NULL;
1836 }
1837}
1838
b87d3952
QT
1839static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
1840{
1841 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1842 vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
1843
a2817d3c
QT
1844 /* Make sure we are still in the boat with libgps */
1845 g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
1846
b87d3952 1847 if (vgl->realtime_tracking) {
c5bc70c3
QT
1848 vgl->first_realtime_trackpoint = TRUE;
1849 if (!rt_gpsd_connect(vgl, TRUE)) {
1850 vgl->first_realtime_trackpoint = FALSE;
b87d3952 1851 vgl->realtime_tracking = FALSE;
8b5838b2 1852 vgl->trkpt = NULL;
b87d3952 1853 }
b87d3952
QT
1854 }
1855 else { /* stop realtime tracking */
c5bc70c3 1856 vgl->first_realtime_trackpoint = FALSE;
8b5838b2 1857 vgl->trkpt = NULL;
c5bc70c3 1858 rt_gpsd_disconnect(vgl);
b87d3952
QT
1859 }
1860}
001a86db 1861#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */