]> git.street.me.uk Git - andy/viking.git/blame - src/vikgoto.c
Add settings to override bfilter simplify and compress default values.
[andy/viking.git] / src / vikgoto.c
CommitLineData
6571a7de
GB
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
bfecd26e 5 * Copyright (C) 2009, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
6571a7de
GB
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 *
6571a7de
GB
21 */
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28#include <glib.h>
29#include <glib/gstdio.h>
30#include <glib/gprintf.h>
31#include <glib/gi18n.h>
32
33#include "viking.h"
34e71b99 34#include "vikgototool.h"
3d2c0c26 35#include "vikgoto.h"
6571a7de 36
a14f46cf
RN
37/* Compatibility */
38#if ! GLIB_CHECK_VERSION(2,22,0)
39#define g_mapped_file_unref g_mapped_file_free
40#endif
41
34e71b99 42static gchar *last_goto_str = NULL;
6571a7de 43static VikCoord *last_coord = NULL;
34e71b99 44static gchar *last_successful_goto_str = NULL;
6571a7de 45
34e71b99 46static GList *goto_tools_list = NULL;
6571a7de 47
d7c8636c
RN
48#define VIK_SETTINGS_GOTO_PROVIDER "goto_provider"
49int last_goto_tool = -1;
6571a7de 50
34e71b99 51void vik_goto_register ( VikGotoTool *tool )
6571a7de 52{
619a68f3
RN
53 if ( IS_VIK_GOTO_TOOL( tool ) )
54 goto_tools_list = g_list_append ( goto_tools_list, g_object_ref ( tool ) );
6571a7de
GB
55}
56
34e71b99 57void vik_goto_unregister_all ()
6571a7de 58{
34e71b99 59 g_list_foreach ( goto_tools_list, (GFunc) g_object_unref, NULL );
6571a7de
GB
60}
61
34e71b99 62gchar * a_vik_goto_get_search_string_for_this_place(VikWindow *vw)
6571a7de
GB
63{
64 if (!last_coord)
65 return NULL;
66
67 VikViewport *vvp = vik_window_viewport(vw);
68 const VikCoord *cur_center = vik_viewport_get_center(vvp);
69 if (vik_coord_equals(cur_center, last_coord)) {
34e71b99 70 return(last_successful_goto_str);
6571a7de
GB
71 }
72 else
73 return NULL;
74}
75
76static void display_no_tool(VikWindow *vw)
77{
78 GtkWidget *dialog = NULL;
79
34e71b99 80 dialog = gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("No goto tool available.") );
6571a7de
GB
81
82 gtk_dialog_run ( GTK_DIALOG(dialog) );
83
84 gtk_widget_destroy(dialog);
85}
86
79f34e27 87static gboolean prompt_try_again(VikWindow *vw, const gchar *msg)
6571a7de
GB
88{
89 GtkWidget *dialog = NULL;
90 gboolean ret = TRUE;
91
92 dialog = gtk_dialog_new_with_buttons ( "", GTK_WINDOW(vw), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL );
34e71b99 93 gtk_window_set_title(GTK_WINDOW(dialog), _("goto"));
6571a7de 94
79f34e27 95 GtkWidget *goto_label = gtk_label_new(msg);
9b082b39 96 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), goto_label, FALSE, FALSE, 5 );
5b7d7928 97 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
6571a7de
GB
98 gtk_widget_show_all(dialog);
99
100 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT )
101 ret = FALSE;
102
103 gtk_widget_destroy(dialog);
104 return ret;
105}
106
d7c8636c
RN
107static gint find_entry = -1;
108static gint wanted_entry = -1;
109
110static void find_provider (gpointer elem, gpointer user_data)
111{
112 const gchar *name = vik_goto_tool_get_label (elem);
113 const gchar *provider = user_data;
114 find_entry++;
115 if (!strcmp(name, provider)) {
116 wanted_entry = find_entry;
117 }
118}
119
a14f46cf
RN
120/**
121 * Setup last_goto_tool value
122 */
123static void get_provider ()
124{
125 // Use setting for the provider if available
126 if ( last_goto_tool < 0 ) {
127 find_entry = -1;
128 wanted_entry = -1;
129 gchar *provider = NULL;
130 if ( a_settings_get_string ( VIK_SETTINGS_GOTO_PROVIDER, &provider ) ) {
131 // Use setting
132 if ( provider )
133 g_list_foreach (goto_tools_list, find_provider, provider);
134 // If not found set it to the first entry, otherwise use the entry
135 last_goto_tool = ( wanted_entry < 0 ) ? 0 : wanted_entry;
136 }
137 else
138 last_goto_tool = 0;
139 }
140}
141
d7c8636c 142static gchar *a_prompt_for_goto_string(VikWindow *vw)
6571a7de
GB
143{
144 GtkWidget *dialog = NULL;
145
146 dialog = gtk_dialog_new_with_buttons ( "", GTK_WINDOW(vw), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL );
34e71b99 147 gtk_window_set_title(GTK_WINDOW(dialog), _("goto"));
6571a7de 148
34e71b99 149 GtkWidget *tool_label = gtk_label_new(_("goto provider:"));
1bc1c05b 150 GtkWidget *tool_list = vik_combo_box_text_new ();
34e71b99 151 GList *current = g_list_first (goto_tools_list);
6571a7de
GB
152 while (current != NULL)
153 {
154 char *label = NULL;
34e71b99
GB
155 VikGotoTool *tool = current->data;
156 label = vik_goto_tool_get_label (tool);
1bc1c05b 157 vik_combo_box_text_append ( tool_list, label );
6571a7de
GB
158 current = g_list_next (current);
159 }
d7c8636c 160
a14f46cf 161 get_provider ();
d7c8636c 162 gtk_combo_box_set_active ( GTK_COMBO_BOX( tool_list ), last_goto_tool );
6571a7de 163
34e71b99
GB
164 GtkWidget *goto_label = gtk_label_new(_("Enter address or place name:"));
165 GtkWidget *goto_entry = gtk_entry_new();
166 if (last_goto_str)
167 gtk_entry_set_text(GTK_ENTRY(goto_entry), last_goto_str);
6571a7de 168
ae1d3fd1
RN
169 // 'ok' when press return in the entry
170 g_signal_connect_swapped (goto_entry, "activate", G_CALLBACK(a_dialog_response_accept), dialog);
171
9b082b39
RN
172 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tool_label, FALSE, FALSE, 5 );
173 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tool_list, FALSE, FALSE, 5 );
174 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), goto_label, FALSE, FALSE, 5 );
175 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), goto_entry, FALSE, FALSE, 5 );
5b7d7928 176 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
6571a7de
GB
177 gtk_widget_show_all(dialog);
178
ae1d3fd1
RN
179 // Ensure the text field has focus so we can start typing straight away
180 gtk_widget_grab_focus ( goto_entry );
181
6571a7de
GB
182 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
183 gtk_widget_destroy(dialog);
184 return NULL;
185 }
186
a14f46cf 187 // TODO check if list is empty
d7c8636c
RN
188 last_goto_tool = gtk_combo_box_get_active ( GTK_COMBO_BOX(tool_list) );
189 gchar *provider = vik_goto_tool_get_label ( g_list_nth_data (goto_tools_list, last_goto_tool) );
190 a_settings_set_string ( VIK_SETTINGS_GOTO_PROVIDER, provider );
6571a7de 191
34e71b99 192 gchar *goto_str = g_strdup ( gtk_entry_get_text ( GTK_ENTRY(goto_entry) ) );
6571a7de
GB
193
194 gtk_widget_destroy(dialog);
195
34e71b99
GB
196 if (goto_str[0] != '\0') {
197 if (last_goto_str)
198 g_free(last_goto_str);
199 last_goto_str = g_strdup(goto_str);
6571a7de
GB
200 }
201
34e71b99 202 return(goto_str); /* goto_str needs to be freed by caller */
6571a7de
GB
203}
204
a14f46cf
RN
205/**
206 * Goto a place when we already have a string to search on
207 *
208 * Returns: %TRUE if a successful lookup
209 */
210static gboolean vik_goto_place ( VikWindow *vw, VikViewport *vvp, gchar* name, VikCoord *vcoord )
211{
212 // Ensure last_goto_tool is given a value
213 get_provider ();
214
215 if ( goto_tools_list ) {
216 VikGotoTool *gototool = g_list_nth_data ( goto_tools_list, last_goto_tool );
217 if ( gototool ) {
218 if ( vik_goto_tool_get_coord ( gototool, vw, vvp, name, vcoord ) == 0 )
219 return TRUE;
220 }
221 }
222 return FALSE;
223}
224
c36a079a 225void a_vik_goto(VikWindow *vw, VikViewport *vvp)
6571a7de
GB
226{
227 VikCoord new_center;
228 gchar *s_str;
229 gboolean more = TRUE;
230
34e71b99 231 if (goto_tools_list == NULL)
6571a7de
GB
232 {
233 /* Empty list */
234 display_no_tool(vw);
235 return;
236 }
237
238 do {
34e71b99 239 s_str = a_prompt_for_goto_string(vw);
6571a7de
GB
240 if ((!s_str) || (s_str[0] == 0)) {
241 more = FALSE;
242 }
79f34e27
RN
243 else {
244 int ans = vik_goto_tool_get_coord(g_list_nth_data (goto_tools_list, last_goto_tool), vw, vvp, s_str, &new_center);
245 if ( ans == 0 ) {
246 if (last_coord)
247 g_free(last_coord);
248 last_coord = g_malloc(sizeof(VikCoord));
249 *last_coord = new_center;
250 if (last_successful_goto_str)
251 g_free(last_successful_goto_str);
252 last_successful_goto_str = g_strdup(last_goto_str);
253 vik_viewport_set_center_coord(vvp, &new_center, TRUE);
6571a7de 254 more = FALSE;
79f34e27
RN
255 }
256 else if ( ans == -1 ) {
257 if (!prompt_try_again(vw, _("I don't know that place. Do you want another goto?")))
258 more = FALSE;
259 }
260 else if (!prompt_try_again(vw, _("Service request failure. Do you want another goto?")))
261 more = FALSE;
262 }
6571a7de
GB
263 g_free(s_str);
264 } while (more);
265}
a14f46cf
RN
266
267#define HOSTIP_LATITUDE_PATTERN "\"lat\":\""
268#define HOSTIP_LONGITUDE_PATTERN "\"lng\":\""
269#define HOSTIP_CITY_PATTERN "\"city\":\""
270#define HOSTIP_COUNTRY_PATTERN "\"country_name\":\""
271
272/**
273 * Automatic attempt to find out where you are using:
274 * 1. http://www.hostip.info ++
275 * 2. if not specific enough fallback to using the default goto tool with a country name
276 * ++ Using returned JSON information
277 * c.f. with googlesearch.c - similar implementation is used here
278 *
279 * returns:
280 * 0 if failed to locate anything
281 * 1 if exact latitude/longitude found
282 * 2 if position only as precise as a city
283 * 3 if position only as precise as a country
284 * @name: Contains the name of place found. Free this string after use.
285 */
286gint a_vik_goto_where_am_i ( VikViewport *vvp, struct LatLon *ll, gchar **name )
287{
288 gint result = 0;
289 *name = NULL;
290
291 gchar *tmpname = a_download_uri_to_tmp_file ( "http://api.hostip.info/get_json.php?position=true", NULL );
292 //gchar *tmpname = g_strdup ("../test/hostip2.json");
293 if (!tmpname) {
294 return result;
295 }
296
297 ll->lat = 0.0;
298 ll->lon = 0.0;
299
300 gchar *pat;
301 GMappedFile *mf;
302 gchar *ss;
303 gint fragment_len;
304
305 gchar lat_buf[32], lon_buf[32];
306 lat_buf[0] = lon_buf[0] = '\0';
cf087260
RN
307 gchar *country = NULL;
308 gchar *city = NULL;
a14f46cf
RN
309
310 if ((mf = g_mapped_file_new(tmpname, FALSE, NULL)) == NULL) {
311 g_critical(_("couldn't map temp file"));
312 goto tidy;
313 }
314
315 gsize len = g_mapped_file_get_length(mf);
316 gchar *text = g_mapped_file_get_contents(mf);
317
318 if ((pat = g_strstr_len(text, len, HOSTIP_COUNTRY_PATTERN))) {
319 pat += strlen(HOSTIP_COUNTRY_PATTERN);
320 fragment_len = 0;
321 ss = pat;
322 while (*pat != '"') {
323 fragment_len++;
324 pat++;
325 }
326 country = g_strndup(ss, fragment_len);
327 }
328
329 if ((pat = g_strstr_len(text, len, HOSTIP_CITY_PATTERN))) {
330 pat += strlen(HOSTIP_CITY_PATTERN);
331 fragment_len = 0;
332 ss = pat;
333 while (*pat != '"') {
334 fragment_len++;
335 pat++;
336 }
337 city = g_strndup(ss, fragment_len);
338 }
339
340 if ((pat = g_strstr_len(text, len, HOSTIP_LATITUDE_PATTERN))) {
341 pat += strlen(HOSTIP_LATITUDE_PATTERN);
342 ss = lat_buf;
343 if (*pat == '-')
344 *ss++ = *pat++;
345 while ((ss < (lat_buf + sizeof(lat_buf))) && (pat < (text + len)) &&
346 (g_ascii_isdigit(*pat) || (*pat == '.')))
347 *ss++ = *pat++;
348 *ss = '\0';
349 ll->lat = g_ascii_strtod(lat_buf, NULL);
350 }
351
352 if ((pat = g_strstr_len(text, len, HOSTIP_LONGITUDE_PATTERN))) {
353 pat += strlen(HOSTIP_LONGITUDE_PATTERN);
354 ss = lon_buf;
355 if (*pat == '-')
356 *ss++ = *pat++;
357 while ((ss < (lon_buf + sizeof(lon_buf))) && (pat < (text + len)) &&
358 (g_ascii_isdigit(*pat) || (*pat == '.')))
359 *ss++ = *pat++;
360 *ss = '\0';
361 ll->lon = g_ascii_strtod(lon_buf, NULL);
362 }
363
364 if ( ll->lat != 0.0 && ll->lon != 0.0 ) {
365 if ( ll->lat > -90.0 && ll->lat < 90.0 && ll->lon > -180.0 && ll->lon < 180.0 ) {
366 // Found a 'sensible' & 'precise' location
367 result = 1;
368 *name = g_strdup ( _("Locality") ); //Albeit maybe not known by an actual name!
369 }
370 }
371 else {
372 // Hopefully city name is unique enough to lookup position on
373 // Maybe for American places where hostip appends the State code on the end
374 // But if the country code is not appended if could easily get confused
375 // e.g. 'Portsmouth' could be at least
376 // Portsmouth, Hampshire, UK or
377 // Portsmouth, Viginia, USA.
378
379 // Try city name lookup
380 if ( city ) {
381 g_debug ( "%s: found city %s", __FUNCTION__, city );
382 if ( strcmp ( city, "(Unknown city)" ) != 0 ) {
383 VikCoord new_center;
384 if ( vik_goto_place ( NULL, vvp, city, &new_center ) ) {
385 // Got something
386 vik_coord_to_latlon ( &new_center, ll );
387 result = 2;
388 *name = city;
389 goto tidy;
390 }
391 }
392 }
393
394 // Try country name lookup
395 if ( country ) {
396 g_debug ( "%s: found country %s", __FUNCTION__, country );
397 if ( strcmp ( country, "(Unknown Country)" ) != 0 ) {
398 VikCoord new_center;
399 if ( vik_goto_place ( NULL, vvp, country, &new_center ) ) {
400 // Finally got something
401 vik_coord_to_latlon ( &new_center, ll );
402 result = 3;
403 *name = country;
404 goto tidy;
405 }
406 }
407 }
408 }
409
410 tidy:
411 g_mapped_file_unref ( mf );
80586339 412 (void)g_remove ( tmpname );
a14f46cf
RN
413 g_free ( tmpname );
414 return result;
415}