]> git.street.me.uk Git - andy/viking.git/blobdiff - src/vikgoto.c
Some explicit setting of dirpath values to NULL.
[andy/viking.git] / src / vikgoto.c
index 61bb22ffc1233b61a462128dad87bc268fc1ff40..ba77b0bb9eed09b1984eec19f53ed11ce65b80cc 100644 (file)
 #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;
@@ -41,7 +46,8 @@ static gchar *last_successful_goto_str = 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 )
 {
@@ -79,7 +85,7 @@ static void display_no_tool(VikWindow *vw)
   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;
@@ -87,7 +93,7 @@ static gboolean prompt_try_again(VikWindow *vw)
   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?"));
+  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);
@@ -99,7 +105,52 @@ static gboolean prompt_try_again(VikWindow *vw)
   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;
 
@@ -107,28 +158,31 @@ static gchar *  a_prompt_for_goto_string(VikWindow *vw)
   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);
+  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 );
@@ -144,7 +198,10 @@ static gchar *  a_prompt_for_goto_string(VikWindow *vw)
     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) ) );
 
@@ -159,6 +216,26 @@ static gchar *  a_prompt_for_goto_string(VikWindow *vw)
   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;
@@ -177,20 +254,176 @@ void a_vik_goto(VikWindow *vw, VikViewport *vvp)
     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 JSON_LATITUDE_PATTERN "\"geoplugin_latitude\":\""
+#define JSON_LONGITUDE_PATTERN "\"geoplugin_longitude\":\""
+#define JSON_CITY_PATTERN "\"geoplugin_city\":\""
+#define JSON_COUNTRY_PATTERN "\"geoplugin_countryName\":\""
+
+/**
+ * Automatic attempt to find out where you are using:
+ *   1. http://www.geoplugin.com ++
+ *   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://www.geoplugin.net/json.gp", NULL );
+  //gchar *tmpname = g_strdup ("../test/www.geoplugin.net-slash-json.gp.result");
+  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, JSON_COUNTRY_PATTERN))) {
+    pat += strlen(JSON_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, JSON_CITY_PATTERN))) {
+    pat += strlen(JSON_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, JSON_LATITUDE_PATTERN))) {
+    pat += strlen(JSON_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, JSON_LONGITUDE_PATTERN))) {
+    pat += strlen(JSON_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
+    // For American places the service may append 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;
+}