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, 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_cmd_string ( gpointer user_data, gchar **cmd, gchar **extra, DownloadMapOptions *options )
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 *cmd = 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 *extra = g_strdup ( parts[0] );
251 g_strfreev ( parts );
256 static gboolean datasource_process ( VikTrwLayer *vtl, const gchar *cmd, const gchar *extra, BabelStatusFunc status_cb, acq_dialog_widgets_t *adw, DownloadMapOptions *options )
258 datasource_t *data = (datasource_t *)adw->user_data;
259 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( data->self );
260 // Dependent on the ExtTool / what extra has been set to...
261 // When extra is NULL - then it interprets results as a GPX
262 gboolean result = a_babel_convert_from_url_filter ( vtl, cmd, extra, priv->babel_filter_args, status_cb, adw, options);
266 static void cleanup ( gpointer data )
271 static void webtool_datasource_open ( VikExtTool *self, VikWindow *vw )
273 gboolean search = webtool_needs_user_string ( VIK_WEBTOOL ( self ) );
275 // Use VikDataSourceInterface to give thready goodness controls of downloading stuff (i.e. can cancel the request)
277 // Can now create a 'VikDataSourceInterface' on the fly...
278 VikDataSourceInterface *vik_datasource_interface = g_malloc(sizeof(VikDataSourceInterface));
280 // An 'easy' way of assigning values
281 VikDataSourceInterface data = {
282 vik_ext_tool_get_label (self),
283 vik_ext_tool_get_label (self),
284 VIK_DATASOURCE_ADDTOLAYER,
285 VIK_DATASOURCE_INPUTTYPE_NONE,
286 FALSE, // Maintain current view - rather than setting it to the acquired points
289 (VikDataSourceInitFunc) datasource_init,
290 (VikDataSourceCheckExistenceFunc) NULL,
291 (VikDataSourceAddSetupWidgetsFunc) (search ? datasource_add_setup_widgets : NULL),
292 (VikDataSourceGetCmdStringFunc) datasource_get_cmd_string,
293 (VikDataSourceProcessFunc) datasource_process,
294 (VikDataSourceProgressFunc) NULL,
295 (VikDataSourceAddProgressWidgetsFunc) NULL,
296 (VikDataSourceCleanupFunc) cleanup,
297 (VikDataSourceOffFunc) NULL,
304 memcpy ( vik_datasource_interface, &data, sizeof(VikDataSourceInterface) );
306 a_acquire ( vw, vik_window_layers_panel(vw), vik_window_viewport (vw), data.mode, vik_datasource_interface, self, cleanup );
309 static void vik_webtool_datasource_class_init ( VikWebtoolDatasourceClass *klass )
311 GObjectClass *gobject_class;
312 VikWebtoolClass *base_class;
315 gobject_class = G_OBJECT_CLASS (klass);
317 gobject_class->finalize = webtool_datasource_finalize;
318 gobject_class->set_property = webtool_datasource_set_property;
319 gobject_class->get_property = webtool_datasource_get_property;
321 pspec = g_param_spec_string ("url",
323 "Set the template URL",
324 VIKING_URL /* default value */,
325 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
326 g_object_class_install_property (gobject_class,
330 pspec = g_param_spec_string ("url_format_code",
331 "Template URL Format Code",
332 "Set the template URL format code",
333 "LRBT", // default value
334 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
335 g_object_class_install_property (gobject_class,
336 PROP_URL_FORMAT_CODE,
339 pspec = g_param_spec_string ("file_type",
340 "The file type expected",
342 NULL, // default value ~ equates to internal GPX reading
343 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
344 g_object_class_install_property (gobject_class,
348 pspec = g_param_spec_string ("babel_filter_args",
349 "The command line filter options to pass to gpsbabel",
350 "Set the command line filter options for gpsbabel",
351 NULL, // default value
352 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
353 g_object_class_install_property (gobject_class,
354 PROP_BABEL_FILTER_ARGS,
357 pspec = g_param_spec_string ("input_label",
358 "The label for the user input box if input is required.",
359 "Set the label to be shown next to the user input box if an input term is required",
361 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
362 g_object_class_install_property (gobject_class,
366 parent_class = g_type_class_peek_parent (klass);
368 base_class = VIK_WEBTOOL_CLASS ( klass );
369 base_class->get_url = webtool_datasource_get_url;
371 // Override default open function here:
372 VikExtToolClass *ext_tool_class = VIK_EXT_TOOL_CLASS ( klass );
373 ext_tool_class->open = webtool_datasource_open;
375 g_type_class_add_private (klass, sizeof (VikWebtoolDatasourcePrivate));
378 VikWebtoolDatasource *vik_webtool_datasource_new ()
380 return VIK_WEBTOOL_DATASOURCE ( g_object_new ( VIK_WEBTOOL_DATASOURCE_TYPE, NULL ) );
383 VikWebtoolDatasource *vik_webtool_datasource_new_with_members ( const gchar *label,
385 const gchar *url_format_code,
386 const gchar *file_type,
387 const gchar *babel_filter_args,
388 const gchar *input_label )
390 VikWebtoolDatasource *result = VIK_WEBTOOL_DATASOURCE ( g_object_new ( VIK_WEBTOOL_DATASOURCE_TYPE,
393 "url_format_code", url_format_code,
394 "file_type", file_type,
395 "babel_filter_args", babel_filter_args,
396 "input_label", input_label,
402 static void vik_webtool_datasource_init ( VikWebtoolDatasource *self )
404 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE (self);
406 priv->url_format_code = NULL;
407 priv->file_type = NULL;
408 priv->babel_filter_args = NULL;
409 priv->input_label = NULL;
410 priv->user_string = NULL;
413 static void webtool_datasource_finalize ( GObject *gob )
415 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( gob );
416 g_free ( priv->url ); priv->url = NULL;
417 g_free ( priv->url_format_code ); priv->url_format_code = NULL;
418 g_free ( priv->file_type ); priv->file_type = NULL;
419 g_free ( priv->babel_filter_args ); priv->babel_filter_args = NULL;
420 g_free ( priv->input_label ); priv->input_label = NULL;
421 g_free ( priv->user_string); priv->user_string = NULL;
422 G_OBJECT_CLASS(parent_class)->finalize(gob);
425 #define MAX_NUMBER_CODES 7
428 * Calculate individual elements (similarly to the VikWebtool Bounds & Center) for *all* potential values
429 * Then only values specified by the URL format are used in parameterizing the URL
431 static gchar *webtool_datasource_get_url ( VikWebtool *self, VikWindow *vw )
433 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
434 VikViewport *viewport = vik_window_viewport ( vw );
436 // Get top left and bottom right lat/lon pairs from the viewport
437 gdouble min_lat, max_lat, min_lon, max_lon;
438 gchar sminlon[G_ASCII_DTOSTR_BUF_SIZE];
439 gchar smaxlon[G_ASCII_DTOSTR_BUF_SIZE];
440 gchar sminlat[G_ASCII_DTOSTR_BUF_SIZE];
441 gchar smaxlat[G_ASCII_DTOSTR_BUF_SIZE];
442 vik_viewport_get_min_max_lat_lon ( viewport, &min_lat, &max_lat, &min_lon, &max_lon );
444 // Cannot simply use g_strdup_printf and gdouble due to locale.
445 // As we compute an URL, we have to think in C locale.
446 g_ascii_dtostr (sminlon, G_ASCII_DTOSTR_BUF_SIZE, min_lon);
447 g_ascii_dtostr (smaxlon, G_ASCII_DTOSTR_BUF_SIZE, max_lon);
448 g_ascii_dtostr (sminlat, G_ASCII_DTOSTR_BUF_SIZE, min_lat);
449 g_ascii_dtostr (smaxlat, G_ASCII_DTOSTR_BUF_SIZE, max_lat);
452 const VikCoord *coord = vik_viewport_get_center ( viewport );
454 vik_coord_to_latlon ( coord, &ll );
456 gchar scenterlat[G_ASCII_DTOSTR_BUF_SIZE];
457 gchar scenterlon[G_ASCII_DTOSTR_BUF_SIZE];
458 g_ascii_dtostr (scenterlat, G_ASCII_DTOSTR_BUF_SIZE, ll.lat);
459 g_ascii_dtostr (scenterlon, G_ASCII_DTOSTR_BUF_SIZE, ll.lon);
461 guint8 zoom = 17; // A zoomed in default
462 // zoom - ideally x & y factors need to be the same otherwise use the default
463 if ( vik_viewport_get_xmpp ( viewport ) == vik_viewport_get_ympp ( viewport ) )
464 zoom = map_utils_mpp_to_zoom_level ( vik_viewport_get_zoom ( viewport ) );
466 gchar szoom[G_ASCII_DTOSTR_BUF_SIZE];
467 g_snprintf ( szoom, G_ASCII_DTOSTR_BUF_SIZE, "%d", zoom );
470 if ( priv->url_format_code )
471 len = strlen ( priv->url_format_code );
472 if ( len > MAX_NUMBER_CODES )
473 len = MAX_NUMBER_CODES;
475 gchar* values[MAX_NUMBER_CODES];
477 for ( i = 0; i < MAX_NUMBER_CODES; i++ ) {
481 for ( i = 0; i < len; i++ ) {
482 switch ( g_ascii_toupper ( priv->url_format_code[i] ) ) {
483 case 'L': values[i] = g_strdup ( sminlon ); break;
484 case 'R': values[i] = g_strdup ( smaxlon ); break;
485 case 'B': values[i] = g_strdup ( sminlat ); break;
486 case 'T': values[i] = g_strdup ( smaxlat ); break;
487 case 'A': values[i] = g_strdup ( scenterlat ); break;
488 case 'O': values[i] = g_strdup ( scenterlon ); break;
489 case 'Z': values[i] = g_strdup ( szoom ); break;
490 case 'S': values[i] = g_strdup ( priv->user_string ); break;
495 gchar *url = g_strdup_printf ( priv->url, values[0], values[1], values[2], values[3], values[4], values[5], values[6] );
497 for ( i = 0; i < MAX_NUMBER_CODES; i++ ) {
498 if ( values[i] != '\0' )
499 g_free ( values[i] );
505 // NB Only works for ascii strings
506 char* strcasestr2(const char *dst, const char *src)
514 int len = strlen(src) - 1;
515 char sc = tolower(src[0]);
516 for(char dc = *dst; (dc = *dst); dst++) {
518 if(sc == dc && (len == 0 || !strncasecmp(dst+1, src+1, len)))
526 * Returns true if the URL format contains 'S' -- that is, a search term entry
527 * box needs to be displayed
529 static gboolean webtool_needs_user_string ( VikWebtool *self )
531 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
532 // For some reason (my) Windows build gets built with -D_GNU_SOURCE
533 #if (_GNU_SOURCE && !WINDOWS)
534 return (strcasestr(priv->url_format_code, "S") != NULL);
536 return (strcasestr2(priv->url_format_code, "S") != NULL);