X-Git-Url: https://git.street.me.uk/andy/viking.git/blobdiff_plain/8cf048bdcfc9bb814b004549ff9319201fe22030..80471a6a905e00bf80ad04fa2061f88ea81f15cb:/src/acquire.c diff --git a/src/acquire.c b/src/acquire.c index 1da404c9..c72330b2 100644 --- a/src/acquire.c +++ b/src/acquire.c @@ -18,215 +18,418 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include #include #include +#include #include "viking.h" #include "babel.h" #include "gpx.h" +#include "acquire.h" + +/************************ FILTER LIST *******************/ +// extern VikDataSourceInterface vik_datasource_gps_interface; +// extern VikDataSourceInterface vik_datasource_google_interface; + +/*** Input is TRWLayer ***/ +extern VikDataSourceInterface vik_datasource_bfilter_simplify_interface; +extern VikDataSourceInterface vik_datasource_bfilter_dup_interface; + +/*** Input is a track and a TRWLayer ***/ +extern VikDataSourceInterface vik_datasource_bfilter_polygon_interface; +extern VikDataSourceInterface vik_datasource_bfilter_exclude_polygon_interface; + +/*** Input is a track ***/ + +const VikDataSourceInterface *filters[] = { + &vik_datasource_bfilter_simplify_interface, + &vik_datasource_bfilter_dup_interface, + &vik_datasource_bfilter_polygon_interface, + &vik_datasource_bfilter_exclude_polygon_interface, +}; + +const guint N_FILTERS = sizeof(filters) / sizeof(filters[0]); + +VikTrack *filter_track = NULL; +gchar *filter_track_name = NULL; + +/********************************************************/ + +/* passed along to worker thread */ +typedef struct { + acq_dialog_widgets_t *w; + gchar *cmd; + gchar *extra; +} w_and_interface_t; + /********************************************************* - * Definitions and routines for acquiring data from GPS + * Definitions and routines for acquiring data from Data Sources in general *********************************************************/ -/* global data structure used to expose the progress dialog to the worker thread */ -typedef struct { - VikWindow *vw; - VikLayersPanel *vlp; - VikViewport *vvp; - GtkWidget *dialog; - GtkWidget *status; - GtkWidget *gps_label; - GtkWidget *ver_label; - GtkWidget *id_label; - GtkWidget *wp_label; - GtkWidget *trk_label; - GtkWidget *progress_label; - gboolean ok; -} acq_dialog_widgets_t; - -acq_dialog_widgets_t *w = NULL; -int total_count = -1; -int count; - -static void set_total_count(gint cnt) +static void progress_func ( BabelProgressCode c, gpointer data, acq_dialog_widgets_t *w ) { - gchar s[128]; - gdk_threads_enter(); - if (w->ok) { - g_sprintf(s, "Downloading %d %s...", cnt, (w->progress_label == w->wp_label) ? "waypoints" : "trackpoints"); - gtk_label_set_text ( GTK_LABEL(w->progress_label), s ); - gtk_widget_show ( w->progress_label ); - total_count = cnt; + gdk_threads_enter (); + if (!w->ok) { + if ( w->source_interface->cleanup_func ) + w->source_interface->cleanup_func( w->user_data ); + g_free ( w ); + gdk_threads_leave(); + g_thread_exit ( NULL ); } - gdk_threads_leave(); -} + gdk_threads_leave (); -static void set_current_count(gint cnt) -{ - gchar s[128]; - gdk_threads_enter(); - if (w->ok) { - if (cnt < total_count) { - g_sprintf(s, "Downloaded %d out of %d %s...", cnt, total_count, (w->progress_label == w->wp_label) ? "waypoints" : "trackpoints"); - } else { - g_sprintf(s, "Downloaded %d %s.", cnt, (w->progress_label == w->wp_label) ? "waypoints" : "trackpoints"); - } - gtk_label_set_text ( GTK_LABEL(w->progress_label), s ); - } - gdk_threads_leave(); + if ( w->source_interface->progress_func ) + w->source_interface->progress_func ( (gpointer) c, data, w ); } -static void set_gps_info(const gchar *info) -{ - gchar s[256]; - gdk_threads_enter(); - if (w->ok) { - g_sprintf(s, "GPS Device: %s", info); - gtk_label_set_text ( GTK_LABEL(w->gps_label), s ); - } - gdk_threads_leave(); -} -/* - * This routine relies on gpsbabel's diagnostic output to display the progress information. - * These outputs differ when different GPS devices are used, so we will need to test - * them on several and add the corresponding support. - */ -static void progress_func ( BabelProgressCode c, gpointer data ) +/* this routine is the worker thread. there is only one simultaneous download allowed */ +static void get_from_anything ( w_and_interface_t *wi ) { - gchar *line; + gchar *cmd = wi->cmd; + gchar *extra = wi->extra; + gboolean result; + VikTrwLayer *vtl; - switch(c) { - case BABEL_DIAG_OUTPUT: - line = (gchar *)data; + gboolean creating_new_layer = TRUE; - /* tells us how many items there will be */ - if (strstr(line, "Xfer Wpt")) { - w->progress_label = w->wp_label; - } - if (strstr(line, "Xfer Trk")) { - w->progress_label = w->trk_label; - } - if (strstr(line, "PRDDAT")) { - gchar **tokens = g_strsplit(line, " ", 0); - gchar info[128]; - int ilen = 0; - int i; - - for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) { - guint ch; - sscanf(tokens[i], "%x", &ch); - info[ilen++] = ch; - } - info[ilen++] = 0; - set_gps_info(info); - } - if (strstr(line, "RECORD")) { - int lsb, msb, cnt; - - sscanf(line+17, "%x", &lsb); - sscanf(line+20, "%x", &msb); - cnt = lsb + msb * 256; - set_total_count(cnt); - count = 0; - } - if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) { - count++; - set_current_count(count); + acq_dialog_widgets_t *w = wi->w; + VikDataSourceInterface *source_interface = wi->w->source_interface; + g_free ( wi ); + wi = NULL; + + gdk_threads_enter(); + if (source_interface->mode == VIK_DATASOURCE_ADDTOLAYER) { + VikLayer *current_selected = vik_layers_panel_get_selected ( w->vlp ); + if ( IS_VIK_TRW_LAYER(current_selected) ) { + vtl = VIK_TRW_LAYER(current_selected); + creating_new_layer = FALSE; } + } + if ( creating_new_layer ) { + vtl = VIK_TRW_LAYER ( vik_layer_create ( VIK_LAYER_TRW, w->vvp, NULL, FALSE ) ); + vik_layer_rename ( VIK_LAYER ( vtl ), _(source_interface->layer_title) ); + gtk_label_set_text ( GTK_LABEL(w->status), _("Working...") ); + } + gdk_threads_leave(); + + switch ( source_interface->type ) { + case VIK_DATASOURCE_GPSBABEL_DIRECT: + result = a_babel_convert_from (vtl, cmd, (BabelStatusFunc) progress_func, extra, w); break; - case BABEL_DONE: + case VIK_DATASOURCE_URL: + result = a_babel_convert_from_url (vtl, cmd, extra, (BabelStatusFunc) progress_func, w); break; - default: + case VIK_DATASOURCE_SHELL_CMD: + result = a_babel_convert_from_shellcommand ( vtl, cmd, extra, (BabelStatusFunc) progress_func, w); break; - } -} - -/* this routine is the worker thread. there is only one simultaneous download allowed */ -static void get_from_gps ( gpointer data ) -{ - VikTrwLayer *vtl; - - if ( w ) { - /* simultaneous downloads are not allowed, so we return. */ - g_free(data); - g_thread_exit ( NULL ); - return; + default: + g_critical("Houston, we've had a problem."); } - w = data; + g_free ( cmd ); + g_free ( extra ); - gdk_threads_enter(); - vtl = VIK_TRW_LAYER ( vik_layer_create ( VIK_LAYER_TRW, w->vvp, NULL, FALSE ) ); - vik_layer_rename ( VIK_LAYER ( vtl ), "Acquired from GPS" ); - gtk_label_set_text ( GTK_LABEL(w->status), "Working..." ); - gdk_threads_leave(); - - if (!a_babel_convert_from (vtl, "-D 9 -t -w -i garmin", progress_func, "/dev/ttyS0")) { -// if (!a_babel_convert_from (vtl, "-D 9 -t -w -i garmin", progress_func, "/dev/ttyS1")) { -// if (!a_babel_convert_from_shellcommand (vtl, "(wget -O - \"http://maps.google.com/maps?q=91214 to 94704&output=xml\" 2>/dev/null) | cat ~/vik/tools/temp.ggl | head -3 | tail -1 | sed 's/.*\\(.*\\)<\\/page>.*/\\1<\\/page>/'", "google", progress_func)) { + if (!result) { gdk_threads_enter(); - gtk_label_set_text ( GTK_LABEL(w->status), "Error: couldn't find gpsbabel." ); + gtk_label_set_text ( GTK_LABEL(w->status), _("Error: acquisition failed.") ); + if ( creating_new_layer ) + g_object_unref ( G_OBJECT ( vtl ) ); gdk_threads_leave(); } + else { + gdk_threads_enter(); + if (w->ok) { + gtk_label_set_text ( GTK_LABEL(w->status), _("Done.") ); + if ( creating_new_layer ) + vik_aggregate_layer_add_layer( vik_layers_panel_get_top_layer(w->vlp), VIK_LAYER(vtl)); + if ( source_interface->keep_dialog_open ) { + gtk_dialog_set_response_sensitive ( GTK_DIALOG(w->dialog), GTK_RESPONSE_ACCEPT, TRUE ); + gtk_dialog_set_response_sensitive ( GTK_DIALOG(w->dialog), GTK_RESPONSE_REJECT, FALSE ); + } else { + gtk_dialog_response ( GTK_DIALOG(w->dialog), GTK_RESPONSE_ACCEPT ); + } + } else { + /* canceled */ + if ( creating_new_layer ) + g_object_unref(vtl); + } + } + if ( source_interface->cleanup_func ) + source_interface->cleanup_func ( w->user_data ); - gdk_threads_enter(); - if (w->ok) { - gtk_label_set_text ( GTK_LABEL(w->status), "Done." ); - vik_aggregate_layer_add_layer( vik_layers_panel_get_top_layer(w->vlp), VIK_LAYER(vtl)); - gtk_dialog_set_response_sensitive ( GTK_DIALOG(w->dialog), GTK_RESPONSE_ACCEPT, TRUE ); - gtk_dialog_set_response_sensitive ( GTK_DIALOG(w->dialog), GTK_RESPONSE_REJECT, FALSE ); + if ( w->ok ) { + w->ok = FALSE; } else { - g_object_unref(vtl); + g_free ( w ); } - g_free ( w ); - w = NULL; + gdk_threads_leave(); g_thread_exit ( NULL ); } -void a_acquire_from_gps ( VikWindow *vw, VikLayersPanel *vlp, VikViewport *vvp ) + +static gchar *write_tmp_trwlayer ( VikTrwLayer *vtl ) +{ + int fd_src; + gchar *name_src; + FILE *f; + g_assert ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0); + f = fdopen(fd_src, "w"); + a_gpx_write_file(vtl, f); + fclose(f); + f = NULL; + return name_src; +} + +/* TODO: write with name of old track */ +static gchar *write_tmp_track ( VikTrack *track ) +{ + int fd_src; + gchar *name_src; + FILE *f; + g_assert ((fd_src = g_file_open_tmp("tmp-viking.XXXXXX", &name_src, NULL)) >= 0); + f = fdopen(fd_src, "w"); + a_gpx_write_track_file("track", track, f); /* Thank you Guilhem! Just when I needed this function... -- Evan */ + fclose(f); + f = NULL; + return name_src; +} + +/* TODO: cleanup, getr rid of redundancy */ + +/* depending on type of filter, often only vtl or track will be given. + * the other can be NULL. + */ +static void acquire ( VikWindow *vw, VikLayersPanel *vlp, VikViewport *vvp, VikDataSourceInterface *source_interface, + VikTrwLayer *vtl, VikTrack *track ) { - GtkWidget *status, *gpslabel, *verlabel, *idlabel, *wplabel, *trklabel; + /* for manual dialogs */ GtkWidget *dialog = NULL; - acq_dialog_widgets_t *w = g_malloc(sizeof(*w)); + GtkWidget *status; + gchar *cmd, *extra; + acq_dialog_widgets_t *w; + gpointer user_data; - dialog = gtk_dialog_new_with_buttons ( "", NULL, 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL ); - gtk_dialog_set_response_sensitive ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT, FALSE ); + /* for UI builder */ + gpointer pass_along_data; + VikLayerParamData *paramdatas = NULL; - status = gtk_label_new ("Status: detecting gpsbabel"); - gpslabel = gtk_label_new ("GPS device: N/A"); - verlabel = gtk_label_new ("Version: N/A"); - idlabel = gtk_label_new ("ID: N/A"); - wplabel = gtk_label_new ("WP"); - trklabel = gtk_label_new ("TRK"); + w_and_interface_t *wi; - gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), status, FALSE, FALSE, 5 ); - gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), gpslabel, FALSE, FALSE, 5 ); - gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), wplabel, FALSE, FALSE, 5 ); - gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), trklabel, FALSE, FALSE, 5 ); + /*** INIT AND CHECK EXISTENCE ***/ + if ( source_interface->init_func ) + user_data = source_interface->init_func(); + else + user_data = NULL; + pass_along_data = user_data; + + if ( source_interface->check_existence_func ) { + gchar *error_str = source_interface->check_existence_func(); + if ( error_str ) { + a_dialog_error_msg ( GTK_WINDOW(vw), error_str ); + g_free ( error_str ); + return; + } + } + + /* BUILD UI & GET OPTIONS IF NECESSARY. */ + + /* POSSIBILITY 0: NO OPTIONS. DO NOTHING HERE. */ + /* POSSIBILITY 1: ADD_SETUP_WIDGETS_FUNC */ + if ( source_interface->add_setup_widgets_func ) { + dialog = gtk_dialog_new_with_buttons ( "", GTK_WINDOW(vw), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL ); + + source_interface->add_setup_widgets_func(dialog, vvp, user_data); + gtk_window_set_title ( GTK_WINDOW(dialog), _(source_interface->window_title) ); + + if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) { + source_interface->cleanup_func(user_data); + gtk_widget_destroy(dialog); + return; + } + } + /* POSSIBILITY 2: UI BUILDER */ + else if ( source_interface->params ) { + paramdatas = a_uibuilder_run_dialog ( GTK_WINDOW(vw), + source_interface->params, source_interface->params_count, + source_interface->params_groups, source_interface->params_groups_count, + source_interface->params_defaults ); + if ( paramdatas ) + pass_along_data = paramdatas; + else + return; /* TODO: do we have to free anything here? */ + } + + /* CREATE INPUT DATA & GET COMMAND STRING */ + + if ( source_interface->inputtype == VIK_DATASOURCE_INPUTTYPE_TRWLAYER ) { + gchar *name_src = write_tmp_trwlayer ( vtl ); + + ((VikDataSourceGetCmdStringFuncWithInput) source_interface->get_cmd_string_func) + ( pass_along_data, &cmd, &extra, name_src ); - gtk_window_set_title ( GTK_WINDOW(dialog), "Acquire data from GPS" ); + g_free ( name_src ); + /* TODO: delete the tmp file? or delete it only after we're done with it? */ + } else if ( source_interface->inputtype == VIK_DATASOURCE_INPUTTYPE_TRWLAYER_TRACK ) { + gchar *name_src = write_tmp_trwlayer ( vtl ); + gchar *name_src_track = write_tmp_track ( track ); + + ((VikDataSourceGetCmdStringFuncWithInputInput) source_interface->get_cmd_string_func) + ( pass_along_data, &cmd, &extra, name_src, name_src_track ); + + g_free ( name_src ); + g_free ( name_src_track ); + } else if ( source_interface->inputtype == VIK_DATASOURCE_INPUTTYPE_TRACK ) { + gchar *name_src_track = write_tmp_track ( track ); + + ((VikDataSourceGetCmdStringFuncWithInput) source_interface->get_cmd_string_func) + ( pass_along_data, &cmd, &extra, name_src_track ); + + g_free ( name_src_track ); + } else + source_interface->get_cmd_string_func ( pass_along_data, &cmd, &extra ); + + /* cleanup for option dialogs */ + if ( source_interface->add_setup_widgets_func ) { + gtk_widget_destroy(dialog); + dialog = NULL; + } else if ( source_interface->params ) { + a_uibuilder_free_paramdatas ( paramdatas, source_interface->params, source_interface->params_count ); + } + + /*** LET'S DO IT! ***/ + + if ( ! cmd ) + return; + + w = g_malloc(sizeof(*w)); + wi = g_malloc(sizeof(*wi)); + wi->w = w; + wi->w->source_interface = source_interface; + wi->cmd = cmd; + wi->extra = extra; /* usually input data type (?) */ + + dialog = gtk_dialog_new_with_buttons ( "", GTK_WINDOW(vw), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL ); + gtk_dialog_set_response_sensitive ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT, FALSE ); + gtk_window_set_title ( GTK_WINDOW(dialog), _(source_interface->window_title) ); - gtk_widget_show ( status ); - gtk_widget_show ( gpslabel ); - gtk_widget_show ( dialog ); w->dialog = dialog; + w->ok = TRUE; + status = gtk_label_new (_("Status: detecting gpsbabel")); + gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), status, FALSE, FALSE, 5 ); + gtk_widget_show_all(status); w->status = status; - w->gps_label = gpslabel; - w->id_label = idlabel; - w->ver_label = verlabel; - w->progress_label = w->wp_label = wplabel; - w->trk_label = trklabel; + w->vw = vw; w->vlp = vlp; w->vvp = vvp; - w->ok = TRUE; - g_thread_create((GThreadFunc)get_from_gps, w, FALSE, NULL ); + if ( source_interface->add_progress_widgets_func ) { + source_interface->add_progress_widgets_func ( dialog, user_data ); + } + w->user_data = user_data; + + + g_thread_create((GThreadFunc)get_from_anything, wi, FALSE, NULL ); gtk_dialog_run ( GTK_DIALOG(dialog) ); - w->ok = FALSE; + if ( w->ok ) + w->ok = FALSE; /* tell thread to stop. TODO: add mutex */ + else { + g_free ( w ); /* thread has finished; free w */ + } gtk_widget_destroy ( dialog ); } + +void a_acquire ( VikWindow *vw, VikLayersPanel *vlp, VikViewport *vvp, VikDataSourceInterface *source_interface ) { + acquire ( vw, vlp, vvp, source_interface, NULL, NULL ); +} + +static void acquire_trwlayer_callback ( GObject *menuitem, gpointer *pass_along ) +{ + VikDataSourceInterface *iface = g_object_get_data ( menuitem, "vik_acq_iface" ); + VikWindow *vw = pass_along[0]; + VikLayersPanel *vlp = pass_along[1]; + VikViewport *vvp = pass_along[2]; + VikTrwLayer *vtl = pass_along[3]; + VikTrack *tr = pass_along[4]; + + acquire ( vw, vlp, vvp, iface, vtl, tr ); +} + +static GtkWidget *acquire_build_menu ( VikWindow *vw, VikLayersPanel *vlp, VikViewport *vvp, + VikTrwLayer *vtl, VikTrack *track, /* both passed to acquire, although for many filters only one ness */ + const gchar *menu_title, vik_datasource_inputtype_t inputtype ) +{ + static gpointer pass_along[4]; + GtkWidget *menu_item=NULL, *menu=NULL; + GtkWidget *item=NULL; + int i; + + pass_along[0] = vw; + pass_along[1] = vlp; + pass_along[2] = vvp; + pass_along[3] = vtl; + pass_along[4] = track; + + for ( i = 0; i < N_FILTERS; i++ ) { + if ( filters[i]->inputtype == inputtype ) { + if ( ! menu_item ) { /* do this just once, but return NULL if no filters */ + menu = gtk_menu_new(); + menu_item = gtk_menu_item_new_with_label ( menu_title ); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu ); + } + + item = gtk_menu_item_new_with_label ( filters[i]->window_title ); + g_object_set_data ( G_OBJECT(item), "vik_acq_iface", (gpointer) filters[i] ); + g_signal_connect ( G_OBJECT(item), "activate", G_CALLBACK(acquire_trwlayer_callback), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + } + } + + return menu_item; +} + +GtkWidget *a_acquire_trwlayer_menu (VikWindow *vw, VikLayersPanel *vlp, VikViewport *vvp, VikTrwLayer *vtl) +{ + return acquire_build_menu ( vw, vlp, vvp, vtl, NULL, "Filter", VIK_DATASOURCE_INPUTTYPE_TRWLAYER ); +} + +GtkWidget *a_acquire_trwlayer_track_menu (VikWindow *vw, VikLayersPanel *vlp, VikViewport *vvp, VikTrwLayer *vtl) +{ + if ( filter_track == NULL ) + return NULL; + else { + gchar *menu_title = g_strdup_printf ( "Filter with %s", filter_track_name ); + GtkWidget *rv = acquire_build_menu ( vw, vlp, vvp, vtl, filter_track, + menu_title, VIK_DATASOURCE_INPUTTYPE_TRWLAYER_TRACK ); + g_free ( menu_title ); + return rv; + } +} + +GtkWidget *a_acquire_track_menu (VikWindow *vw, VikLayersPanel *vlp, VikViewport *vvp, VikTrack *tr) +{ + return acquire_build_menu ( vw, vlp, vvp, NULL, tr, "Filter", VIK_DATASOURCE_INPUTTYPE_TRACK ); +} + +void a_acquire_set_filter_track ( VikTrack *tr, const gchar *name ) +{ + if ( filter_track ) + vik_track_free ( filter_track ); + if ( filter_track_name ) + g_free ( filter_track_name ); + + filter_track = tr; + vik_track_ref ( tr ); + + filter_track_name = g_strdup(name); +}