#include <glib/gi18n.h>
#include "viking.h"
-#include "util.h"
#include "vikgototool.h"
#include "vikgoto.h"
+#include "ui_util.h"
+
+/* Compatibility */
+#if ! GLIB_CHECK_VERSION(2,22,0)
+#define g_mapped_file_unref g_mapped_file_free
+#endif
static gchar *last_goto_str = NULL;
static VikCoord *last_coord = NULL;
static GList *goto_tools_list = NULL;
-int last_goto_tool = 0;
+#define VIK_SETTINGS_GOTO_PROVIDER "goto_provider"
+int last_goto_tool = -1;
void vik_goto_register ( VikGotoTool *tool )
{
gtk_widget_destroy(dialog);
}
-static gboolean prompt_try_again(VikWindow *vw)
+static gboolean prompt_try_again(VikWindow *vw, const gchar *msg)
{
GtkWidget *dialog = NULL;
gboolean ret = TRUE;
dialog = gtk_dialog_new_with_buttons ( "", GTK_WINDOW(vw), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL );
gtk_window_set_title(GTK_WINDOW(dialog), _("goto"));
- GtkWidget *goto_label = gtk_label_new(_("I don't know that place. Do you want another goto?"));
- gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), goto_label, FALSE, FALSE, 5 );
+ GtkWidget *goto_label = gtk_label_new(msg);
+ gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), goto_label, FALSE, FALSE, 5 );
gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
gtk_widget_show_all(dialog);
return ret;
}
-static gchar * a_prompt_for_goto_string(VikWindow *vw)
+static gint find_entry = -1;
+static gint wanted_entry = -1;
+
+static void find_provider (gpointer elem, gpointer user_data)
+{
+ const gchar *name = vik_goto_tool_get_label (elem);
+ const gchar *provider = user_data;
+ find_entry++;
+ if (!strcmp(name, provider)) {
+ wanted_entry = find_entry;
+ }
+}
+
+/**
+ * Setup last_goto_tool value
+ */
+static void get_provider ()
+{
+ // Use setting for the provider if available
+ if ( last_goto_tool < 0 ) {
+ find_entry = -1;
+ wanted_entry = -1;
+ gchar *provider = NULL;
+ if ( a_settings_get_string ( VIK_SETTINGS_GOTO_PROVIDER, &provider ) ) {
+ // Use setting
+ if ( provider )
+ g_list_foreach (goto_tools_list, find_provider, provider);
+ // If not found set it to the first entry, otherwise use the entry
+ last_goto_tool = ( wanted_entry < 0 ) ? 0 : wanted_entry;
+ }
+ else
+ last_goto_tool = 0;
+ }
+}
+
+static void
+text_changed_cb (GtkEntry *entry,
+ GParamSpec *pspec,
+ GtkWidget *button)
+{
+ gboolean has_text = gtk_entry_get_text_length(entry) > 0;
+ gtk_entry_set_icon_sensitive ( entry, GTK_ENTRY_ICON_SECONDARY, has_text );
+ gtk_widget_set_sensitive ( button, has_text );
+}
+
+static gchar *a_prompt_for_goto_string(VikWindow *vw)
{
GtkWidget *dialog = NULL;
gtk_window_set_title(GTK_WINDOW(dialog), _("goto"));
GtkWidget *tool_label = gtk_label_new(_("goto provider:"));
- GtkWidget *tool_list = gtk_combo_box_new_text ();
-
+ GtkWidget *tool_list = vik_combo_box_text_new ();
GList *current = g_list_first (goto_tools_list);
while (current != NULL)
{
char *label = NULL;
VikGotoTool *tool = current->data;
label = vik_goto_tool_get_label (tool);
- gtk_combo_box_append_text ( GTK_COMBO_BOX( tool_list ), label);
+ vik_combo_box_text_append ( tool_list, label );
current = g_list_next (current);
}
- /* Set the previously selected provider as default */
- gtk_combo_box_set_active ( GTK_COMBO_BOX( tool_list ), last_goto_tool);
+
+ get_provider ();
+ gtk_combo_box_set_active ( GTK_COMBO_BOX( tool_list ), last_goto_tool );
GtkWidget *goto_label = gtk_label_new(_("Enter address or place name:"));
- GtkWidget *goto_entry = gtk_entry_new();
- if (last_goto_str)
- gtk_entry_set_text(GTK_ENTRY(goto_entry), last_goto_str);
-
- gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), tool_label, FALSE, FALSE, 5 );
- gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), tool_list, FALSE, FALSE, 5 );
- gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), goto_label, FALSE, FALSE, 5 );
- gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), goto_entry, FALSE, FALSE, 5 );
+ GtkWidget *goto_entry = ui_entry_new ( last_goto_str, GTK_ENTRY_ICON_SECONDARY );
+
+ // 'ok' when press return in the entry
+ g_signal_connect_swapped (goto_entry, "activate", G_CALLBACK(a_dialog_response_accept), dialog);
+
+#if GTK_CHECK_VERSION (2,20,0)
+ GtkWidget *ok_button = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
+ text_changed_cb ( GTK_ENTRY(goto_entry), NULL, ok_button );
+ g_signal_connect ( goto_entry, "notify::text", G_CALLBACK (text_changed_cb), ok_button );
+#endif
+ gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tool_label, FALSE, FALSE, 5 );
+ gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tool_list, FALSE, FALSE, 5 );
+ gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), goto_label, FALSE, FALSE, 5 );
+ gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), goto_entry, FALSE, FALSE, 5 );
gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
gtk_widget_show_all(dialog);
+ // Ensure the text field has focus so we can start typing straight away
+ gtk_widget_grab_focus ( goto_entry );
+
if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
gtk_widget_destroy(dialog);
return NULL;
}
- last_goto_tool = gtk_combo_box_get_active ( GTK_COMBO_BOX (tool_list) );
+ // TODO check if list is empty
+ last_goto_tool = gtk_combo_box_get_active ( GTK_COMBO_BOX(tool_list) );
+ gchar *provider = vik_goto_tool_get_label ( g_list_nth_data (goto_tools_list, last_goto_tool) );
+ a_settings_set_string ( VIK_SETTINGS_GOTO_PROVIDER, provider );
gchar *goto_str = g_strdup ( gtk_entry_get_text ( GTK_ENTRY(goto_entry) ) );
return(goto_str); /* goto_str needs to be freed by caller */
}
+/**
+ * Goto a place when we already have a string to search on
+ *
+ * Returns: %TRUE if a successful lookup
+ */
+static gboolean vik_goto_place ( VikWindow *vw, VikViewport *vvp, gchar* name, VikCoord *vcoord )
+{
+ // Ensure last_goto_tool is given a value
+ get_provider ();
+
+ if ( goto_tools_list ) {
+ VikGotoTool *gototool = g_list_nth_data ( goto_tools_list, last_goto_tool );
+ if ( gototool ) {
+ if ( vik_goto_tool_get_coord ( gototool, vw, vvp, name, vcoord ) == 0 )
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
void a_vik_goto(VikWindow *vw, VikViewport *vvp)
{
VikCoord new_center;
if ((!s_str) || (s_str[0] == 0)) {
more = FALSE;
}
-
- else if (!vik_goto_tool_get_coord(g_list_nth_data (goto_tools_list, last_goto_tool), vw, vvp, s_str, &new_center)) {
- if (last_coord)
- g_free(last_coord);
- last_coord = g_malloc(sizeof(VikCoord));
- *last_coord = new_center;
- if (last_successful_goto_str)
- g_free(last_successful_goto_str);
- last_successful_goto_str = g_strdup(last_goto_str);
- vik_viewport_set_center_coord(vvp, &new_center);
- more = FALSE;
- }
- else if (!prompt_try_again(vw))
+ else {
+ int ans = vik_goto_tool_get_coord(g_list_nth_data (goto_tools_list, last_goto_tool), vw, vvp, s_str, &new_center);
+ if ( ans == 0 ) {
+ if (last_coord)
+ g_free(last_coord);
+ last_coord = g_malloc(sizeof(VikCoord));
+ *last_coord = new_center;
+ if (last_successful_goto_str)
+ g_free(last_successful_goto_str);
+ last_successful_goto_str = g_strdup(last_goto_str);
+ vik_viewport_set_center_coord(vvp, &new_center, TRUE);
+ more = FALSE;
+ }
+ else if ( ans == -1 ) {
+ if (!prompt_try_again(vw, _("I don't know that place. Do you want another goto?")))
+ more = FALSE;
+ }
+ else if (!prompt_try_again(vw, _("Service request failure. Do you want another goto?")))
more = FALSE;
+ }
g_free(s_str);
} while (more);
}
+
+#define HOSTIP_LATITUDE_PATTERN "\"lat\":\""
+#define HOSTIP_LONGITUDE_PATTERN "\"lng\":\""
+#define HOSTIP_CITY_PATTERN "\"city\":\""
+#define HOSTIP_COUNTRY_PATTERN "\"country_name\":\""
+
+/**
+ * Automatic attempt to find out where you are using:
+ * 1. http://www.hostip.info ++
+ * 2. if not specific enough fallback to using the default goto tool with a country name
+ * ++ Using returned JSON information
+ * c.f. with googlesearch.c - similar implementation is used here
+ *
+ * returns:
+ * 0 if failed to locate anything
+ * 1 if exact latitude/longitude found
+ * 2 if position only as precise as a city
+ * 3 if position only as precise as a country
+ * @name: Contains the name of place found. Free this string after use.
+ */
+gint a_vik_goto_where_am_i ( VikViewport *vvp, struct LatLon *ll, gchar **name )
+{
+ gint result = 0;
+ *name = NULL;
+
+ gchar *tmpname = a_download_uri_to_tmp_file ( "http://api.hostip.info/get_json.php?position=true", NULL );
+ //gchar *tmpname = g_strdup ("../test/hostip2.json");
+ if (!tmpname) {
+ return result;
+ }
+
+ ll->lat = 0.0;
+ ll->lon = 0.0;
+
+ gchar *pat;
+ GMappedFile *mf;
+ gchar *ss;
+ gint fragment_len;
+
+ gchar lat_buf[32], lon_buf[32];
+ lat_buf[0] = lon_buf[0] = '\0';
+ gchar *country = NULL;
+ gchar *city = NULL;
+
+ if ((mf = g_mapped_file_new(tmpname, FALSE, NULL)) == NULL) {
+ g_critical(_("couldn't map temp file"));
+ goto tidy;
+ }
+
+ gsize len = g_mapped_file_get_length(mf);
+ gchar *text = g_mapped_file_get_contents(mf);
+
+ if ((pat = g_strstr_len(text, len, HOSTIP_COUNTRY_PATTERN))) {
+ pat += strlen(HOSTIP_COUNTRY_PATTERN);
+ fragment_len = 0;
+ ss = pat;
+ while (*pat != '"') {
+ fragment_len++;
+ pat++;
+ }
+ country = g_strndup(ss, fragment_len);
+ }
+
+ if ((pat = g_strstr_len(text, len, HOSTIP_CITY_PATTERN))) {
+ pat += strlen(HOSTIP_CITY_PATTERN);
+ fragment_len = 0;
+ ss = pat;
+ while (*pat != '"') {
+ fragment_len++;
+ pat++;
+ }
+ city = g_strndup(ss, fragment_len);
+ }
+
+ if ((pat = g_strstr_len(text, len, HOSTIP_LATITUDE_PATTERN))) {
+ pat += strlen(HOSTIP_LATITUDE_PATTERN);
+ ss = lat_buf;
+ if (*pat == '-')
+ *ss++ = *pat++;
+ while ((ss < (lat_buf + sizeof(lat_buf))) && (pat < (text + len)) &&
+ (g_ascii_isdigit(*pat) || (*pat == '.')))
+ *ss++ = *pat++;
+ *ss = '\0';
+ ll->lat = g_ascii_strtod(lat_buf, NULL);
+ }
+
+ if ((pat = g_strstr_len(text, len, HOSTIP_LONGITUDE_PATTERN))) {
+ pat += strlen(HOSTIP_LONGITUDE_PATTERN);
+ ss = lon_buf;
+ if (*pat == '-')
+ *ss++ = *pat++;
+ while ((ss < (lon_buf + sizeof(lon_buf))) && (pat < (text + len)) &&
+ (g_ascii_isdigit(*pat) || (*pat == '.')))
+ *ss++ = *pat++;
+ *ss = '\0';
+ ll->lon = g_ascii_strtod(lon_buf, NULL);
+ }
+
+ if ( ll->lat != 0.0 && ll->lon != 0.0 ) {
+ if ( ll->lat > -90.0 && ll->lat < 90.0 && ll->lon > -180.0 && ll->lon < 180.0 ) {
+ // Found a 'sensible' & 'precise' location
+ result = 1;
+ *name = g_strdup ( _("Locality") ); //Albeit maybe not known by an actual name!
+ }
+ }
+ else {
+ // Hopefully city name is unique enough to lookup position on
+ // Maybe for American places where hostip appends the State code on the end
+ // But if the country code is not appended if could easily get confused
+ // e.g. 'Portsmouth' could be at least
+ // Portsmouth, Hampshire, UK or
+ // Portsmouth, Viginia, USA.
+
+ // Try city name lookup
+ if ( city ) {
+ g_debug ( "%s: found city %s", __FUNCTION__, city );
+ if ( strcmp ( city, "(Unknown city)" ) != 0 ) {
+ VikCoord new_center;
+ if ( vik_goto_place ( NULL, vvp, city, &new_center ) ) {
+ // Got something
+ vik_coord_to_latlon ( &new_center, ll );
+ result = 2;
+ *name = city;
+ goto tidy;
+ }
+ }
+ }
+
+ // Try country name lookup
+ if ( country ) {
+ g_debug ( "%s: found country %s", __FUNCTION__, country );
+ if ( strcmp ( country, "(Unknown Country)" ) != 0 ) {
+ VikCoord new_center;
+ if ( vik_goto_place ( NULL, vvp, country, &new_center ) ) {
+ // Finally got something
+ vik_coord_to_latlon ( &new_center, ll );
+ result = 3;
+ *name = country;
+ goto tidy;
+ }
+ }
+ }
+ }
+
+ tidy:
+ g_mapped_file_unref ( mf );
+ (void)g_remove ( tmpname );
+ g_free ( tmpname );
+ return result;
+}