1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
5 * Copyright (C) 2013-2015, Rob Norris <rw_norris@hotmail.com>
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.
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.
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
27 #include "vikwebtool_datasource.h"
32 #include <glib/gi18n.h>
39 static GObjectClass *parent_class;
40 static GHashTable *last_user_strings = NULL;
42 static void webtool_datasource_finalize ( GObject *gob );
44 static gchar *webtool_datasource_get_url ( VikWebtool *self, VikWindow *vw );
46 static gboolean webtool_needs_user_string ( VikWebtool *self );
48 typedef struct _VikWebtoolDatasourcePrivate VikWebtoolDatasourcePrivate;
50 struct _VikWebtoolDatasourcePrivate
53 gchar *url_format_code;
55 gchar *babel_filter_args;
60 #define WEBTOOL_DATASOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
61 VIK_WEBTOOL_DATASOURCE_TYPE, \
62 VikWebtoolDatasourcePrivate))
64 G_DEFINE_TYPE (VikWebtoolDatasource, vik_webtool_datasource, VIK_WEBTOOL_TYPE)
72 PROP_BABEL_FILTER_ARGS,
76 static void webtool_datasource_set_property (GObject *object,
81 VikWebtoolDatasource *self = VIK_WEBTOOL_DATASOURCE ( object );
82 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
84 switch ( property_id ) {
88 priv->url = g_value_dup_string ( value );
89 g_debug ( "VikWebtoolDatasource.url: %s", priv->url );
92 case PROP_URL_FORMAT_CODE:
93 g_free ( priv->url_format_code );
94 priv->url_format_code = g_value_dup_string ( value );
95 g_debug ( "VikWebtoolDatasource.url_format_code: %s", priv->url_format_code );
99 g_free ( priv->file_type );
100 priv->file_type = g_value_dup_string ( value );
101 g_debug ( "VikWebtoolDatasource.file_type: %s", priv->url_format_code );
104 case PROP_BABEL_FILTER_ARGS:
105 g_free ( priv->babel_filter_args );
106 priv->babel_filter_args = g_value_dup_string ( value );
107 g_debug ( "VikWebtoolDatasource.babel_filter_args: %s", priv->babel_filter_args );
110 case PROP_INPUT_LABEL:
111 g_free ( priv->input_label );
112 priv->input_label = g_value_dup_string ( value );
113 g_debug ( "VikWebtoolDatasource.input_label: %s", priv->input_label );
117 /* We don't have any other property... */
118 G_OBJECT_WARN_INVALID_PROPERTY_ID ( object, property_id, pspec );
123 static void webtool_datasource_get_property (GObject *object,
128 VikWebtoolDatasource *self = VIK_WEBTOOL_DATASOURCE ( object );
129 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
131 switch ( property_id ) {
133 case PROP_URL: g_value_set_string ( value, priv->url ); break;
134 case PROP_URL_FORMAT_CODE: g_value_set_string ( value, priv->url_format_code ); break;
135 case PROP_FILE_TYPE: g_value_set_string ( value, priv->url ); break;
136 case PROP_BABEL_FILTER_ARGS: g_value_set_string ( value, priv->babel_filter_args ); break;
137 case PROP_INPUT_LABEL: g_value_set_string ( value, priv->input_label ); break;
140 /* We don't have any other property... */
141 G_OBJECT_WARN_INVALID_PROPERTY_ID ( object, property_id, pspec );
150 GtkWidget *user_string;
154 static void ensure_last_user_strings_hash() {
155 if ( last_user_strings == NULL ) {
156 last_user_strings = g_hash_table_new_full ( g_str_hash,
164 static gchar *get_last_user_string ( const datasource_t *source ) {
165 ensure_last_user_strings_hash();
166 gchar *label = vik_ext_tool_get_label ( source->self );
167 gchar *last_str = g_hash_table_lookup ( last_user_strings, label );
173 static void set_last_user_string ( const datasource_t *source, const gchar *s ) {
174 ensure_last_user_strings_hash();
175 g_hash_table_insert ( last_user_strings,
176 vik_ext_tool_get_label ( source->self ),
180 static gpointer datasource_init ( acq_vik_t *avt )
182 datasource_t *data = g_malloc(sizeof(*data));
183 data->self = avt->userdata;
185 data->vvp = avt->vvp;
186 data->user_string = NULL;
190 static void datasource_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data )
192 datasource_t *widgets = (datasource_t *)user_data;
193 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( widgets->self );
194 GtkWidget *user_string_label;
195 gchar *label = g_strdup_printf( "%s:", priv->input_label );
196 user_string_label = gtk_label_new ( label );
197 widgets->user_string = gtk_entry_new ( );
199 gchar *last_str = get_last_user_string ( widgets );
201 gtk_entry_set_text( GTK_ENTRY( widgets->user_string ), last_str );
203 // 'ok' when press return in the entry
204 g_signal_connect_swapped (widgets->user_string, "activate", G_CALLBACK(a_dialog_response_accept), dialog);
206 /* Packing all widgets */
207 GtkBox *box = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
208 gtk_box_pack_start ( box, user_string_label, FALSE, FALSE, 5 );
209 gtk_box_pack_start ( box, widgets->user_string, FALSE, FALSE, 5 );
210 gtk_widget_show_all ( dialog );
211 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
212 // NB presently the focus is overridden later on by the acquire.c code.
213 gtk_widget_grab_focus ( widgets->user_string );
220 static void datasource_get_process_options ( gpointer user_data, ProcessOptions *po, DownloadMapOptions *options, const gchar *notused1, const gchar *notused2 )
222 datasource_t *data = (datasource_t*) user_data;
224 VikWebtool *vwd = VIK_WEBTOOL ( data->self );
225 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( data->self );
227 if ( webtool_needs_user_string ( vwd ) ) {
228 priv->user_string = g_strdup ( gtk_entry_get_text ( GTK_ENTRY ( data->user_string ) ) );
230 if ( priv->user_string[0] != '\0' ) {
231 set_last_user_string ( data, priv->user_string );
235 gchar *url = vik_webtool_get_url ( vwd, data->vw );
236 g_debug ("%s: %s", __FUNCTION__, url );
238 po->url = g_strdup ( url );
240 // Only use first section of the file_type string
241 // One can't use values like 'kml -x transform,rte=wpt' in order to do fancy things
242 // since it won't be in the right order for the overall GPSBabel command
243 // So prevent any potentially dangerous behaviour
244 gchar **parts = NULL;
245 if ( priv->file_type )
246 parts = g_strsplit ( priv->file_type, " ", 0);
248 po->input_file_type = g_strdup ( parts[0] );
250 po->input_file_type = NULL;
251 g_strfreev ( parts );
254 po->babel_filters = priv->babel_filter_args;
257 static void cleanup ( gpointer data )
262 static void webtool_datasource_open ( VikExtTool *self, VikWindow *vw )
264 gboolean search = webtool_needs_user_string ( VIK_WEBTOOL ( self ) );
266 // Use VikDataSourceInterface to give thready goodness controls of downloading stuff (i.e. can cancel the request)
268 // Can now create a 'VikDataSourceInterface' on the fly...
269 VikDataSourceInterface *vik_datasource_interface = g_malloc(sizeof(VikDataSourceInterface));
271 // An 'easy' way of assigning values
272 VikDataSourceInterface data = {
273 vik_ext_tool_get_label (self),
274 vik_ext_tool_get_label (self),
275 VIK_DATASOURCE_ADDTOLAYER,
276 VIK_DATASOURCE_INPUTTYPE_NONE,
277 FALSE, // Maintain current view - rather than setting it to the acquired points
280 (VikDataSourceInitFunc) datasource_init,
281 (VikDataSourceCheckExistenceFunc) NULL,
282 (VikDataSourceAddSetupWidgetsFunc) (search ? datasource_add_setup_widgets : NULL),
283 (VikDataSourceGetProcessOptionsFunc) datasource_get_process_options,
284 (VikDataSourceProcessFunc) a_babel_convert_from,
285 (VikDataSourceProgressFunc) NULL,
286 (VikDataSourceAddProgressWidgetsFunc) NULL,
287 (VikDataSourceCleanupFunc) cleanup,
288 (VikDataSourceOffFunc) NULL,
295 memcpy ( vik_datasource_interface, &data, sizeof(VikDataSourceInterface) );
297 a_acquire ( vw, vik_window_layers_panel(vw), vik_window_viewport (vw), data.mode, vik_datasource_interface, self, cleanup );
300 static void vik_webtool_datasource_class_init ( VikWebtoolDatasourceClass *klass )
302 GObjectClass *gobject_class;
303 VikWebtoolClass *base_class;
306 gobject_class = G_OBJECT_CLASS (klass);
308 gobject_class->finalize = webtool_datasource_finalize;
309 gobject_class->set_property = webtool_datasource_set_property;
310 gobject_class->get_property = webtool_datasource_get_property;
312 pspec = g_param_spec_string ("url",
314 "Set the template URL",
315 VIKING_URL /* default value */,
316 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
317 g_object_class_install_property (gobject_class,
321 pspec = g_param_spec_string ("url_format_code",
322 "Template URL Format Code",
323 "Set the template URL format code",
324 "LRBT", // default value
325 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
326 g_object_class_install_property (gobject_class,
327 PROP_URL_FORMAT_CODE,
330 pspec = g_param_spec_string ("file_type",
331 "The file type expected",
333 NULL, // default value ~ equates to internal GPX reading
334 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
335 g_object_class_install_property (gobject_class,
339 pspec = g_param_spec_string ("babel_filter_args",
340 "The command line filter options to pass to gpsbabel",
341 "Set the command line filter options for gpsbabel",
342 NULL, // default value
343 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
344 g_object_class_install_property (gobject_class,
345 PROP_BABEL_FILTER_ARGS,
348 pspec = g_param_spec_string ("input_label",
349 "The label for the user input box if input is required.",
350 "Set the label to be shown next to the user input box if an input term is required",
352 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
353 g_object_class_install_property (gobject_class,
357 parent_class = g_type_class_peek_parent (klass);
359 base_class = VIK_WEBTOOL_CLASS ( klass );
360 base_class->get_url = webtool_datasource_get_url;
362 // Override default open function here:
363 VikExtToolClass *ext_tool_class = VIK_EXT_TOOL_CLASS ( klass );
364 ext_tool_class->open = webtool_datasource_open;
366 g_type_class_add_private (klass, sizeof (VikWebtoolDatasourcePrivate));
369 VikWebtoolDatasource *vik_webtool_datasource_new ()
371 return VIK_WEBTOOL_DATASOURCE ( g_object_new ( VIK_WEBTOOL_DATASOURCE_TYPE, NULL ) );
374 VikWebtoolDatasource *vik_webtool_datasource_new_with_members ( const gchar *label,
376 const gchar *url_format_code,
377 const gchar *file_type,
378 const gchar *babel_filter_args,
379 const gchar *input_label )
381 VikWebtoolDatasource *result = VIK_WEBTOOL_DATASOURCE ( g_object_new ( VIK_WEBTOOL_DATASOURCE_TYPE,
384 "url_format_code", url_format_code,
385 "file_type", file_type,
386 "babel_filter_args", babel_filter_args,
387 "input_label", input_label,
393 static void vik_webtool_datasource_init ( VikWebtoolDatasource *self )
395 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE (self);
397 priv->url_format_code = NULL;
398 priv->file_type = NULL;
399 priv->babel_filter_args = NULL;
400 priv->input_label = NULL;
401 priv->user_string = NULL;
404 static void webtool_datasource_finalize ( GObject *gob )
406 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( gob );
407 g_free ( priv->url ); priv->url = NULL;
408 g_free ( priv->url_format_code ); priv->url_format_code = NULL;
409 g_free ( priv->file_type ); priv->file_type = NULL;
410 g_free ( priv->babel_filter_args ); priv->babel_filter_args = NULL;
411 g_free ( priv->input_label ); priv->input_label = NULL;
412 g_free ( priv->user_string); priv->user_string = NULL;
413 G_OBJECT_CLASS(parent_class)->finalize(gob);
416 #define MAX_NUMBER_CODES 7
419 * Calculate individual elements (similarly to the VikWebtool Bounds & Center) for *all* potential values
420 * Then only values specified by the URL format are used in parameterizing the URL
422 static gchar *webtool_datasource_get_url ( VikWebtool *self, VikWindow *vw )
424 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
425 VikViewport *viewport = vik_window_viewport ( vw );
427 // Get top left and bottom right lat/lon pairs from the viewport
428 gdouble min_lat, max_lat, min_lon, max_lon;
429 gchar sminlon[G_ASCII_DTOSTR_BUF_SIZE];
430 gchar smaxlon[G_ASCII_DTOSTR_BUF_SIZE];
431 gchar sminlat[G_ASCII_DTOSTR_BUF_SIZE];
432 gchar smaxlat[G_ASCII_DTOSTR_BUF_SIZE];
433 vik_viewport_get_min_max_lat_lon ( viewport, &min_lat, &max_lat, &min_lon, &max_lon );
435 // Cannot simply use g_strdup_printf and gdouble due to locale.
436 // As we compute an URL, we have to think in C locale.
437 g_ascii_dtostr (sminlon, G_ASCII_DTOSTR_BUF_SIZE, min_lon);
438 g_ascii_dtostr (smaxlon, G_ASCII_DTOSTR_BUF_SIZE, max_lon);
439 g_ascii_dtostr (sminlat, G_ASCII_DTOSTR_BUF_SIZE, min_lat);
440 g_ascii_dtostr (smaxlat, G_ASCII_DTOSTR_BUF_SIZE, max_lat);
443 const VikCoord *coord = vik_viewport_get_center ( viewport );
445 vik_coord_to_latlon ( coord, &ll );
447 gchar scenterlat[G_ASCII_DTOSTR_BUF_SIZE];
448 gchar scenterlon[G_ASCII_DTOSTR_BUF_SIZE];
449 g_ascii_dtostr (scenterlat, G_ASCII_DTOSTR_BUF_SIZE, ll.lat);
450 g_ascii_dtostr (scenterlon, G_ASCII_DTOSTR_BUF_SIZE, ll.lon);
452 guint8 zoom = 17; // A zoomed in default
453 // zoom - ideally x & y factors need to be the same otherwise use the default
454 if ( vik_viewport_get_xmpp ( viewport ) == vik_viewport_get_ympp ( viewport ) )
455 zoom = map_utils_mpp_to_zoom_level ( vik_viewport_get_zoom ( viewport ) );
457 gchar szoom[G_ASCII_DTOSTR_BUF_SIZE];
458 g_snprintf ( szoom, G_ASCII_DTOSTR_BUF_SIZE, "%d", zoom );
461 if ( priv->url_format_code )
462 len = strlen ( priv->url_format_code );
463 if ( len > MAX_NUMBER_CODES )
464 len = MAX_NUMBER_CODES;
466 gchar* values[MAX_NUMBER_CODES];
468 for ( i = 0; i < MAX_NUMBER_CODES; i++ ) {
472 for ( i = 0; i < len; i++ ) {
473 switch ( g_ascii_toupper ( priv->url_format_code[i] ) ) {
474 case 'L': values[i] = g_strdup ( sminlon ); break;
475 case 'R': values[i] = g_strdup ( smaxlon ); break;
476 case 'B': values[i] = g_strdup ( sminlat ); break;
477 case 'T': values[i] = g_strdup ( smaxlat ); break;
478 case 'A': values[i] = g_strdup ( scenterlat ); break;
479 case 'O': values[i] = g_strdup ( scenterlon ); break;
480 case 'Z': values[i] = g_strdup ( szoom ); break;
481 case 'S': values[i] = g_strdup ( priv->user_string ); break;
486 gchar *url = g_strdup_printf ( priv->url, values[0], values[1], values[2], values[3], values[4], values[5], values[6] );
488 for ( i = 0; i < MAX_NUMBER_CODES; i++ ) {
489 if ( values[i] != '\0' )
490 g_free ( values[i] );
496 // NB Only works for ascii strings
497 char* strcasestr2(const char *dst, const char *src)
505 int len = strlen(src) - 1;
506 char sc = tolower(src[0]);
507 for(char dc = *dst; (dc = *dst); dst++) {
509 if(sc == dc && (len == 0 || !strncasecmp(dst+1, src+1, len)))
517 * Returns true if the URL format contains 'S' -- that is, a search term entry
518 * box needs to be displayed
520 static gboolean webtool_needs_user_string ( VikWebtool *self )
522 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
523 // For some reason (my) Windows build gets built with -D_GNU_SOURCE
524 #if (_GNU_SOURCE && !WINDOWS)
525 return (strcasestr(priv->url_format_code, "S") != NULL);
527 return (strcasestr2(priv->url_format_code, "S") != NULL);