2 * Copyright (C) 2012 Adam Boggs <boggs@aircrafter.org>
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * If gpsd connection fails, try to connect again periodically.
20 * If gps stops sending data there should be an indication that it's stale.
31 #include <glib/gstdio.h>
39 #include "gps-plugin.h"
41 #include "../aweather-location.h"
43 /* interval to update map with new gps data in seconds. */
44 #define GPS_UPDATE_INTERVAL (2)
46 /* interval to update log file in seconds (default value for slider) */
47 #define GPS_LOG_DEFAULT_UPDATE_INTERVAL (30)
48 #define GPS_LOG_EXT "csv"
50 /* For updating the status bar conveniently */
51 #define GPS_STATUSBAR_CONTEXT "GPS"
53 #define GPS_STATUS(gps_state, format, args...) \
55 char *buf = g_strdup_printf(format, ##args); \
56 gtk_statusbar_push(GTK_STATUSBAR(gps_state->status_bar), \
57 gtk_statusbar_get_context_id( \
58 GTK_STATUSBAR(gps_state->status_bar), \
59 GPS_STATUSBAR_CONTEXT), \
64 #define GPS_STATUS(gps_state, format, args...) \
66 char *buf = g_strdup_printf(format, ##args); \
67 g_warning("STATUS: %s", buf); \
71 static gboolean gps_data_is_valid(struct gps_data_t *gps_data);
72 static char *gps_get_time_string(time_t gps_time);
73 static char *gps_get_date_string(double gps_time);
74 static void process_gps( gpointer, gint, GdkInputCondition);
76 #ifdef GPS_RANGE_RINGS
77 static void gps_init_range_rings(GritsPluginGPS *gps_state,
79 static gboolean on_gps_rangering_clicked_event (GtkWidget *widget, gpointer user_data);
82 static void gps_init_status_info(GritsPluginGPS *gps_state,
84 static void gps_init_control_frame(GritsPluginGPS *gps_state,
86 static gboolean on_gps_follow_clicked_event (GtkWidget *widget, gpointer user_data);
88 /* GPS logging support */
89 static void gps_init_track_log_frame(GritsPluginGPS *gps_state,
91 static gboolean on_gps_log_clicked_event (GtkWidget *widget, gpointer user_data);
92 static gboolean on_gps_track_clicked_event (GtkWidget *widget, gpointer user_data);
93 static gboolean gps_write_log(gpointer data);
95 static char *gps_get_status(struct gps_data_t *);
96 static char *gps_get_latitude(struct gps_data_t *);
97 static char *gps_get_longitude(struct gps_data_t *);
98 static char *gps_get_elevation(struct gps_data_t *);
99 static char *gps_get_heading(struct gps_data_t *);
100 static char *gps_get_speed(struct gps_data_t *);
102 /* Describes a line in the gps table */
103 struct gps_status_info {
106 char *(*get_data)(struct gps_data_t *);
107 unsigned int font_size;
108 GtkWidget *label_widget;
109 GtkWidget *value_widget;
112 struct gps_status_info gps_table[] = {
113 {"Status:", "No Data", gps_get_status, 14, NULL, NULL},
114 // {"Online:", "No Data", gps_get_online, 14, NULL, NULL},
115 {"Latitude:", "No Data", gps_get_latitude, 14, NULL, NULL},
116 {"Longitude:", "No Data", gps_get_longitude, 14, NULL, NULL},
117 {"Elevation:", "No Data", gps_get_elevation, 14, NULL, NULL},
118 {"Heading:", "No Data", gps_get_heading, 14, NULL, NULL},
119 {"Speed:", "No Data", gps_get_speed, 14, NULL, NULL},
123 gboolean gps_data_is_valid(struct gps_data_t *gps_data)
125 if (gps_data != NULL && gps_data->online != -1.0 &&
126 gps_data->fix.mode >= MODE_2D &&
127 gps_data->status > STATUS_NO_FIX) {
135 gps_get_date_string(double gps_time)
137 static char buf[256];
138 time_t int_time = (time_t)gps_time;
141 gmtime_r(&int_time, &tm_time);
143 snprintf(buf, sizeof(buf), "%04d-%02d-%02d",
144 tm_time.tm_year+1900, tm_time.tm_mon+1, tm_time.tm_mday);
151 gps_get_time_string(time_t gps_time)
153 static char buf[256];
154 time_t int_time = (time_t)gps_time;
157 gmtime_r(&int_time, &tm_time);
159 snprintf(buf, sizeof(buf), "%02d:%02d:%02dZ",
160 tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
167 update_gps_status(GritsPluginGPS *gps_state)
169 struct gps_data_t *gps_data = &gps_state->gps_data;
171 /* gps table update */
174 for (i = 0; i < sizeof(gps_table)/sizeof(*gps_table); i++) {
175 gtk_label_set_markup (GTK_LABEL(gps_table[i].value_widget),
176 (str = gps_table[i].get_data(gps_data)));
182 gps_init_control_frame(GritsPluginGPS *gps_state, GtkWidget *gbox)
184 /* Control checkboxes */
185 GtkWidget *gps_control_frame = gtk_frame_new ("GPS Control");
186 GtkWidget *cbox = gtk_vbox_new (FALSE, 2);
187 gtk_container_add (GTK_CONTAINER (gps_control_frame), cbox);
188 gtk_box_pack_start (GTK_BOX(gbox), gps_control_frame, FALSE, FALSE, 0);
190 gps_state->ui.gps_follow_checkbox = gtk_check_button_new_with_label("Follow GPS");
191 g_signal_connect (G_OBJECT (gps_state->ui.gps_follow_checkbox), "clicked",
192 G_CALLBACK (on_gps_follow_clicked_event),
193 (gpointer)gps_state);
194 gtk_box_pack_start (GTK_BOX(cbox), gps_state->ui.gps_follow_checkbox,
196 gps_state->ui.gps_track_checkbox = gtk_check_button_new_with_label("Show Track");
197 g_signal_connect (G_OBJECT (gps_state->ui.gps_track_checkbox), "clicked",
198 G_CALLBACK (on_gps_track_clicked_event),
199 (gpointer)gps_state);
200 gtk_box_pack_start (GTK_BOX(cbox), gps_state->ui.gps_track_checkbox,
205 on_gps_track_clicked_event (GtkWidget *widget, gpointer user_data)
207 g_debug("on_gps_track_clicked_event called!");
208 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
209 /* XXX start logging trip history */
210 GPS_STATUS(gps_state, "Enabled GPS track.");
212 /* XXX stop logging trip history */
213 GPS_STATUS(gps_state, "Disabled GPS track.");
220 on_gps_log_interval_changed_event(GtkWidget *widget, gpointer user_data)
222 GritsPluginGPS *gps_state = (GritsPluginGPS *)user_data;
226 g_debug("gps interval changed, value = %f",
227 gtk_range_get_value(GTK_RANGE(widget)));
229 if (gtk_toggle_button_get_active(
230 GTK_TOGGLE_BUTTON(gps_state->ui.gps_log_checkbox))) {
231 assert(gps_state->ui.gps_log_timeout_id != 0);
233 /* disable old timeout */
234 g_source_remove(gps_state->ui.gps_log_timeout_id);
235 gps_state->ui.gps_log_timeout_id = 0;
237 /* Schedule new log file write */
238 gps_state->ui.gps_log_timeout_id = g_timeout_add(
239 gtk_range_get_value(GTK_RANGE(widget))*1000,
240 gps_write_log, gps_state);
241 gps_write_log(gps_state);
248 gps_write_log(gpointer data)
250 GritsPluginGPS *gps_state = (GritsPluginGPS *)data;
251 struct gps_data_t *gps_data = &gps_state->gps_data;
255 gboolean new_file = FALSE;
257 if (gps_data == NULL) {
258 g_warning("Skipped write to GPS log file: "
259 "can not get GPS coordinates.");
260 GPS_STATUS(gps_state, "Skipped write to GPS log file: "
261 "can not get GPS coordinates.");
265 /* get filename from text entry box. If empty, generate a name from
266 * the date and time and set it.
268 if (strlen(gtk_entry_get_text(GTK_ENTRY(gps_state->ui.gps_log_filename_entry)))
270 snprintf(filename, sizeof(filename),
272 gps_get_date_string(gps_state->gps_data.fix.time),
273 gps_get_time_string(gps_state->gps_data.fix.time),
275 gtk_entry_set_text(GTK_ENTRY(gps_state->ui.gps_log_filename_entry),
280 gtk_entry_get_text(GTK_ENTRY(gps_state->ui.gps_log_filename_entry)),
283 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
287 if ((fd = open(filename, O_CREAT|O_APPEND|O_WRONLY, 0644)) == -1) {
288 g_warning("Error opening log file %s: %s",
289 filename, strerror(errno));
294 /* write header and reset record counter */
295 snprintf(buf, sizeof(buf),
296 "No,Date,Time,Lat,Lon,Ele,Head,Speed,RTR\n");
297 if (write(fd, buf, strlen(buf)) == -1) {
298 g_warning("Error writing header to log file %s: %s",
299 filename, strerror(errno));
301 gps_state->ui.gps_log_number = 1;
304 /* Write log entry. Make sure this matches the header */
305 /* "No,Date,Time,Lat,Lon,Ele,Head,Speed,Fix,RTR\n" */
306 /* RTR values: T=time, B=button push, S=speed, D=distance */
307 snprintf(buf, sizeof(buf), "%d,%s,%s,%lf,%lf,%lf,%lf,%lf,%c\n",
308 gps_state->ui.gps_log_number++,
309 gps_get_date_string(gps_state->gps_data.fix.time),
310 gps_get_time_string(gps_state->gps_data.fix.time),
311 // gps_data->fix.time,
312 gps_data->fix.latitude,
313 gps_data->fix.longitude,
314 gps_data->fix.altitude * METERS_TO_FEET,
316 gps_data->fix.speed * METERS_TO_FEET,
317 'T'); /* position due to timer expired */
319 if (write(fd, buf, strlen(buf)) == -1) {
320 g_warning("Could not write log number %d to log file %s: %s",
321 gps_state->ui.gps_log_number-1, filename, strerror(errno));
325 GPS_STATUS(gps_state, "Updated GPS log file %s.", filename);
332 on_gps_follow_clicked_event (GtkWidget *widget, gpointer user_data)
334 GritsPluginGPS *gps_state = (GritsPluginGPS *)user_data;
336 g_debug("on_gps_follow_clicked_event called!, button status %d",
337 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
338 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
339 gps_state->follow_gps = TRUE;
341 gps_state->follow_gps = FALSE;
348 gps_init_track_log_frame(GritsPluginGPS *gps_state, GtkWidget *gbox)
350 /* Track log box with enable checkbox and filename entry */
351 GtkWidget *gps_log_frame = gtk_frame_new ("Track Log");
352 GtkWidget *lbox = gtk_vbox_new (FALSE, 2);
353 gtk_container_add (GTK_CONTAINER (gps_log_frame), lbox);
354 gtk_box_pack_start (GTK_BOX(gbox), gps_log_frame,
357 gps_state->ui.gps_log_checkbox = gtk_check_button_new_with_label("Log Position to File");
358 g_signal_connect (G_OBJECT (gps_state->ui.gps_log_checkbox), "clicked",
359 G_CALLBACK (on_gps_log_clicked_event),
360 (gpointer)gps_state);
361 gtk_box_pack_start (GTK_BOX(lbox), gps_state->ui.gps_log_checkbox,
364 /* Set up filename entry box */
365 GtkWidget *fbox = gtk_hbox_new (FALSE, 2);
366 GtkWidget *filename_label = gtk_label_new ("Filename:");
367 gtk_box_pack_start (GTK_BOX(fbox), filename_label, FALSE, FALSE, 0);
368 gps_state->ui.gps_log_filename_entry = gtk_entry_new();
369 gtk_box_pack_start (GTK_BOX(fbox), gps_state->ui.gps_log_filename_entry,
371 gtk_box_pack_start (GTK_BOX(lbox), fbox, FALSE, FALSE, 0);
373 /* set up gps log interval slider */
374 GtkWidget *ubox = gtk_hbox_new (FALSE, 4);
375 GtkWidget *interval_label = gtk_label_new ("Update Interval:");
376 gtk_box_pack_start (GTK_BOX(ubox), interval_label, FALSE, FALSE, 0);
377 gps_state->ui.gps_log_interval_slider =
378 gtk_hscale_new_with_range(1.0, 600.0, 30.0);
379 gtk_range_set_value (GTK_RANGE(gps_state->ui.gps_log_interval_slider),
380 GPS_LOG_DEFAULT_UPDATE_INTERVAL);
381 g_signal_connect (G_OBJECT (gps_state->ui.gps_log_interval_slider),
383 G_CALLBACK(on_gps_log_interval_changed_event),
384 (gpointer)gps_state);
385 gtk_range_set_increments (GTK_RANGE(gps_state->ui.gps_log_interval_slider),
386 10.0 /* step */, 30.0 /* page up/down */);
387 gtk_range_set_update_policy (GTK_RANGE(gps_state->ui.gps_log_interval_slider),
389 gtk_box_pack_start (GTK_BOX(ubox), gps_state->ui.gps_log_interval_slider,
391 gtk_box_pack_start (GTK_BOX(lbox), ubox, FALSE, FALSE, 0);
395 on_gps_log_clicked_event (GtkWidget *widget, gpointer user_data)
397 GritsPluginGPS *gps_state = (GritsPluginGPS *)user_data;
399 g_debug("on_gps_log_clicked_event called!");
401 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
402 gps_write_log(gps_state);
404 /* Schedule log file write */
405 gps_state->ui.gps_log_timeout_id = g_timeout_add(
407 GTK_RANGE(gps_state->ui.gps_log_interval_slider))*1000,
408 gps_write_log, gps_state);
410 /* button unchecked */
411 g_source_remove(gps_state->ui.gps_log_timeout_id);
412 gps_state->ui.gps_log_timeout_id = 0;
413 g_debug("Closed log file.");
421 gps_init_status_info(GritsPluginGPS *gps_state, GtkWidget *gbox)
423 gps_state->ui.gps_status_frame = gtk_frame_new ("GPS Data");
424 gps_state->ui.gps_status_table = gtk_table_new (5, 2, TRUE);
425 gtk_container_add (GTK_CONTAINER (gps_state->ui.gps_status_frame),
426 gps_state->ui.gps_status_table);
428 /* gps data table setup */
430 for (i = 0; i < sizeof(gps_table)/sizeof(*gps_table); i++) {
431 gps_table[i].label_widget = gtk_label_new (gps_table[i].label);
432 gtk_label_set_justify(GTK_LABEL(gps_table[i].label_widget),
434 gtk_table_attach( GTK_TABLE(gps_state->ui.gps_status_table),
435 gps_table[i].label_widget, 0, 1, i, i+1, 0, 0, 0, 0);
436 gps_table[i].value_widget = gtk_label_new (gps_table[i].initial_val);
437 gtk_table_attach( GTK_TABLE(gps_state->ui.gps_status_table),
438 gps_table[i].value_widget, 1, 2, i, i+1, 0, 0, 0, 0);
440 PangoFontDescription *font_desc = pango_font_description_new ();
441 pango_font_description_set_size (font_desc,
442 gps_table[i].font_size*PANGO_SCALE);
443 gtk_widget_modify_font (gps_table[i].label_widget, font_desc);
444 gtk_widget_modify_font (gps_table[i].value_widget, font_desc);
445 pango_font_description_free (font_desc);
447 gtk_box_pack_start (GTK_BOX(gbox), gps_state->ui.gps_status_frame,
450 /* Start UI refresh task, which will reschedule itself periodically. */
451 gps_redraw_all(gps_state);
452 gps_state->gps_update_timeout_id = g_timeout_add(
453 GPS_UPDATE_INTERVAL*1000,
454 gps_redraw_all, gps_state);
459 #ifdef GPS_RANGE_RINGS
461 gps_init_range_rings(GritsPluginGPS *gps_state, GtkWidget *gbox)
463 GtkWidget *gps_range_ring_frame = gtk_frame_new ("Range Rings");
464 GtkWidget *cbox = gtk_vbox_new (FALSE, 2);
465 gtk_container_add (GTK_CONTAINER (gps_range_ring_frame), cbox);
466 gtk_box_pack_start (GTK_BOX(gbox), gps_range_ring_frame, FALSE, FALSE, 0);
468 gps_state->ui.gps_rangering_checkbox = gtk_check_button_new_with_label("Enable Range Rings");
469 g_signal_connect (G_OBJECT (gps_state->ui.gps_rangering_checkbox), "clicked",
470 G_CALLBACK (on_gps_rangering_clicked_event),
471 (gpointer)gps_state);
472 gtk_box_pack_start (GTK_BOX(cbox), gps_state->ui.gps_rangering_checkbox,
477 on_gps_rangering_clicked_event (GtkWidget *widget, gpointer user_data)
479 GritsPluginGPS *gps_state = (GritsPluginGPS *)user_data;
481 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
482 gps_state->gps_rangering_active = TRUE;
484 gps_state->gps_rangering_active = FALSE;
487 /* XXX force a redraw */
491 #endif /* GPS_RANGE_RINGS */
493 /* external interface to update UI from latest GPS data. */
494 gboolean gps_redraw_all(gpointer data)
496 GritsPluginGPS *gps_state = (GritsPluginGPS *)data;
499 struct gps_data_t *gps_data = &gps_state->gps_data;
501 g_debug("gps_redraw_all called");
504 if (!gps_data_is_valid(gps_data)) {
505 g_debug("gps_data is not valid.");
506 /* XXX Change marker to indicate data is not valid */
510 /* update position labels */
511 update_gps_status(gps_state);
513 /* Update track and marker position */
514 if (gps_data_is_valid(gps_data)) {
515 g_debug("Updating track at lat = %f, long = %f, track = %f",
516 gps_data->fix.latitude,
517 gps_data->fix.longitude,
518 gps_data->fix.track);
520 if (gps_state->marker) {
521 grits_viewer_remove(gps_state->viewer,
522 GRITS_OBJECT(gps_state->marker));
523 gps_state->marker = NULL;
526 gps_state->marker = grits_marker_icon_new("GPS", "car.png",
527 gps_data->fix.track, TRUE);
529 GRITS_OBJECT(gps_state->marker)->center.lat = gps_data->fix.latitude;
530 GRITS_OBJECT(gps_state->marker)->center.lon = gps_data->fix.longitude;
531 GRITS_OBJECT(gps_state->marker)->center.elev = 0.0;
532 GRITS_OBJECT(gps_state->marker)->lod = EARTH_R;
534 grits_viewer_add(gps_state->viewer, GRITS_OBJECT(gps_state->marker),
535 GRITS_LEVEL_OVERLAY, TRUE);
536 grits_viewer_refresh(gps_state->viewer);
539 if (gps_state->follow_gps && gps_data_is_valid(gps_data)) {
540 /* Center map at current GPS position. */
541 g_debug("Centering map at lat = %f, long = %f, track = %f",
542 gps_data->fix.latitude,
543 gps_data->fix.longitude,
544 gps_data->fix.track);
546 double lat, lon, elev;
547 grits_viewer_get_location(gps_state->viewer, &lat, &lon, &elev);
548 grits_viewer_set_location(gps_state->viewer, gps_data->fix.latitude,
549 gps_data->fix.longitude, elev);
550 //grits_viewer_set_rotation(gps_state->viewer, 0, 0, 0);
558 char *gps_get_status(struct gps_data_t *gps_data)
563 switch (gps_data->fix.mode) {
565 status_color = "red";
566 status_text = "No Signal";
569 status_color = "red";
570 status_text = "No Fix";
573 status_color = "yellow";
574 status_text = "2D Mode";
577 status_color = "green";
578 status_text = "3D Mode";
581 status_color = "black";
582 status_text = "Unknown";
585 return g_strdup_printf("<span foreground=\"%s\">%s</span>",
586 status_color, status_text);
590 static char *gps_get_online(struct gps_data_t *);
593 char *gps_get_online(struct gps_data_t *gps_data)
598 if (gps_data->online == -1.0) {
599 online_str = "Offline";
601 online_str = "Online";
604 switch (gps_data->status) {
606 status_str = "No Fix";
609 status_str = "Fix Acquired";
612 status_str = "DGPS Fix";
615 status_str = "Unknown Status";
619 return g_strdup_printf("%lf,%s,%s", gps_data->online, online_str, status_str);
626 char *gps_get_latitude(struct gps_data_t *gps_data)
628 return g_strdup_printf("%3.4f", gps_data->fix.latitude);
632 char *gps_get_longitude(struct gps_data_t *gps_data)
634 return g_strdup_printf("%3.4f", gps_data->fix.longitude);
638 char *gps_get_elevation(struct gps_data_t *gps_data)
640 /* XXX Make units (m/ft) settable */
641 return g_strdup_printf("%.1lf %s",
642 (gps_data->fix.altitude * METERS_TO_FEET), "ft");
646 char *gps_get_heading(struct gps_data_t *gps_data)
648 /* XXX Make units (m/ft) settable */
649 return g_strdup_printf("%03.0lf", gps_data->fix.track);
653 char *gps_get_speed(struct gps_data_t *gps_data)
655 /* XXX Make units (m/ft) settable */
656 return g_strdup_printf("%1.1f %s",
657 (gps_data->fix.speed*3600.0*METERS_TO_FEET/5280.0), "mph");
662 gint initialize_gpsd(char *server, char *port,
663 struct gps_data_t *gps_data)
665 #if GPSD_API_MAJOR_VERSION < 5
666 #error "GPSD protocol version 5 or greater required."
670 if ((result = gps_open(server, port, gps_data)) != 0) {
671 g_warning("Unable to open gpsd connection to %s:%s: %d, %d, %s",
672 server, port, result, errno, gps_errstr(errno));
674 (void)gps_stream(gps_data, WATCH_ENABLE|WATCH_JSON, NULL);
675 g_debug("initialize_gpsd(): gpsd fd %u.", gps_data->gps_fd);
676 gdk_input_add(gps_data->gps_fd, GDK_INPUT_READ, process_gps, gps_data);
683 process_gps(gpointer data, gint source, GdkInputCondition condition)
685 struct gps_data_t *gps_data = (struct gps_data_t *)data;
687 /* Process any data from the gps and call the hook function */
688 g_debug("In process_gps()");
689 if (gps_data != NULL) {
690 int result = gps_read(gps_data);
691 g_debug("In process_gps(), gps_read returned %d, position %f, %f.", result, gps_data->fix.latitude, gps_data->fix.longitude);
693 g_debug("process_gps: gps_data == NULL.");
697 /************************** GPS Object Methods *************************/
700 GritsPluginGPS *grits_plugin_gps_new(GritsViewer *viewer, GritsPrefs *prefs)
702 /* TODO: move to constructor if possible */
703 g_debug("GritsPluginGPS: new");
704 GritsPluginGPS *self = g_object_new(GRITS_TYPE_PLUGIN_GPS, NULL);
705 self->viewer = viewer;
708 g_debug("grits_plugin_gps_new()");
710 initialize_gpsd("localhost", DEFAULT_GPSD_PORT, &self->gps_data);
711 self->follow_gps = FALSE;
713 gps_init_status_info(self, self->hbox);
714 gps_init_control_frame(self, self->hbox);
715 gps_init_track_log_frame(self, self->hbox);
716 #ifdef GPS_RANGE_RINGS
717 gps_init_range_rings(self, self->hbox);
723 static GtkWidget *grits_plugin_gps_get_config(GritsPlugin *_self)
725 GritsPluginGPS *self = GRITS_PLUGIN_GPS(_self);
730 static void grits_plugin_gps_plugin_init(GritsPluginInterface *iface);
731 G_DEFINE_TYPE_WITH_CODE(GritsPluginGPS, grits_plugin_gps, G_TYPE_OBJECT,
732 G_IMPLEMENT_INTERFACE(GRITS_TYPE_PLUGIN,
733 grits_plugin_gps_plugin_init));
735 static void grits_plugin_gps_plugin_init(GritsPluginInterface *iface)
737 g_debug("GritsPluginGPS: plugin_init");
738 /* Add methods to the interface */
739 iface->get_config = grits_plugin_gps_get_config;
742 static void grits_plugin_gps_init(GritsPluginGPS *self)
744 g_debug("GritsPluginGPS: in gps_init()");
746 self->config = gtk_notebook_new();
748 self->hbox = gtk_hbox_new(FALSE, 2);
749 gtk_notebook_insert_page(GTK_NOTEBOOK(self->config),
750 GTK_WIDGET(self->hbox),
751 gtk_label_new("GPS"), 0);
752 /* Need to position on the top because of Win32 bug */
753 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(self->config), GTK_POS_LEFT);
756 static void grits_plugin_gps_dispose(GObject *gobject)
758 GritsPluginGPS *self = GRITS_PLUGIN_GPS(gobject);
760 g_debug("GritsPluginGPS: dispose");
764 grits_viewer_remove(self->viewer,
765 GRITS_OBJECT(self->marker));
767 g_object_unref(self->viewer);
771 /* Drop references */
772 G_OBJECT_CLASS(grits_plugin_gps_parent_class)->dispose(gobject);
775 static void grits_plugin_gps_finalize(GObject *gobject)
777 GritsPluginGPS *self = GRITS_PLUGIN_GPS(gobject);
779 g_debug("GritsPluginGPS: finalize");
782 gtk_widget_destroy(self->config);
783 G_OBJECT_CLASS(grits_plugin_gps_parent_class)->finalize(gobject);
786 static void grits_plugin_gps_class_init(GritsPluginGPSClass *klass)
788 g_debug("GritsPluginGPS: class_init");
789 GObjectClass *gobject_class = (GObjectClass*)klass;
790 gobject_class->dispose = grits_plugin_gps_dispose;
791 gobject_class->finalize = grits_plugin_gps_finalize;