2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2009, Guilhem Bonnefille <guilhem.bonnefille@gmail.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
29 #include <glib/gstdio.h>
30 #include <glib/gprintf.h>
31 #include <glib/gi18n.h>
34 #include "vikgototool.h"
38 #if ! GLIB_CHECK_VERSION(2,22,0)
39 #define g_mapped_file_unref g_mapped_file_free
42 static gchar *last_goto_str = NULL;
43 static VikCoord *last_coord = NULL;
44 static gchar *last_successful_goto_str = NULL;
46 static GList *goto_tools_list = NULL;
48 #define VIK_SETTINGS_GOTO_PROVIDER "goto_provider"
49 int last_goto_tool = -1;
51 void vik_goto_register ( VikGotoTool *tool )
53 if ( IS_VIK_GOTO_TOOL( tool ) )
54 goto_tools_list = g_list_append ( goto_tools_list, g_object_ref ( tool ) );
57 void vik_goto_unregister_all ()
59 g_list_foreach ( goto_tools_list, (GFunc) g_object_unref, NULL );
62 gchar * a_vik_goto_get_search_string_for_this_place(VikWindow *vw)
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)) {
70 return(last_successful_goto_str);
76 static void display_no_tool(VikWindow *vw)
78 GtkWidget *dialog = NULL;
80 dialog = gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("No goto tool available.") );
82 gtk_dialog_run ( GTK_DIALOG(dialog) );
84 gtk_widget_destroy(dialog);
87 static gboolean prompt_try_again(VikWindow *vw, const gchar *msg)
89 GtkWidget *dialog = NULL;
92 dialog = gtk_dialog_new_with_buttons ( "", GTK_WINDOW(vw), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL );
93 gtk_window_set_title(GTK_WINDOW(dialog), _("goto"));
95 GtkWidget *goto_label = gtk_label_new(msg);
96 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), goto_label, FALSE, FALSE, 5 );
97 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
98 gtk_widget_show_all(dialog);
100 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT )
103 gtk_widget_destroy(dialog);
107 static gint find_entry = -1;
108 static gint wanted_entry = -1;
110 static void find_provider (gpointer elem, gpointer user_data)
112 const gchar *name = vik_goto_tool_get_label (elem);
113 const gchar *provider = user_data;
115 if (!strcmp(name, provider)) {
116 wanted_entry = find_entry;
121 * Setup last_goto_tool value
123 static void get_provider ()
125 // Use setting for the provider if available
126 if ( last_goto_tool < 0 ) {
129 gchar *provider = NULL;
130 if ( a_settings_get_string ( VIK_SETTINGS_GOTO_PROVIDER, &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;
143 text_changed_cb (GtkEntry *entry,
147 gboolean has_text = gtk_entry_get_text_length(entry) > 0;
148 gtk_entry_set_icon_sensitive ( entry, GTK_ENTRY_ICON_SECONDARY, has_text );
149 gtk_widget_set_sensitive ( button, has_text );
152 static gchar *a_prompt_for_goto_string(VikWindow *vw)
154 GtkWidget *dialog = NULL;
156 dialog = gtk_dialog_new_with_buttons ( "", GTK_WINDOW(vw), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL );
157 gtk_window_set_title(GTK_WINDOW(dialog), _("goto"));
159 GtkWidget *tool_label = gtk_label_new(_("goto provider:"));
160 GtkWidget *tool_list = vik_combo_box_text_new ();
161 GList *current = g_list_first (goto_tools_list);
162 while (current != NULL)
165 VikGotoTool *tool = current->data;
166 label = vik_goto_tool_get_label (tool);
167 vik_combo_box_text_append ( tool_list, label );
168 current = g_list_next (current);
172 gtk_combo_box_set_active ( GTK_COMBO_BOX( tool_list ), last_goto_tool );
174 GtkWidget *goto_label = gtk_label_new(_("Enter address or place name:"));
175 GtkWidget *goto_entry = gtk_entry_new();
177 gtk_entry_set_text(GTK_ENTRY(goto_entry), last_goto_str);
179 // 'ok' when press return in the entry
180 g_signal_connect_swapped (goto_entry, "activate", G_CALLBACK(a_dialog_response_accept), dialog);
182 #if GTK_CHECK_VERSION (2,20,0)
183 GtkWidget *ok_button = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
184 text_changed_cb ( GTK_ENTRY(goto_entry), NULL, ok_button );
185 g_signal_connect ( goto_entry, "notify::text", G_CALLBACK (text_changed_cb), ok_button );
187 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tool_label, FALSE, FALSE, 5 );
188 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tool_list, FALSE, FALSE, 5 );
189 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), goto_label, FALSE, FALSE, 5 );
190 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), goto_entry, FALSE, FALSE, 5 );
191 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
192 gtk_widget_show_all(dialog);
194 // Ensure the text field has focus so we can start typing straight away
195 gtk_widget_grab_focus ( goto_entry );
197 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
198 gtk_widget_destroy(dialog);
202 // TODO check if list is empty
203 last_goto_tool = gtk_combo_box_get_active ( GTK_COMBO_BOX(tool_list) );
204 gchar *provider = vik_goto_tool_get_label ( g_list_nth_data (goto_tools_list, last_goto_tool) );
205 a_settings_set_string ( VIK_SETTINGS_GOTO_PROVIDER, provider );
207 gchar *goto_str = g_strdup ( gtk_entry_get_text ( GTK_ENTRY(goto_entry) ) );
209 gtk_widget_destroy(dialog);
211 if (goto_str[0] != '\0') {
213 g_free(last_goto_str);
214 last_goto_str = g_strdup(goto_str);
217 return(goto_str); /* goto_str needs to be freed by caller */
221 * Goto a place when we already have a string to search on
223 * Returns: %TRUE if a successful lookup
225 static gboolean vik_goto_place ( VikWindow *vw, VikViewport *vvp, gchar* name, VikCoord *vcoord )
227 // Ensure last_goto_tool is given a value
230 if ( goto_tools_list ) {
231 VikGotoTool *gototool = g_list_nth_data ( goto_tools_list, last_goto_tool );
233 if ( vik_goto_tool_get_coord ( gototool, vw, vvp, name, vcoord ) == 0 )
240 void a_vik_goto(VikWindow *vw, VikViewport *vvp)
244 gboolean more = TRUE;
246 if (goto_tools_list == NULL)
254 s_str = a_prompt_for_goto_string(vw);
255 if ((!s_str) || (s_str[0] == 0)) {
259 int ans = vik_goto_tool_get_coord(g_list_nth_data (goto_tools_list, last_goto_tool), vw, vvp, s_str, &new_center);
263 last_coord = g_malloc(sizeof(VikCoord));
264 *last_coord = new_center;
265 if (last_successful_goto_str)
266 g_free(last_successful_goto_str);
267 last_successful_goto_str = g_strdup(last_goto_str);
268 vik_viewport_set_center_coord(vvp, &new_center, TRUE);
271 else if ( ans == -1 ) {
272 if (!prompt_try_again(vw, _("I don't know that place. Do you want another goto?")))
275 else if (!prompt_try_again(vw, _("Service request failure. Do you want another goto?")))
282 #define HOSTIP_LATITUDE_PATTERN "\"lat\":\""
283 #define HOSTIP_LONGITUDE_PATTERN "\"lng\":\""
284 #define HOSTIP_CITY_PATTERN "\"city\":\""
285 #define HOSTIP_COUNTRY_PATTERN "\"country_name\":\""
288 * Automatic attempt to find out where you are using:
289 * 1. http://www.hostip.info ++
290 * 2. if not specific enough fallback to using the default goto tool with a country name
291 * ++ Using returned JSON information
292 * c.f. with googlesearch.c - similar implementation is used here
295 * 0 if failed to locate anything
296 * 1 if exact latitude/longitude found
297 * 2 if position only as precise as a city
298 * 3 if position only as precise as a country
299 * @name: Contains the name of place found. Free this string after use.
301 gint a_vik_goto_where_am_i ( VikViewport *vvp, struct LatLon *ll, gchar **name )
306 gchar *tmpname = a_download_uri_to_tmp_file ( "http://api.hostip.info/get_json.php?position=true", NULL );
307 //gchar *tmpname = g_strdup ("../test/hostip2.json");
320 gchar lat_buf[32], lon_buf[32];
321 lat_buf[0] = lon_buf[0] = '\0';
322 gchar *country = NULL;
325 if ((mf = g_mapped_file_new(tmpname, FALSE, NULL)) == NULL) {
326 g_critical(_("couldn't map temp file"));
330 gsize len = g_mapped_file_get_length(mf);
331 gchar *text = g_mapped_file_get_contents(mf);
333 if ((pat = g_strstr_len(text, len, HOSTIP_COUNTRY_PATTERN))) {
334 pat += strlen(HOSTIP_COUNTRY_PATTERN);
337 while (*pat != '"') {
341 country = g_strndup(ss, fragment_len);
344 if ((pat = g_strstr_len(text, len, HOSTIP_CITY_PATTERN))) {
345 pat += strlen(HOSTIP_CITY_PATTERN);
348 while (*pat != '"') {
352 city = g_strndup(ss, fragment_len);
355 if ((pat = g_strstr_len(text, len, HOSTIP_LATITUDE_PATTERN))) {
356 pat += strlen(HOSTIP_LATITUDE_PATTERN);
360 while ((ss < (lat_buf + sizeof(lat_buf))) && (pat < (text + len)) &&
361 (g_ascii_isdigit(*pat) || (*pat == '.')))
364 ll->lat = g_ascii_strtod(lat_buf, NULL);
367 if ((pat = g_strstr_len(text, len, HOSTIP_LONGITUDE_PATTERN))) {
368 pat += strlen(HOSTIP_LONGITUDE_PATTERN);
372 while ((ss < (lon_buf + sizeof(lon_buf))) && (pat < (text + len)) &&
373 (g_ascii_isdigit(*pat) || (*pat == '.')))
376 ll->lon = g_ascii_strtod(lon_buf, NULL);
379 if ( ll->lat != 0.0 && ll->lon != 0.0 ) {
380 if ( ll->lat > -90.0 && ll->lat < 90.0 && ll->lon > -180.0 && ll->lon < 180.0 ) {
381 // Found a 'sensible' & 'precise' location
383 *name = g_strdup ( _("Locality") ); //Albeit maybe not known by an actual name!
387 // Hopefully city name is unique enough to lookup position on
388 // Maybe for American places where hostip appends the State code on the end
389 // But if the country code is not appended if could easily get confused
390 // e.g. 'Portsmouth' could be at least
391 // Portsmouth, Hampshire, UK or
392 // Portsmouth, Viginia, USA.
394 // Try city name lookup
396 g_debug ( "%s: found city %s", __FUNCTION__, city );
397 if ( strcmp ( city, "(Unknown city)" ) != 0 ) {
399 if ( vik_goto_place ( NULL, vvp, city, &new_center ) ) {
401 vik_coord_to_latlon ( &new_center, ll );
409 // Try country name lookup
411 g_debug ( "%s: found country %s", __FUNCTION__, country );
412 if ( strcmp ( country, "(Unknown Country)" ) != 0 ) {
414 if ( vik_goto_place ( NULL, vvp, country, &new_center ) ) {
415 // Finally got something
416 vik_coord_to_latlon ( &new_center, ll );
426 g_mapped_file_unref ( mf );
427 (void)g_remove ( tmpname );