]> git.street.me.uk Git - andy/viking.git/blame - src/vikwebtool_datasource.c
Add option to auto connect to GPSD rather than having to manually control
[andy/viking.git] / src / vikwebtool_datasource.c
CommitLineData
82993cc7
RN
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2/*
3 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 *
17acdaec 5 * Copyright (C) 2013-2015, Rob Norris <rw_norris@hotmail.com>
82993cc7
RN
6 *
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.
11 *
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.
16 *
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
20 *
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "vikwebtool_datasource.h"
3c304fd0 28#include <ctype.h>
82993cc7
RN
29#include <string.h>
30
31#include <glib.h>
32#include <glib/gi18n.h>
33
34#include "globals.h"
35#include "acquire.h"
3957ef85 36#include "maputils.h"
9449ccf9 37#include "dialog.h"
82993cc7
RN
38
39static GObjectClass *parent_class;
290fa6d7 40static GHashTable *last_user_strings = NULL;
82993cc7
RN
41
42static void webtool_datasource_finalize ( GObject *gob );
43
44static gchar *webtool_datasource_get_url ( VikWebtool *self, VikWindow *vw );
45
1a8f3840 46static gboolean webtool_needs_user_string ( VikWebtool *self );
e16e573b 47
82993cc7
RN
48typedef struct _VikWebtoolDatasourcePrivate VikWebtoolDatasourcePrivate;
49
50struct _VikWebtoolDatasourcePrivate
51{
52 gchar *url;
53 gchar *url_format_code;
54 gchar *file_type;
19d749d8 55 gchar *babel_filter_args;
1a8f3840
MH
56 gchar *input_label;
57 gchar *user_string;
82993cc7
RN
58};
59
60#define WEBTOOL_DATASOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
61 VIK_WEBTOOL_DATASOURCE_TYPE, \
62 VikWebtoolDatasourcePrivate))
63
64G_DEFINE_TYPE (VikWebtoolDatasource, vik_webtool_datasource, VIK_WEBTOOL_TYPE)
65
66enum
67{
68 PROP_0,
69 PROP_URL,
70 PROP_URL_FORMAT_CODE,
71 PROP_FILE_TYPE,
1a8f3840
MH
72 PROP_BABEL_FILTER_ARGS,
73 PROP_INPUT_LABEL
82993cc7
RN
74};
75
76static void webtool_datasource_set_property (GObject *object,
77 guint property_id,
78 const GValue *value,
79 GParamSpec *pspec)
80{
81 VikWebtoolDatasource *self = VIK_WEBTOOL_DATASOURCE ( object );
82 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
83
84 switch ( property_id ) {
290fa6d7
MH
85
86 case PROP_URL:
82993cc7
RN
87 g_free ( priv->url );
88 priv->url = g_value_dup_string ( value );
89 g_debug ( "VikWebtoolDatasource.url: %s", priv->url );
90 break;
91
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 );
96 break;
97
98 case PROP_FILE_TYPE:
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 );
102 break;
103
19d749d8
MH
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 );
108 break;
109
1a8f3840
MH
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 );
114 break;
115
82993cc7
RN
116 default:
117 /* We don't have any other property... */
118 G_OBJECT_WARN_INVALID_PROPERTY_ID ( object, property_id, pspec );
119 break;
19d749d8 120 }
82993cc7
RN
121}
122
123static void webtool_datasource_get_property (GObject *object,
124 guint property_id,
125 GValue *value,
126 GParamSpec *pspec)
127{
128 VikWebtoolDatasource *self = VIK_WEBTOOL_DATASOURCE ( object );
129 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
130
131 switch ( property_id ) {
132
19d749d8
MH
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;
1a8f3840 137 case PROP_INPUT_LABEL: g_value_set_string ( value, priv->input_label ); break;
82993cc7
RN
138
139 default:
140 /* We don't have any other property... */
141 G_OBJECT_WARN_INVALID_PROPERTY_ID ( object, property_id, pspec );
142 break;
19d749d8 143 }
82993cc7
RN
144}
145
146typedef struct {
147 VikExtTool *self;
148 VikWindow *vw;
149 VikViewport *vvp;
1a8f3840 150 GtkWidget *user_string;
82993cc7
RN
151} datasource_t;
152
290fa6d7
MH
153
154static void ensure_last_user_strings_hash() {
155 if ( last_user_strings == NULL ) {
156 last_user_strings = g_hash_table_new_full ( g_str_hash,
157 g_str_equal,
158 g_free,
159 g_free );
160 }
161}
162
163
164static 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 );
168 g_free( label );
169 return last_str;
170}
171
172
173static 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 ),
177 g_strdup ( s ) );
178}
179
82993cc7
RN
180static gpointer datasource_init ( acq_vik_t *avt )
181{
182 datasource_t *data = g_malloc(sizeof(*data));
183 data->self = avt->userdata;
184 data->vw = avt->vw;
185 data->vvp = avt->vvp;
1a8f3840 186 data->user_string = NULL;
82993cc7
RN
187 return data;
188}
189
e16e573b
MH
190static void datasource_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data )
191{
192 datasource_t *widgets = (datasource_t *)user_data;
1a8f3840
MH
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 ( );
198
290fa6d7
MH
199 gchar *last_str = get_last_user_string ( widgets );
200 if ( last_str )
201 gtk_entry_set_text( GTK_ENTRY( widgets->user_string ), last_str );
e16e573b 202
9449ccf9
RN
203 // 'ok' when press return in the entry
204 g_signal_connect_swapped (widgets->user_string, "activate", G_CALLBACK(a_dialog_response_accept), dialog);
205
e16e573b
MH
206 /* Packing all widgets */
207 GtkBox *box = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
1a8f3840
MH
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 );
9449ccf9
RN
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 );
1a8f3840 214
1a8f3840 215 g_free ( label );
e16e573b
MH
216}
217
218
219
686baff0 220static void datasource_get_process_options ( gpointer user_data, ProcessOptions *po, DownloadFileOptions *options, const gchar *notused1, const gchar *notused2 )
82993cc7
RN
221{
222 datasource_t *data = (datasource_t*) user_data;
223
224 VikWebtool *vwd = VIK_WEBTOOL ( data->self );
e16e573b
MH
225 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( data->self );
226
1a8f3840
MH
227 if ( webtool_needs_user_string ( vwd ) ) {
228 priv->user_string = g_strdup ( gtk_entry_get_text ( GTK_ENTRY ( data->user_string ) ) );
229
230 if ( priv->user_string[0] != '\0' ) {
290fa6d7 231 set_last_user_string ( data, priv->user_string );
1a8f3840
MH
232 }
233 }
e16e573b 234
82993cc7
RN
235 gchar *url = vik_webtool_get_url ( vwd, data->vw );
236 g_debug ("%s: %s", __FUNCTION__, url );
237
17acdaec 238 po->url = g_strdup ( url );
82993cc7
RN
239
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
82993cc7
RN
244 gchar **parts = NULL;
245 if ( priv->file_type )
246 parts = g_strsplit ( priv->file_type, " ", 0);
247 if ( parts )
17acdaec 248 po->input_file_type = g_strdup ( parts[0] );
82993cc7 249 else
17acdaec 250 po->input_file_type = NULL;
82993cc7
RN
251 g_strfreev ( parts );
252
253 options = NULL;
17acdaec 254 po->babel_filters = priv->babel_filter_args;
82993cc7
RN
255}
256
257static void cleanup ( gpointer data )
258{
259 g_free ( data );
260}
261
262static void webtool_datasource_open ( VikExtTool *self, VikWindow *vw )
263{
1a8f3840 264 gboolean search = webtool_needs_user_string ( VIK_WEBTOOL ( self ) );
e16e573b 265
82993cc7
RN
266 // Use VikDataSourceInterface to give thready goodness controls of downloading stuff (i.e. can cancel the request)
267
268 // Can now create a 'VikDataSourceInterface' on the fly...
269 VikDataSourceInterface *vik_datasource_interface = g_malloc(sizeof(VikDataSourceInterface));
270
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
278 TRUE,
279 TRUE,
280 (VikDataSourceInitFunc) datasource_init,
281 (VikDataSourceCheckExistenceFunc) NULL,
e16e573b 282 (VikDataSourceAddSetupWidgetsFunc) (search ? datasource_add_setup_widgets : NULL),
17acdaec
RN
283 (VikDataSourceGetProcessOptionsFunc) datasource_get_process_options,
284 (VikDataSourceProcessFunc) a_babel_convert_from,
82993cc7
RN
285 (VikDataSourceProgressFunc) NULL,
286 (VikDataSourceAddProgressWidgetsFunc) NULL,
287 (VikDataSourceCleanupFunc) cleanup,
288 (VikDataSourceOffFunc) NULL,
289 NULL,
290 0,
291 NULL,
292 NULL,
293 0
294 };
295 memcpy ( vik_datasource_interface, &data, sizeof(VikDataSourceInterface) );
296
9cc13848 297 a_acquire ( vw, vik_window_layers_panel(vw), vik_window_viewport (vw), data.mode, vik_datasource_interface, self, cleanup );
82993cc7
RN
298}
299
300static void vik_webtool_datasource_class_init ( VikWebtoolDatasourceClass *klass )
301{
302 GObjectClass *gobject_class;
303 VikWebtoolClass *base_class;
304 GParamSpec *pspec;
305
306 gobject_class = G_OBJECT_CLASS (klass);
307
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;
311
312 pspec = g_param_spec_string ("url",
313 "Template 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,
318 PROP_URL,
319 pspec);
320
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,
328 pspec);
329
330 pspec = g_param_spec_string ("file_type",
331 "The file type expected",
332 "Set the file type",
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,
336 PROP_FILE_TYPE,
337 pspec);
338
19d749d8
MH
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",
1a8f3840 342 NULL, // default value
19d749d8
MH
343 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
344 g_object_class_install_property (gobject_class,
345 PROP_BABEL_FILTER_ARGS,
346 pspec);
347
1a8f3840
MH
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",
9449ccf9 351 _("Search Term"),
1a8f3840
MH
352 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
353 g_object_class_install_property (gobject_class,
354 PROP_INPUT_LABEL,
355 pspec);
19d749d8 356
82993cc7
RN
357 parent_class = g_type_class_peek_parent (klass);
358
359 base_class = VIK_WEBTOOL_CLASS ( klass );
360 base_class->get_url = webtool_datasource_get_url;
361
362 // Override default open function here:
363 VikExtToolClass *ext_tool_class = VIK_EXT_TOOL_CLASS ( klass );
364 ext_tool_class->open = webtool_datasource_open;
365
366 g_type_class_add_private (klass, sizeof (VikWebtoolDatasourcePrivate));
367}
368
369VikWebtoolDatasource *vik_webtool_datasource_new ()
370{
371 return VIK_WEBTOOL_DATASOURCE ( g_object_new ( VIK_WEBTOOL_DATASOURCE_TYPE, NULL ) );
372}
373
374VikWebtoolDatasource *vik_webtool_datasource_new_with_members ( const gchar *label,
375 const gchar *url,
376 const gchar *url_format_code,
19d749d8 377 const gchar *file_type,
1a8f3840
MH
378 const gchar *babel_filter_args,
379 const gchar *input_label )
82993cc7
RN
380{
381 VikWebtoolDatasource *result = VIK_WEBTOOL_DATASOURCE ( g_object_new ( VIK_WEBTOOL_DATASOURCE_TYPE,
382 "label", label,
383 "url", url,
384 "url_format_code", url_format_code,
385 "file_type", file_type,
19d749d8 386 "babel_filter_args", babel_filter_args,
1a8f3840 387 "input_label", input_label,
82993cc7
RN
388 NULL ) );
389
390 return result;
391}
392
393static void vik_webtool_datasource_init ( VikWebtoolDatasource *self )
394{
395 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE (self);
396 priv->url = NULL;
397 priv->url_format_code = NULL;
398 priv->file_type = NULL;
19d749d8 399 priv->babel_filter_args = NULL;
1a8f3840
MH
400 priv->input_label = NULL;
401 priv->user_string = NULL;
82993cc7
RN
402}
403
404static void webtool_datasource_finalize ( GObject *gob )
405{
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;
1a8f3840
MH
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;
82993cc7
RN
413 G_OBJECT_CLASS(parent_class)->finalize(gob);
414}
415
416#define MAX_NUMBER_CODES 7
417
418/**
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
421 */
422static gchar *webtool_datasource_get_url ( VikWebtool *self, VikWindow *vw )
423{
424 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
425 VikViewport *viewport = vik_window_viewport ( vw );
426
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 );
434
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);
441
442 // Center values
443 const VikCoord *coord = vik_viewport_get_center ( viewport );
444 struct LatLon ll;
445 vik_coord_to_latlon ( coord, &ll );
446
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);
451
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 ) )
3957ef85 455 zoom = map_utils_mpp_to_zoom_level ( vik_viewport_get_zoom ( viewport ) );
82993cc7
RN
456
457 gchar szoom[G_ASCII_DTOSTR_BUF_SIZE];
458 g_snprintf ( szoom, G_ASCII_DTOSTR_BUF_SIZE, "%d", zoom );
459
460 gint len = 0;
461 if ( priv->url_format_code )
462 len = strlen ( priv->url_format_code );
463 if ( len > MAX_NUMBER_CODES )
464 len = MAX_NUMBER_CODES;
465
466 gchar* values[MAX_NUMBER_CODES];
467 int i;
468 for ( i = 0; i < MAX_NUMBER_CODES; i++ ) {
644eea0e 469 values[i] = '\0';
82993cc7
RN
470 }
471
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;
1a8f3840 481 case 'S': values[i] = g_strdup ( priv->user_string ); break;
82993cc7
RN
482 default: break;
483 }
484 }
485
486 gchar *url = g_strdup_printf ( priv->url, values[0], values[1], values[2], values[3], values[4], values[5], values[6] );
487
488 for ( i = 0; i < MAX_NUMBER_CODES; i++ ) {
644eea0e
RN
489 if ( values[i] != '\0' )
490 g_free ( values[i] );
82993cc7
RN
491 }
492
493 return url;
494}
e16e573b 495
3c304fd0
RN
496// NB Only works for ascii strings
497char* strcasestr2(const char *dst, const char *src)
498{
499 if ( !dst || !src )
500 return NULL;
501
502 if(src[0] == '\0')
503 return (char*)dst;
504
505 int len = strlen(src) - 1;
506 char sc = tolower(src[0]);
507 for(char dc = *dst; (dc = *dst); dst++) {
508 dc = tolower(dc);
509 if(sc == dc && (len == 0 || !strncasecmp(dst+1, src+1, len)))
510 return (char*)dst;
511 }
512
513 return NULL;
514}
515
e16e573b
MH
516/**
517 * Returns true if the URL format contains 'S' -- that is, a search term entry
518 * box needs to be displayed
519 */
1a8f3840 520static gboolean webtool_needs_user_string ( VikWebtool *self )
e16e573b
MH
521{
522 VikWebtoolDatasourcePrivate *priv = WEBTOOL_DATASOURCE_GET_PRIVATE ( self );
3c304fd0
RN
523 // For some reason (my) Windows build gets built with -D_GNU_SOURCE
524#if (_GNU_SOURCE && !WINDOWS)
e16e573b 525 return (strcasestr(priv->url_format_code, "S") != NULL);
3c304fd0
RN
526#else
527 return (strcasestr2(priv->url_format_code, "S") != NULL);
528#endif
e16e573b 529}