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