]> git.street.me.uk Git - andy/viking.git/blame - src/vikgpslayer.c
[QA] Minor function rename for consistency
[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
20c7a3a0
QT
264 (VikLayerFuncSetMenuItemsSelection) NULL,
265 (VikLayerFuncGetMenuItemsSelection) NULL,
266
b364d6bc
QT
267 (VikLayerFuncAddMenuItems) gps_layer_add_menu_items,
268 (VikLayerFuncSublayerAddMenuItems) NULL,
269
270 (VikLayerFuncSublayerRenameRequest) NULL,
271 (VikLayerFuncSublayerToggleVisible) NULL,
9da7faf2 272 (VikLayerFuncSublayerTooltip) NULL,
45da1f2f 273 (VikLayerFuncLayerTooltip) gps_layer_tooltip,
a5dcfdb7 274 (VikLayerFuncLayerSelected) NULL,
b364d6bc 275
b364d6bc
QT
276 (VikLayerFuncMarshall) gps_layer_marshall,
277 (VikLayerFuncUnmarshall) gps_layer_unmarshall,
278
279 (VikLayerFuncSetParam) gps_layer_set_param,
280 (VikLayerFuncGetParam) gps_layer_get_param,
db43cfa4 281 (VikLayerFuncChangeParam) NULL,
b364d6bc
QT
282
283 (VikLayerFuncReadFileData) NULL,
284 (VikLayerFuncWriteFileData) NULL,
285
286 (VikLayerFuncDeleteItem) NULL,
d5874ef9 287 (VikLayerFuncCutItem) NULL,
b364d6bc
QT
288 (VikLayerFuncCopyItem) NULL,
289 (VikLayerFuncPasteItem) NULL,
290 (VikLayerFuncFreeCopiedItem) NULL,
b7b6b047 291 (VikLayerFuncDragDropRequest) NULL,
77ad64fa
RN
292
293 (VikLayerFuncSelectClick) NULL,
08f14055
RN
294 (VikLayerFuncSelectMove) NULL,
295 (VikLayerFuncSelectRelease) NULL,
e46f259a 296 (VikLayerFuncSelectedViewportMenu) NULL,
b364d6bc
QT
297};
298
58a642b6 299enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
728db5b5 300#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
58a642b6
GB
301 TRW_REALTIME,
302#endif
303 NUM_TRW};
304static gchar * trw_names[] = {
4c77d5e0 305 N_("GPS Download"), N_("GPS Upload"),
728db5b5 306#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
4c77d5e0 307 N_("GPS Realtime Tracking"),
58a642b6
GB
308#endif
309};
b87d3952 310
728db5b5 311#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b87d3952
QT
312typedef struct {
313 struct gps_data_t gpsd;
314 VikGpsLayer *vgl;
315} VglGpsd;
316
317typedef struct {
c4e61875 318 struct gps_fix_t fix;
a2817d3c 319 gint satellites_used;
b87d3952
QT
320 gboolean dirty; /* needs to be saved */
321} GpsFix;
001a86db 322#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
b87d3952 323
b364d6bc
QT
324struct _VikGpsLayer {
325 VikLayer vl;
326 VikTrwLayer * trw_children[NUM_TRW];
f253a6a6
QT
327 GList * children; /* used only for writing file */
328 int cur_read_child; /* used only for reading file */
728db5b5 329#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b87d3952 330 VglGpsd *vgpsd;
ae198aa3 331 gboolean realtime_tracking; /* set/reset only by the callback */
c5bc70c3 332 gboolean first_realtime_trackpoint;
b87d3952
QT
333 GpsFix realtime_fix;
334 GpsFix last_fix;
335
b87d3952 336 VikTrack *realtime_track;
b87d3952
QT
337
338 GIOChannel *realtime_io_channel;
339 guint realtime_io_watch_id;
c5bc70c3 340 guint realtime_retry_timer;
b87d3952
QT
341 GdkGC *realtime_track_gc;
342 GdkGC *realtime_track_bg_gc;
343 GdkGC *realtime_track_pt_gc;
344 GdkGC *realtime_track_pt1_gc;
345 GdkGC *realtime_track_pt2_gc;
346
b364d6bc 347 /* params */
b87d3952
QT
348 gchar *gpsd_host;
349 gchar *gpsd_port;
c5bc70c3 350 gint gpsd_retry_interval;
b87d3952
QT
351 gboolean realtime_record;
352 gboolean realtime_jump_to_start;
dc3a1898 353 guint vehicle_position;
8b5838b2
RN
354 gboolean realtime_update_statusbar;
355 VikTrackpoint *trkpt;
356 VikTrackpoint *trkpt_prev;
001a86db 357#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
5f955595 358 gchar *protocol;
ce37ab9b 359 gchar *serial_port;
d6c58ab9 360 gboolean download_tracks;
0d2b891f 361 gboolean download_routes;
d6c58ab9
RN
362 gboolean download_waypoints;
363 gboolean upload_tracks;
0d2b891f 364 gboolean upload_routes;
d6c58ab9 365 gboolean upload_waypoints;
b364d6bc
QT
366};
367
5f955595
RN
368/**
369 * Overwrite the static setup with dynamically generated GPS Babel device list
370 */
371static void gps_layer_inst_init ( VikGpsLayer *self )
372{
373 gint new_proto = 0;
374 // +1 for luck (i.e the NULL terminator)
375 gchar **new_protocols = g_malloc(1 + g_list_length(a_babel_device_list)*sizeof(gpointer));
376
377 GList *gl = g_list_first ( a_babel_device_list );
378 while ( gl ) {
379 // should be using label property but use name for now
380 // thus don't need to mess around converting label to name later on
381 new_protocols[new_proto++] = ((BabelDevice*)gl->data)->name;
382 gl = g_list_next ( gl );
383 }
384 new_protocols[new_proto] = NULL;
385
a7023a1b 386 vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
5f955595
RN
387}
388
b364d6bc
QT
389GType vik_gps_layer_get_type ()
390{
391 static GType val_type = 0;
392
393 if (!val_type)
394 {
395 static const GTypeInfo val_info =
396 {
397 sizeof (VikGpsLayerClass),
398 NULL, /* base_init */
399 NULL, /* base_finalize */
400 NULL, /* class init */
401 NULL, /* class_finalize */
402 NULL, /* class_data */
403 sizeof (VikGpsLayer),
404 0,
5f955595 405 (GInstanceInitFunc) gps_layer_inst_init,
b364d6bc
QT
406 };
407 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
408 }
409
410 return val_type;
411}
412
fdca5edb 413static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
b364d6bc 414{
c4e61875
QT
415 int i;
416
b87d3952 417 VikGpsLayer *rv = vik_gps_layer_new (vp);
b364d6bc 418 vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
c4e61875
QT
419
420 for (i = 0; i < NUM_TRW; i++) {
0ab35525 421 rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, FALSE ));
c4e61875
QT
422 vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
423 }
b364d6bc
QT
424 return rv;
425}
426
45da1f2f
RN
427static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
428{
5f955595 429 return vgl->protocol;
45da1f2f
RN
430}
431
b364d6bc
QT
432/* "Copy" */
433static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
434{
435 VikLayer *child_layer;
436 guint8 *ld;
437 gint ll;
438 GByteArray* b = g_byte_array_new ();
439 gint len;
440 gint i;
441
442#define alm_append(obj, sz) \
443 len = (sz); \
444 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
445 g_byte_array_append ( b, (guint8 *)(obj), len );
446
447 vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
448 alm_append(ld, ll);
449 g_free(ld);
450
451 for (i = 0; i < NUM_TRW; i++) {
452 child_layer = VIK_LAYER(vgl->trw_children[i]);
453 vik_layer_marshall(child_layer, &ld, &ll);
454 if (ld) {
455 alm_append(ld, ll);
456 g_free(ld);
457 }
458 }
459 *data = b->data;
460 *datalen = b->len;
461 g_byte_array_free(b, FALSE);
462#undef alm_append
463}
464
465/* "Paste" */
466static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
467{
468#define alm_size (*(gint *)data)
469#define alm_next \
470 len -= sizeof(gint) + alm_size; \
471 data += sizeof(gint) + alm_size;
472
30a9c13f 473 VikGpsLayer *rv = vik_gps_layer_new(vvp);
b364d6bc
QT
474 VikLayer *child_layer;
475 gint i;
476
477 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
478 alm_next;
479
480 i = 0;
481 while (len>0 && i < NUM_TRW) {
482 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
483 if (child_layer) {
fdca5edb 484 rv->trw_children[i++] = (VikTrwLayer *)child_layer;
325af20b
RN
485 // NB no need to attach signal update handler here
486 // as this will always be performed later on in vik_gps_layer_realize()
b364d6bc
QT
487 }
488 alm_next;
489 }
490 // g_print("gps_layer_unmarshall ended with len=%d\n", len);
491 g_assert(len == 0);
492 return rv;
493#undef alm_size
494#undef alm_next
495}
496
158b3642 497static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
b364d6bc
QT
498{
499 switch ( id )
500 {
501 case PARAM_PROTOCOL:
5f955595
RN
502 if (data.s) {
503 g_free(vgl->protocol);
504 // Backwards Compatibility: previous versions <v1.4 stored protocol as an array index
505 int index = data.s[0] - '0';
506 if (data.s[0] != '\0' &&
507 g_ascii_isdigit (data.s[0]) &&
508 data.s[1] == '\0' &&
509 index < OLD_NUM_PROTOCOLS)
510 // It is a single digit: activate compatibility
511 vgl->protocol = g_strdup(protocols_args[index]);
512 else
513 vgl->protocol = g_strdup(data.s);
514 g_debug("%s: %s", __FUNCTION__, vgl->protocol);
515 }
b364d6bc 516 else
4c77d5e0 517 g_warning(_("Unknown GPS Protocol"));
b364d6bc
QT
518 break;
519 case PARAM_PORT:
5f955595 520 if (data.s) {
ce37ab9b 521 g_free(vgl->serial_port);
5f955595 522 // Backwards Compatibility: previous versions <v0.9.91 stored serial_port as an array index
bb8aed99
GB
523 int index = data.s[0] - '0';
524 if (data.s[0] != '\0' &&
525 g_ascii_isdigit (data.s[0]) &&
526 data.s[1] == '\0' &&
527 index < OLD_NUM_PORTS)
528 /* It is a single digit: activate compatibility */
529 vgl->serial_port = g_strdup(old_params_ports[index]);
530 else
531 vgl->serial_port = g_strdup(data.s);
5f955595
RN
532 g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
533 }
b364d6bc 534 else
4c77d5e0 535 g_warning(_("Unknown serial port device"));
b364d6bc 536 break;
d6c58ab9
RN
537 case PARAM_DOWNLOAD_TRACKS:
538 vgl->download_tracks = data.b;
539 break;
540 case PARAM_UPLOAD_TRACKS:
541 vgl->upload_tracks = data.b;
542 break;
0d2b891f
RN
543 case PARAM_DOWNLOAD_ROUTES:
544 vgl->download_routes = data.b;
545 break;
546 case PARAM_UPLOAD_ROUTES:
547 vgl->upload_routes = data.b;
548 break;
d6c58ab9
RN
549 case PARAM_DOWNLOAD_WAYPOINTS:
550 vgl->download_waypoints = data.b;
551 break;
552 case PARAM_UPLOAD_WAYPOINTS:
553 vgl->upload_waypoints = data.b;
554 break;
728db5b5 555#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b87d3952 556 case PARAM_GPSD_HOST:
a7023a1b
RN
557 if (data.s) {
558 if (vgl->gpsd_host)
559 g_free(vgl->gpsd_host);
560 vgl->gpsd_host = g_strdup(data.s);
561 }
b87d3952
QT
562 break;
563 case PARAM_GPSD_PORT:
a7023a1b 564 if (data.s) {
fe6ce054
RN
565 if (vgl->gpsd_port)
566 g_free(vgl->gpsd_port);
a7023a1b
RN
567 vgl->gpsd_port = g_strdup(data.s);
568 }
b87d3952 569 break;
c5bc70c3
QT
570 case PARAM_GPSD_RETRY_INTERVAL:
571 vgl->gpsd_retry_interval = strtol(data.s, NULL, 10);
572 break;
b87d3952
QT
573 case PARAM_REALTIME_REC:
574 vgl->realtime_record = data.b;
575 break;
576 case PARAM_REALTIME_CENTER_START:
577 vgl->realtime_jump_to_start = data.b;
578 break;
dc3a1898
QT
579 case PARAM_VEHICLE_POSITION:
580 vgl->vehicle_position = data.u;
b87d3952 581 break;
8b5838b2
RN
582 case PARAM_REALTIME_UPDATE_STATUSBAR:
583 vgl->realtime_update_statusbar = data.b;
584 break;
001a86db 585#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
b87d3952
QT
586 default:
587 g_warning("gps_layer_set_param(): unknown parameter");
b364d6bc
QT
588 }
589
590 return TRUE;
591}
592
158b3642 593static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
b364d6bc
QT
594{
595 VikLayerParamData rv;
596 switch ( id )
597 {
598 case PARAM_PROTOCOL:
5f955595
RN
599 rv.s = vgl->protocol;
600 g_debug("%s: %s", __FUNCTION__, rv.s);
b364d6bc
QT
601 break;
602 case PARAM_PORT:
ce37ab9b
GB
603 rv.s = vgl->serial_port;
604 g_debug("%s: %s", __FUNCTION__, rv.s);
b364d6bc 605 break;
d6c58ab9
RN
606 case PARAM_DOWNLOAD_TRACKS:
607 rv.b = vgl->download_tracks;
608 break;
609 case PARAM_UPLOAD_TRACKS:
610 rv.b = vgl->upload_tracks;
611 break;
0d2b891f
RN
612 case PARAM_DOWNLOAD_ROUTES:
613 rv.b = vgl->download_routes;
614 break;
615 case PARAM_UPLOAD_ROUTES:
616 rv.b = vgl->upload_routes;
617 break;
d6c58ab9
RN
618 case PARAM_DOWNLOAD_WAYPOINTS:
619 rv.b = vgl->download_waypoints;
620 break;
621 case PARAM_UPLOAD_WAYPOINTS:
622 rv.b = vgl->upload_waypoints;
623 break;
728db5b5 624#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b87d3952
QT
625 case PARAM_GPSD_HOST:
626 rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
627 break;
628 case PARAM_GPSD_PORT:
629 rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
630 break;
c5bc70c3
QT
631 case PARAM_GPSD_RETRY_INTERVAL:
632 rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
633 break;
b87d3952
QT
634 case PARAM_REALTIME_REC:
635 rv.b = vgl->realtime_record;
636 break;
637 case PARAM_REALTIME_CENTER_START:
638 rv.b = vgl->realtime_jump_to_start;
639 break;
dc3a1898
QT
640 case PARAM_VEHICLE_POSITION:
641 rv.u = vgl->vehicle_position;
b87d3952 642 break;
8b5838b2
RN
643 case PARAM_REALTIME_UPDATE_STATUSBAR:
644 rv.u = vgl->realtime_update_statusbar;
645 break;
001a86db 646#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
b364d6bc 647 default:
4c77d5e0 648 g_warning(_("%s: unknown parameter"), __FUNCTION__);
b364d6bc
QT
649 }
650
b364d6bc
QT
651 return rv;
652}
653
b87d3952 654VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
b364d6bc
QT
655{
656 gint i;
657 VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
a0c65899 658 vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GPS );
b364d6bc
QT
659 for (i = 0; i < NUM_TRW; i++) {
660 vgl->trw_children[i] = NULL;
661 }
7886de28 662 vgl->children = NULL;
f253a6a6 663 vgl->cur_read_child = 0;
b364d6bc 664
728db5b5 665#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
c5bc70c3 666 vgl->first_realtime_trackpoint = FALSE;
8b5838b2
RN
667 vgl->trkpt = NULL;
668 vgl->trkpt_prev = NULL;
b87d3952 669 vgl->vgpsd = NULL;
c5bc70c3
QT
670 vgl->realtime_io_channel = NULL;
671 vgl->realtime_io_watch_id = 0;
672 vgl->realtime_retry_timer = 0;
8aff54f2
RN
673 if ( vp ) {
674 vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
675 vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
676 vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
677 vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
678 vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
679 }
b87d3952 680 vgl->realtime_track = NULL;
a7023a1b 681#endif // VIK_CONFIG_REALTIME_GPS_TRACKING
b87d3952 682
a7023a1b 683 vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
99163c34 684
b364d6bc
QT
685 return vgl;
686}
687
45c5ac8e 688static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
b364d6bc
QT
689{
690 gint i;
b87d3952 691 VikLayer *vl;
45c5ac8e 692 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
b364d6bc
QT
693
694 for (i = 0; i < NUM_TRW; i++) {
b87d3952
QT
695 vl = VIK_LAYER(vgl->trw_children[i]);
696 if (vl == trigger) {
45c5ac8e
RN
697 if ( vik_viewport_get_half_drawn ( vp ) ) {
698 vik_viewport_set_half_drawn ( vp, FALSE );
699 vik_viewport_snapshot_load( vp );
b87d3952 700 } else {
45c5ac8e 701 vik_viewport_snapshot_save( vp );
b87d3952
QT
702 }
703 }
45c5ac8e
RN
704 if (!vik_viewport_get_half_drawn(vp))
705 vik_layer_draw ( vl, vp );
b87d3952 706 }
728db5b5 707#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b87d3952
QT
708 if (vgl->realtime_tracking) {
709 if (VIK_LAYER(vgl) == trigger) {
45c5ac8e
RN
710 if ( vik_viewport_get_half_drawn ( vp ) ) {
711 vik_viewport_set_half_drawn ( vp, FALSE );
712 vik_viewport_snapshot_load( vp );
b87d3952 713 } else {
45c5ac8e 714 vik_viewport_snapshot_save( vp );
b87d3952
QT
715 }
716 }
45c5ac8e
RN
717 if (!vik_viewport_get_half_drawn(vp))
718 realtime_tracking_draw(vgl, vp);
b364d6bc 719 }
001a86db 720#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
b364d6bc
QT
721}
722
723static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
724{
725 gint i;
726 for (i = 0; i < NUM_TRW; i++) {
2cebc318 727 vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
b364d6bc
QT
728 }
729}
730
731static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
732{
733 static gpointer pass_along[2];
734 GtkWidget *item;
735 pass_along[0] = vgl;
736 pass_along[1] = vlp;
737
738 item = gtk_menu_item_new();
739 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
740 gtk_widget_show ( item );
741
d6de71f9
RN
742 /* Now with icons */
743 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS") );
744 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
b364d6bc
QT
745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
746 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
747 gtk_widget_show ( item );
748
d6de71f9
RN
749 item = gtk_image_menu_item_new_with_mnemonic ( _("Download from _GPS") );
750 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
b364d6bc
QT
751 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
752 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
753 gtk_widget_show ( item );
754
728db5b5 755#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
d6de71f9
RN
756 item = gtk_image_menu_item_new_with_mnemonic ( vgl->realtime_tracking ?
757 "_Stop Realtime Tracking" :
758 "_Start Realtime Tracking" );
759 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, vgl->realtime_tracking ?
760 gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_MENU) :
761 gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU) );
b87d3952
QT
762 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
763 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
764 gtk_widget_show ( item );
765
700b0908
QT
766 item = gtk_menu_item_new();
767 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
768 gtk_widget_show ( item );
22d1e285 769
d6de71f9
RN
770 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _Realtime") );
771 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
22d1e285
RN
772 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_realtime_cb), pass_along );
773 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
774 gtk_widget_show ( item );
001a86db 775#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
700b0908 776
d6de71f9
RN
777 item = gtk_image_menu_item_new_with_mnemonic ( _("E_mpty Upload") );
778 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
700b0908
QT
779 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
780 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
781 gtk_widget_show ( item );
782
d6de71f9
RN
783 item = gtk_image_menu_item_new_with_mnemonic ( _("_Empty Download") );
784 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
700b0908
QT
785 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_cb), pass_along );
786 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
787 gtk_widget_show ( item );
788
d6de71f9
RN
789 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _All") );
790 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
700b0908
QT
791 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
792 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
793 gtk_widget_show ( item );
794
b364d6bc
QT
795}
796
797static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
798{
325af20b 799 guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, vgl);
30a9c13f 800 if ( number_handlers != 1 ) {
325af20b 801 g_critical(_("Unexpected number of disconnected handlers: %d"), number_handlers);
30a9c13f 802 }
b364d6bc
QT
803}
804
fdca5edb 805static void vik_gps_layer_free ( VikGpsLayer *vgl )
b364d6bc 806{
b364d6bc
QT
807 gint i;
808 for (i = 0; i < NUM_TRW; i++) {
e03074ba 809 if (vgl->vl.realized)
fdca5edb 810 disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
b364d6bc
QT
811 g_object_unref(vgl->trw_children[i]);
812 }
728db5b5 813#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
e92cb4e5 814 rt_gpsd_disconnect(vgl);
b87d3952
QT
815 if (vgl->realtime_track_gc != NULL)
816 g_object_unref(vgl->realtime_track_gc);
817 if (vgl->realtime_track_bg_gc != NULL)
818 g_object_unref(vgl->realtime_track_bg_gc);
819 if (vgl->realtime_track_pt1_gc != NULL)
820 g_object_unref(vgl->realtime_track_pt1_gc);
821 if (vgl->realtime_track_pt2_gc != NULL)
822 g_object_unref(vgl->realtime_track_pt2_gc);
001a86db 823#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
b364d6bc
QT
824}
825
fdca5edb 826static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
b364d6bc
QT
827{
828 GtkTreeIter iter;
829 int ix;
830
5f955595
RN
831 // TODO set to garmin by default
832 //if (a_babel_device_list)
833 // device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
834 // Need to access uibuild widgets somehow....
835
b364d6bc 836 for (ix = 0; ix < NUM_TRW; ix++) {
fdca5edb 837 VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
b364d6bc 838 vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
c3a02429 839 _(trw_names[ix]), vgl, TRUE,
b364d6bc
QT
840 trw, trw->type, trw->type );
841 if ( ! trw->visible )
842 vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
843 vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
b87d3952 844 g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
b364d6bc
QT
845 }
846}
847
7886de28
QT
848const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
849{
850 int i;
851
852 if (vgl->children == NULL) {
853 for (i = NUM_TRW - 1; i >= 0; i--)
854 vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
855 }
856 return vgl->children;
857}
858
f253a6a6
QT
859VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
860{
861 g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
862
863 VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
864 if (++(vgl->cur_read_child) >= NUM_TRW)
865 vgl->cur_read_child = 0;
866 return(vtl);
867}
868
7886de28
QT
869gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
870{
871 if ( vgl->trw_children[0] )
872 return FALSE;
873 return TRUE;
874}
875
b364d6bc
QT
876static void gps_session_delete(GpsSession *sess)
877{
878 /* TODO */
879 g_mutex_free(sess->mutex);
880 g_free(sess->cmd_args);
881
882 g_free(sess);
883
884}
885
886static void set_total_count(gint cnt, GpsSession *sess)
887{
888 gchar s[128];
889 gdk_threads_enter();
890 g_mutex_lock(sess->mutex);
891 if (sess->ok) {
97634600
GB
892 const gchar *tmp_str;
893 if (sess->direction == GPS_DOWN)
894 {
0d2b891f
RN
895 switch (sess->progress_type) {
896 case WPT: tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); sess->total_count = cnt; break;
897 case TRK: tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); sess->total_count = cnt; break;
898 default:
899 {
900 // 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
901 gint mycnt = (cnt / 2) + 1;
902 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt); break;
903 sess->total_count = mycnt;
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
1183 if (sess->direction == GPS_DOWN)
9181183a 1184 result = a_babel_convert_from (sess->vtl, sess->cmd_args, sess->port,
ed691ed1 1185 (BabelStatusFunc) gps_download_progress_func, sess, NULL);
10aa2b84 1186 else {
778f41c5 1187 result = a_babel_convert_to (sess->vtl, sess->track, sess->cmd_args, sess->port,
9181183a 1188 (BabelStatusFunc) gps_upload_progress_func, sess);
10aa2b84 1189 }
b364d6bc 1190
b364d6bc 1191 if (!result) {
4c77d5e0 1192 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: couldn't find gpsbabel.") );
b364d6bc
QT
1193 }
1194 else {
1195 g_mutex_lock(sess->mutex);
1196 if (sess->ok) {
4c77d5e0 1197 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
b364d6bc
QT
1198 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1199 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
b870a512
RN
1200
1201 /* Do not change the view if we are following the current GPS position */
728db5b5 1202#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b870a512
RN
1203 if (!sess->realtime_tracking)
1204#endif
1205 {
2eb89de4 1206 if ( sess->vvp && sess->direction == GPS_DOWN ) {
b870a512
RN
1207 /* View the data available */
1208 vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp) ;
da121f9b 1209 vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update from background thread
b870a512
RN
1210 }
1211 }
b364d6bc
QT
1212 } else {
1213 /* canceled */
1214 }
1215 g_mutex_unlock(sess->mutex);
1216 }
1217
1218 g_mutex_lock(sess->mutex);
1219 if (sess->ok) {
1220 sess->ok = FALSE;
1221 g_mutex_unlock(sess->mutex);
1222 }
1223 else {
1224 g_mutex_unlock(sess->mutex);
1225 gps_session_delete(sess);
1226 }
b364d6bc
QT
1227 g_thread_exit(NULL);
1228}
1229
e4b02b41
RN
1230/**
1231 * vik_gps_comm:
eebf8102
RN
1232 * @vtl: The TrackWaypoint layer to operate on
1233 * @track: Operate on a particular track when specified
1234 * @dir: The direction of the transfer
1235 * @protocol: The GPS device communication protocol
1236 * @port: The GPS serial port
1237 * @tracking: If tracking then viewport display update will be skipped
1238 * @vvp: A viewport is required as the display may get updated
1239 * @vlp: A layers panel is needed for uploading as the items maybe modified
1240 * @do_tracks: Whether tracks shoud be processed
1241 * @do_waypoints: Whether waypoints shoud be processed
1242 * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
e4b02b41
RN
1243 *
1244 * Talk to a GPS Device using a thread which updates a dialog with the progress
1245 */
1246gint vik_gps_comm ( VikTrwLayer *vtl,
778f41c5 1247 VikTrack *track,
e4b02b41
RN
1248 vik_gps_dir dir,
1249 gchar *protocol,
1250 gchar *port,
1251 gboolean tracking,
1252 VikViewport *vvp,
1253 VikLayersPanel *vlp,
1254 gboolean do_tracks,
0d2b891f 1255 gboolean do_routes,
eebf8102
RN
1256 gboolean do_waypoints,
1257 gboolean turn_off )
e4b02b41 1258{
b364d6bc 1259 GpsSession *sess = g_malloc(sizeof(GpsSession));
d6c58ab9 1260 char *tracks = NULL;
0d2b891f 1261 char *routes = NULL;
d6c58ab9 1262 char *waypoints = NULL;
b364d6bc
QT
1263
1264 sess->mutex = g_mutex_new();
1265 sess->direction = dir;
1266 sess->vtl = vtl;
778f41c5 1267 sess->track = track;
b364d6bc
QT
1268 sess->port = g_strdup(port);
1269 sess->ok = TRUE;
4c77d5e0 1270 sess->window_title = (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload");
b870a512 1271 sess->vvp = vvp;
bc950037
RN
1272
1273 // This must be done inside the main thread as the uniquify causes screen updates
1274 // (originally performed this nearer the point of upload in the thread)
1275 if ( dir == GPS_UP ) {
1276 // Enforce unique names in the layer upload to the GPS device
1277 // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1278 // Thus this maintains the older code in built restriction
1279 if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1280 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1281 _("Warning - GPS Upload items may overwrite each other") );
1282 }
1283
728db5b5 1284#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b870a512
RN
1285 sess->realtime_tracking = tracking;
1286#endif
d6c58ab9
RN
1287
1288 if (do_tracks)
1289 tracks = "-t";
1290 else
1291 tracks = "";
0d2b891f
RN
1292 if (do_routes)
1293 routes = "-r";
1294 else
1295 routes = "";
d6c58ab9
RN
1296 if (do_waypoints)
1297 waypoints = "-w";
1298 else
1299 waypoints = "";
1300
0d2b891f
RN
1301 sess->cmd_args = g_strdup_printf("-D 9 %s %s %s -%c %s",
1302 tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
d6c58ab9
RN
1303 tracks = NULL;
1304 waypoints = NULL;
1305
e5f73681 1306 // Only create dialog if we're going to do some transferring
0d2b891f 1307 if ( do_tracks || do_waypoints || do_routes ) {
e5f73681
RN
1308 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 );
1309 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1310 GTK_RESPONSE_ACCEPT, FALSE );
1311 gtk_window_set_title ( GTK_WINDOW(sess->dialog), sess->window_title );
b364d6bc 1312
e5f73681 1313 sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
9b082b39 1314 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
e5f73681 1315 gtk_widget_show_all(sess->status_label);
b364d6bc 1316
e5f73681
RN
1317 sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1318 sess->ver_label = gtk_label_new ("");
1319 sess->id_label = gtk_label_new ("");
1320 sess->wp_label = gtk_label_new ("");
1321 sess->trk_label = gtk_label_new ("");
0d2b891f 1322 sess->rte_label = gtk_label_new ("");
b364d6bc 1323
9b082b39
RN
1324 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1325 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wp_label, FALSE, FALSE, 5 );
1326 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1327 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
b364d6bc 1328
e5f73681 1329 gtk_widget_show_all(sess->dialog);
b364d6bc 1330
e5f73681
RN
1331 sess->progress_label = sess->wp_label;
1332 sess->total_count = -1;
b364d6bc 1333
e5f73681 1334 // Starting gps read/write thread
b832ea69
RN
1335#if GLIB_CHECK_VERSION (2, 32, 0)
1336 g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1337#else
1338 g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1339#endif
b364d6bc 1340
e5f73681
RN
1341 gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1342 gtk_dialog_run(GTK_DIALOG(sess->dialog));
b364d6bc 1343
e5f73681
RN
1344 gtk_widget_destroy(sess->dialog);
1345 }
1346 else {
1347 if ( !turn_off )
1348 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1349 }
b364d6bc
QT
1350
1351 g_mutex_lock(sess->mutex);
e5f73681 1352
b364d6bc
QT
1353 if (sess->ok) {
1354 sess->ok = FALSE; /* tell thread to stop */
1355 g_mutex_unlock(sess->mutex);
1356 }
1357 else {
eebf8102
RN
1358 if ( turn_off ) {
1359 // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1360 gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
ed691ed1 1361 gboolean result = a_babel_convert_from (NULL, (const char*)device_off, (const char*)port, NULL, NULL, NULL);
eebf8102
RN
1362 if ( !result )
1363 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1364 g_free ( device_off );
1365 }
b364d6bc
QT
1366 g_mutex_unlock(sess->mutex);
1367 gps_session_delete(sess);
1368 }
1369
b364d6bc
QT
1370 return 0;
1371}
1372
1373static void gps_upload_cb( gpointer layer_and_vlp[2] )
1374{
1375 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
10aa2b84 1376 VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
b364d6bc 1377 VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
2eb89de4
RN
1378 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1379 VikViewport *vvp = vik_window_viewport(vw);
0d2b891f 1380 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
1381}
1382
1383static void gps_download_cb( gpointer layer_and_vlp[2] )
1384{
1385 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1386 VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
b870a512
RN
1387 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1388 VikViewport *vvp = vik_window_viewport(vw);
728db5b5 1389#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
0d2b891f 1390 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 1391#else
0d2b891f 1392 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 1393#endif
b364d6bc 1394}
700b0908
QT
1395
1396static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1397{
1398 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
a6a4825c
RN
1399 // Get confirmation from the user
1400 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1401 _("Are you sure you want to delete GPS Upload data?"),
1402 NULL ) )
1403 return;
700b0908
QT
1404 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1405 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
0d2b891f 1406 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
700b0908
QT
1407}
1408
1409static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1410{
1411 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
a6a4825c
RN
1412 // Get confirmation from the user
1413 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1414 _("Are you sure you want to delete GPS Download data?"),
1415 NULL ) )
1416 return;
700b0908
QT
1417 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1418 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
0d2b891f 1419 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
700b0908
QT
1420}
1421
728db5b5 1422#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
22d1e285
RN
1423static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1424{
1425 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
a6a4825c
RN
1426 // Get confirmation from the user
1427 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1428 _("Are you sure you want to delete GPS Realtime data?"),
1429 NULL ) )
1430 return;
22d1e285
RN
1431 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1432 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1433}
1434#endif
1435
700b0908
QT
1436static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1437{
1438 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
a6a4825c
RN
1439 // Get confirmation from the user
1440 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1441 _("Are you sure you want to delete All GPS data?"),
1442 NULL ) )
1443 return;
700b0908
QT
1444 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1445 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
0d2b891f 1446 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
700b0908
QT
1447 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1448 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
0d2b891f 1449 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
728db5b5 1450#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
dfef116b
RN
1451 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1452 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1453#endif
700b0908 1454}
b87d3952 1455
728db5b5 1456#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
b87d3952
QT
1457static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1458{
c4e61875 1459 struct LatLon ll;
b87d3952
QT
1460 VikCoord nw, se;
1461 struct LatLon lnw, lse;
1462 vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1463 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1464 vik_coord_to_latlon ( &nw, &lnw );
1465 vik_coord_to_latlon ( &se, &lse );
c4e61875
QT
1466 if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1467 vgl->realtime_fix.fix.latitude < lnw.lat &&
1468 vgl->realtime_fix.fix.longitude > lnw.lon &&
7888d76f
RN
1469 vgl->realtime_fix.fix.longitude < lse.lon &&
1470 !isnan (vgl->realtime_fix.fix.track) ) {
b87d3952
QT
1471 VikCoord gps;
1472 gint x, y;
1473 gint half_back_x, half_back_y;
1474 gint half_back_bg_x, half_back_bg_y;
1475 gint pt_x, pt_y;
8b15d148 1476 gint ptbg_x;
b87d3952
QT
1477 gint side1_x, side1_y, side2_x, side2_y;
1478 gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1479
c4e61875
QT
1480 ll.lat = vgl->realtime_fix.fix.latitude;
1481 ll.lon = vgl->realtime_fix.fix.longitude;
1482 vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
b87d3952
QT
1483 vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1484
0da53bd9
GB
1485 gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1486 gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
b87d3952
QT
1487
1488 half_back_y = y+8*heading_cos;
1489 half_back_x = x-8*heading_sin;
1490 half_back_bg_y = y+10*heading_cos;
1491 half_back_bg_x = x-10*heading_sin;
1492
1493 pt_y = half_back_y-24*heading_cos;
1494 pt_x = half_back_x+24*heading_sin;
8b15d148 1495 //ptbg_y = half_back_bg_y-28*heading_cos;
b87d3952
QT
1496 ptbg_x = half_back_bg_x+28*heading_sin;
1497
1498 side1_y = half_back_y+9*heading_sin;
1499 side1_x = half_back_x+9*heading_cos;
1500 side1bg_y = half_back_bg_y+11*heading_sin;
1501 side1bg_x = half_back_bg_x+11*heading_cos;
1502
1503 side2_y = half_back_y-9*heading_sin;
1504 side2_x = half_back_x-9*heading_cos;
1505 side2bg_y = half_back_bg_y-11*heading_sin;
1506 side2bg_x = half_back_bg_x-11*heading_cos;
1507
1508 GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1509 GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1510
1511 vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1512 vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
c4e61875
QT
1513 vik_viewport_draw_rectangle ( vp,
1514 (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1515 TRUE, x-2, y-2, 4, 4 );
1516 //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
1517 }
1518}
1519
8b5838b2 1520static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
b87d3952 1521{
c4e61875
QT
1522 struct LatLon ll;
1523 GList *last_tp;
97cab2d5
QT
1524
1525 /* Note that fix.time is a double, but it should not affect the precision
1526 for most GPS */
1527 time_t cur_timestamp = vgl->realtime_fix.fix.time;
1528 time_t last_timestamp = vgl->last_fix.fix.time;
1529
1530 if (cur_timestamp < last_timestamp) {
8b5838b2 1531 return NULL;
97cab2d5
QT
1532 }
1533
b87d3952 1534 if (vgl->realtime_record && vgl->realtime_fix.dirty) {
c4e61875 1535 gboolean replace = FALSE;
7888d76f
RN
1536 int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1537 int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
c4e61875
QT
1538 int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1539 int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1540 if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1541 (vgl->realtime_fix.fix.mode > MODE_2D) &&
1542 (vgl->last_fix.fix.mode <= MODE_2D) &&
97cab2d5 1543 ((cur_timestamp - last_timestamp) < 2)) {
c4e61875
QT
1544 g_free(last_tp->data);
1545 vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1546 replace = TRUE;
1547 }
97cab2d5
QT
1548 if (replace ||
1549 ((cur_timestamp != last_timestamp) &&
1550 ((forced ||
1551 ((heading < last_heading) && (heading < (last_heading - 3))) ||
1552 ((heading > last_heading) && (heading > (last_heading + 3))) ||
1553 ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
b87d3952
QT
1554 /* TODO: check for new segments */
1555 VikTrackpoint *tp = vik_trackpoint_new();
1556 tp->newsegment = FALSE;
b87d3952 1557 tp->has_timestamp = TRUE;
c4e61875 1558 tp->timestamp = vgl->realtime_fix.fix.time;
a2817d3c
QT
1559 tp->altitude = alt;
1560 /* speed only available for 3D fix. Check for NAN when use this speed */
1561 tp->speed = vgl->realtime_fix.fix.speed;
1562 tp->course = vgl->realtime_fix.fix.track;
1563 tp->nsats = vgl->realtime_fix.satellites_used;
1564 tp->fix_mode = vgl->realtime_fix.fix.mode;
c4e61875
QT
1565
1566 ll.lat = vgl->realtime_fix.fix.latitude;
1567 ll.lon = vgl->realtime_fix.fix.longitude;
b87d3952 1568 vik_coord_load_from_latlon(&tp->coord,
c4e61875 1569 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
b87d3952 1570
9bc95d58 1571 vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
b87d3952 1572 vgl->realtime_fix.dirty = FALSE;
a2817d3c 1573 vgl->realtime_fix.satellites_used = 0;
b87d3952 1574 vgl->last_fix = vgl->realtime_fix;
8b5838b2 1575 return tp;
b87d3952
QT
1576 }
1577 }
8b5838b2
RN
1578 return NULL;
1579}
1580
1581#define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1582
1583static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1584{
1585 gchar *statusbar_format_code = NULL;
1586 gboolean need2free = FALSE;
1587 if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1588 // Otherwise use default
1589 statusbar_format_code = g_strdup ( "GSA" );
1590 need2free = TRUE;
1591 }
1592
1593 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track );
1594 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1595 g_free ( msg );
1596
1597 if ( need2free )
1598 g_free ( statusbar_format_code );
b87d3952
QT
1599
1600}
1601
c5bc70c3 1602static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
b87d3952 1603{
c4e61875 1604 gboolean update_all = FALSE;
b87d3952
QT
1605 VikGpsLayer *vgl = vgpsd->vgl;
1606
c4e61875
QT
1607 if (!vgl->realtime_tracking) {
1608 g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
b87d3952 1609 return;
b87d3952
QT
1610 }
1611
b87d3952
QT
1612 if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1613 !isnan(vgpsd->gpsd.fix.latitude) &&
7888d76f 1614 !isnan(vgpsd->gpsd.fix.longitude)) {
dc3a1898
QT
1615
1616 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1617 VikViewport *vvp = vik_window_viewport(vw);
c4e61875 1618 vgl->realtime_fix.fix = vgpsd->gpsd.fix;
a2817d3c 1619 vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
b87d3952
QT
1620 vgl->realtime_fix.dirty = TRUE;
1621
dc3a1898
QT
1622 struct LatLon ll;
1623 VikCoord vehicle_coord;
c4e61875 1624
dc3a1898
QT
1625 ll.lat = vgl->realtime_fix.fix.latitude;
1626 ll.lon = vgl->realtime_fix.fix.longitude;
1627 vik_coord_load_from_latlon(&vehicle_coord,
1628 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1629
1630 if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
c5bc70c3 1631 (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
be5554c5 1632 vik_viewport_set_center_coord(vvp, &vehicle_coord, FALSE);
dc3a1898
QT
1633 update_all = TRUE;
1634 }
a00e1e36 1635 else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
dc3a1898
QT
1636 const int hdiv = 6;
1637 const int vdiv = 6;
1638 const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1639 gint width = vik_viewport_get_width(vvp);
1640 gint height = vik_viewport_get_height(vvp);
1641 gint vx, vy;
1642
1643 vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
c4e61875 1644 update_all = TRUE;
dc3a1898
QT
1645 if (vx < (width/hdiv))
1646 vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1647 else if (vx > (width - width/hdiv))
1648 vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1649 else if (vy < (height/vdiv))
1650 vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1651 else if (vy > (height - height/vdiv))
1652 vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1653 else
1654 update_all = FALSE;
c4e61875
QT
1655 }
1656
c5bc70c3 1657 vgl->first_realtime_trackpoint = FALSE;
8b5838b2
RN
1658
1659 vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1660
1661 if ( vgl->trkpt ) {
1662 if ( vgl->realtime_update_statusbar )
1663 update_statusbar ( vgl, vw );
1664 vgl->trkpt_prev = vgl->trkpt;
1665 }
b87d3952 1666
da121f9b 1667 vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
b87d3952
QT
1668 }
1669}
1670
1671static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1672{
b87d3952 1673 VikGpsLayer *vgl = data;
c5bc70c3 1674 if (condition == G_IO_IN) {
bff27ab1
RN
1675#if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1676 if (!gps_poll(&vgl->vgpsd->gpsd)) {
1677#elif GPSD_API_MAJOR_VERSION == 5
1678 if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1679 // Reuse old function to perform operations on the new GPS data
1680 gpsd_raw_hook(vgl->vgpsd, NULL);
1681#else
1682 // Broken compile
1683#endif
c5bc70c3 1684 return TRUE;
bff27ab1 1685 }
c5bc70c3 1686 else {
24953b45 1687 g_warning("Disconnected from gpsd. Trying to reconnect");
c5bc70c3
QT
1688 rt_gpsd_disconnect(vgl);
1689 rt_gpsd_connect(vgl, FALSE);
1690 }
1691 }
1692 return FALSE; /* no further calling */
b87d3952
QT
1693}
1694
1695static gchar *make_track_name(VikTrwLayer *vtl)
1696{
1697 const gchar basename[] = "REALTIME";
1698 const gint bufsize = sizeof(basename) + 5;
1699 gchar *name = g_malloc(bufsize);
1700 strcpy(name, basename);
1701 gint i = 2;
1702
1703 while (vik_trw_layer_get_track(vtl, name) != NULL) {
1704 g_snprintf(name, bufsize, "%s#%d", basename, i);
1705 i++;
1706 }
1707 return(name);
1708
1709}
1710
c5bc70c3
QT
1711static gboolean rt_gpsd_try_connect(gpointer *data)
1712{
1713 VikGpsLayer *vgl = (VikGpsLayer *)data;
728db5b5 1714#if GPSD_API_MAJOR_VERSION == 3
c5bc70c3
QT
1715 struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1716
1717 if (gpsd == NULL) {
728db5b5 1718#elif GPSD_API_MAJOR_VERSION == 4
a263d172
GB
1719 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1720
1721 if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
bff27ab1
RN
1722#elif GPSD_API_MAJOR_VERSION == 5
1723 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1724 if (gps_open(vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd) != 0) {
728db5b5 1725#else
bff27ab1 1726// Delibrately break compilation...
a263d172 1727#endif
24953b45
QT
1728 g_warning("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1729 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
c5bc70c3
QT
1730 return TRUE; /* keep timer running */
1731 }
1732
728db5b5 1733#if GPSD_API_MAJOR_VERSION == 3
a263d172
GB
1734 vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1735#endif
c5bc70c3
QT
1736 vgl->vgpsd->vgl = vgl;
1737
1738 vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1739 /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1740 vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1741 vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1742
1743 if (vgl->realtime_record) {
1744 VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1745 vgl->realtime_track = vik_track_new();
1746 vgl->realtime_track->visible = TRUE;
ce4bd1cf 1747 vik_trw_layer_add_track(vtl, make_track_name(vtl), vgl->realtime_track);
c5bc70c3
QT
1748 }
1749
bff27ab1 1750#if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
c5bc70c3 1751 gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
bff27ab1
RN
1752#endif
1753
c5bc70c3
QT
1754 vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1755 vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1756 G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
728db5b5
RN
1757
1758#if GPSD_API_MAJOR_VERSION == 3
bec818c4
GB
1759 gps_query(&vgl->vgpsd->gpsd, "w+x");
1760#endif
bff27ab1 1761#if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
728db5b5
RN
1762 gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL);
1763#endif
1764
c5bc70c3
QT
1765 return FALSE; /* no longer called by timeout */
1766}
1767
1768static gboolean rt_ask_retry(VikGpsLayer *vgl)
1769{
1770 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER(vgl),
1771 GTK_DIALOG_DESTROY_WITH_PARENT,
1772 GTK_MESSAGE_QUESTION,
1773 GTK_BUTTONS_YES_NO,
1774 "Failed to connect to gpsd at %s (port %s)\n"
1775 "Should Viking keep trying (every %d seconds)?",
1776 vgl->gpsd_host, vgl->gpsd_port,
1777 vgl->gpsd_retry_interval);
1778
1779 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1780 gtk_widget_destroy(dialog);
1781 return (res == GTK_RESPONSE_YES);
1782}
1783
1784static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1785{
1786 vgl->realtime_retry_timer = 0;
1787 if (rt_gpsd_try_connect((gpointer *)vgl)) {
1788 if (vgl->gpsd_retry_interval <= 0) {
1789 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);
1790 return FALSE;
1791 }
1792 else if (ask_if_failed && !rt_ask_retry(vgl))
1793 return FALSE;
1794 else
1795 vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1796 (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1797 }
1798 return TRUE;
1799}
1800
1801static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1802{
c5bc70c3
QT
1803 if (vgl->realtime_retry_timer) {
1804 g_source_remove(vgl->realtime_retry_timer);
1805 vgl->realtime_retry_timer = 0;
1806 }
bff27ab1
RN
1807 if (vgl->realtime_io_watch_id) {
1808 g_source_remove(vgl->realtime_io_watch_id);
1809 vgl->realtime_io_watch_id = 0;
1810 }
c5bc70c3
QT
1811 if (vgl->realtime_io_channel) {
1812 GError *error = NULL;
1813 g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
1814 vgl->realtime_io_channel = NULL;
1815 }
1816 if (vgl->vgpsd) {
bff27ab1
RN
1817#if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
1818 gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
1819#endif
c5bc70c3 1820 gps_close(&vgl->vgpsd->gpsd);
728db5b5 1821#if GPSD_API_MAJOR_VERSION == 3
a263d172 1822 free(vgl->vgpsd);
bff27ab1 1823#elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
728db5b5 1824 g_free(vgl->vgpsd);
a263d172 1825#endif
c5bc70c3
QT
1826 vgl->vgpsd = NULL;
1827 }
1828
1829 if (vgl->realtime_record && vgl->realtime_track) {
c5bc70c3 1830 if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
ce4bd1cf 1831 vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
c5bc70c3
QT
1832 vgl->realtime_track = NULL;
1833 }
1834}
1835
b87d3952
QT
1836static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
1837{
1838 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1839 vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
1840
a2817d3c
QT
1841 /* Make sure we are still in the boat with libgps */
1842 g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
1843
b87d3952 1844 if (vgl->realtime_tracking) {
c5bc70c3
QT
1845 vgl->first_realtime_trackpoint = TRUE;
1846 if (!rt_gpsd_connect(vgl, TRUE)) {
1847 vgl->first_realtime_trackpoint = FALSE;
b87d3952 1848 vgl->realtime_tracking = FALSE;
8b5838b2 1849 vgl->trkpt = NULL;
b87d3952 1850 }
b87d3952
QT
1851 }
1852 else { /* stop realtime tracking */
c5bc70c3 1853 vgl->first_realtime_trackpoint = FALSE;
8b5838b2 1854 vgl->trkpt = NULL;
c5bc70c3 1855 rt_gpsd_disconnect(vgl);
b87d3952
QT
1856 }
1857}
001a86db 1858#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */