[geocode-glib] lib: Add GeocodeForward



commit 9ac3bfb98fa0e4627123a233666a3ec23e6d3fb9
Author: Bastien Nocera <hadess hadess net>
Date:   Sat Nov 24 19:09:25 2012 +0100

    lib: Add GeocodeForward
    
    The description generation isn't implemented yet though.

 geocode-glib/Makefile.am          |    4 +-
 geocode-glib/geocode-forward.c    |  738 +++++++++++++++++++++++++++++++++++++
 geocode-glib/geocode-forward.h    |   78 ++++
 geocode-glib/geocode-glib.h       |    1 +
 geocode-glib/geocode-glib.symbols |    7 +
 5 files changed, 827 insertions(+), 1 deletions(-)
---
diff --git a/geocode-glib/Makefile.am b/geocode-glib/Makefile.am
index 7e951bd..be168ad 100644
--- a/geocode-glib/Makefile.am
+++ b/geocode-glib/Makefile.am
@@ -8,6 +8,8 @@ lib_LTLIBRARIES = libgeocode-glib.la
 public_files =						\
 	geocode-location.c				\
 	geocode-location.h				\
+	geocode-forward.c				\
+	geocode-forward.h				\
 	geocode-glib.c					\
 	geocode-glib.h					\
 	geocode-error.c					\
@@ -26,7 +28,7 @@ libgeocode_glib_la_LDFLAGS =				\
 	-export-symbols $(srcdir)/geocode-glib.symbols
 
 gcglibdir = $(pkgincludedir)
-gcglib_HEADERS = geocode-glib.h geocode-location.h
+gcglib_HEADERS = geocode-glib.h geocode-location.h geocode-forward.h
 
 AM_CFLAGS = -I$(top_srcdir) $(GEOCODE_CFLAGS) $(COMMON_CFLAGS) $(WARN_CFLAGS) $(DISABLE_DEPRECATED)
 
diff --git a/geocode-glib/geocode-forward.c b/geocode-glib/geocode-forward.c
new file mode 100644
index 0000000..9150e73
--- /dev/null
+++ b/geocode-glib/geocode-forward.c
@@ -0,0 +1,738 @@
+/*
+   Copyright (C) 2011 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301  USA.
+
+   Authors: Bastien Nocera <hadess hadess net>
+
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <gio/gio.h>
+#include <json-glib/json-glib.h>
+#include <libsoup/soup.h>
+#include <geocode-glib/geocode-forward.h>
+#include <geocode-glib/geocode-error.h>
+#include <geocode-glib/geocode-glib-private.h>
+
+/**
+ * SECTION:geocode-forward
+ * @short_description: Geocode forward geocoding object
+ * @include: geocode-glib/geocode-glib.h
+ *
+ * Contains functions for forward geocoding using the
+ * <ulink url="http://developer.yahoo.com/geo/geoplanet/";>Yahoo! GeoPlanet APIs</ulink>.
+ **/
+
+struct _GeocodeForwardPrivate {
+	GHashTable *ht;
+	guint       answer_count;
+};
+
+G_DEFINE_TYPE (GeocodeForward, geocode_forward, G_TYPE_OBJECT)
+
+#define DEFAULT_ANSWER_COUNT 10
+
+static void geocode_forward_add (GeocodeForward *forward,
+				 const char     *key,
+				 const char     *value);
+static GList *__geocode_parse_search_json (const char *contents,
+					  GError    **error);
+
+static void
+geocode_forward_finalize (GObject *gforward)
+{
+	GeocodeForward *forward = (GeocodeForward *) gforward;
+
+	g_clear_pointer (&forward->priv->ht, g_hash_table_destroy);
+
+	G_OBJECT_CLASS (geocode_forward_parent_class)->finalize (gforward);
+}
+
+static void
+geocode_forward_class_init (GeocodeForwardClass *klass)
+{
+	GObjectClass *gforward_class = G_OBJECT_CLASS (klass);
+
+	gforward_class->finalize = geocode_forward_finalize;
+
+	g_type_class_add_private (klass, sizeof (GeocodeForwardPrivate));
+}
+
+static void
+geocode_forward_init (GeocodeForward *forward)
+{
+	forward->priv = G_TYPE_INSTANCE_GET_PRIVATE ((forward), GEOCODE_TYPE_FORWARD, GeocodeForwardPrivate);
+	forward->priv->ht = g_hash_table_new_full (g_str_hash, g_str_equal,
+						   g_free, g_free);
+	forward->priv->answer_count = DEFAULT_ANSWER_COUNT;
+}
+
+static gboolean
+parse_lang (const char *locale,
+	    char      **language_codep,
+	    char      **territory_codep)
+{
+	GRegex     *re;
+	GMatchInfo *match_info;
+	gboolean    res;
+	GError     *error;
+	gboolean    retval;
+
+	match_info = NULL;
+	retval = FALSE;
+
+	error = NULL;
+	re = g_regex_new ("^(?P<language>[^_  [:space:]]+)"
+			  "(_(?P<territory>[[:upper:]]+))?"
+			  "(\\.(?P<codeset>[-_0-9a-zA-Z]+))?"
+			  "(@(?P<modifier>[[:ascii:]]+))?$",
+			  0, 0, &error);
+	if (re == NULL) {
+		g_warning ("%s", error->message);
+		goto out;
+	}
+
+	if (!g_regex_match (re, locale, 0, &match_info) ||
+	    g_match_info_is_partial_match (match_info)) {
+		g_warning ("locale '%s' isn't valid\n", locale);
+		goto out;
+	}
+
+	res = g_match_info_matches (match_info);
+	if (! res) {
+		g_warning ("Unable to parse locale: %s", locale);
+		goto out;
+	}
+
+	retval = TRUE;
+
+	*language_codep = g_match_info_fetch_named (match_info, "language");
+
+	*territory_codep = g_match_info_fetch_named (match_info, "territory");
+
+	if (*territory_codep != NULL &&
+	    *territory_codep[0] == '\0') {
+		g_free (*territory_codep);
+		*territory_codep = NULL;
+	}
+
+out:
+	g_match_info_free (match_info);
+	g_regex_unref (re);
+
+	return retval;
+}
+
+static char *
+geocode_forward_get_lang_for_locale (const char *locale)
+{
+	char *lang;
+	char *territory;
+
+	if (parse_lang (locale, &lang, &territory) == FALSE)
+		return NULL;
+
+	return g_strdup_printf ("%s%s%s",
+				lang,
+				territory ? "_" : "",
+				territory ? territory : "");
+}
+
+static char *
+geocode_forward_get_lang (void)
+{
+	return geocode_forward_get_lang_for_locale (setlocale (LC_MESSAGES, NULL));
+}
+
+static struct {
+	const char *tp_attr;
+	const char *gc_attr; /* NULL to ignore */
+} attrs_map[] = {
+	{ "countrycode", NULL },
+	{ "country", "country" },
+	{ "region", "state" },
+	{ "locality", "city" },
+	{ "area", "neighborhood" },
+	{ "postalcode", "postal" },
+	{ "street", "street" },
+	{ "building", "house" },
+	{ "floor", "" },
+	{ "room", "unit" },
+	{ "text", NULL },
+	{ "description", NULL },
+	{ "uri", NULL },
+	{ "language", "locale" },
+};
+
+static void
+geocode_forward_fill_params (GeocodeForward *forward,
+			    GHashTable    *params,
+			    gboolean       value_is_str)
+{
+	guint i;
+
+	for (i = 0; i < G_N_ELEMENTS (attrs_map); i++) {
+		const char *str;
+
+		if (attrs_map[i].gc_attr == NULL)
+			continue;
+
+		if (value_is_str == FALSE) {
+			GValue *value;
+
+			value = g_hash_table_lookup (params, attrs_map[i].tp_attr);
+			if (value == NULL)
+				continue;
+
+			str = g_value_get_string (value);
+		} else {
+			str = g_hash_table_lookup (params, attrs_map[i].tp_attr);
+		}
+
+		if (str == NULL)
+			continue;
+
+		geocode_forward_add (forward,
+				    attrs_map[i].gc_attr,
+				    str);
+	}
+}
+
+/**
+ * geocode_forward_new_for_params:
+ * @params: (transfer none) (element-type utf8 GValue): a #GHashTable with string keys, and #GValue values.
+ *
+ * Creates a new #GeocodeForward to perform geocoding with. The
+ * #GHashTable is in the format used by Telepathy, and documented
+ * on <ulink url="http://telepathy.freedesktop.org/spec/Connection_Interface_Location.html#Mapping:Location";>Telepathy's specification site</ulink>.
+ *
+ * See also: <ulink url="http://xmpp.org/extensions/xep-0080.html";>XEP-0080 specification</ulink>.
+ *
+ * Returns: a new #GeocodeForward. Use g_object_unref() when done.
+ **/
+GeocodeForward *
+geocode_forward_new_for_params (GHashTable *params)
+{
+	GeocodeForward *forward;
+
+	g_return_val_if_fail (params != NULL, NULL);
+
+	if (g_hash_table_lookup (params, "lat") != NULL &&
+	    g_hash_table_lookup (params, "long") != NULL) {
+		g_warning ("You already have longitude and latitude in those parameters");
+	}
+
+	forward = g_object_new (GEOCODE_TYPE_FORWARD, NULL);
+	geocode_forward_fill_params (forward, params, FALSE);
+
+	return forward;
+}
+
+/**
+ * geocode_forward_new_for_string:
+ * @str: a string containing a free-form description of the location
+ *
+ * Creates a new #GeocodeForward to perform geocoding with. The
+ * string is in free-form format.
+ *
+ * Returns: a new #GeocodeForward. Use g_object_unref() when done.
+ **/
+GeocodeForward *
+geocode_forward_new_for_string (const char *location)
+{
+	GeocodeForward *forward;
+
+	g_return_val_if_fail (location != NULL, NULL);
+
+	forward = g_object_new (GEOCODE_TYPE_FORWARD, NULL);
+	geocode_forward_add (forward, "location", location);
+
+	return forward;
+}
+
+/**
+ * geocode_forward_add:
+ * @forward: a #GeocodeForward
+ * @key: a string representing a parameter to the web service
+ * @value: a string representing the value of a parameter
+ *
+ * Adds parameters to the geocoding or reverse geocoding request.
+ * A copy of the key and value parameters are kept internally.
+ *
+ * This function is mainly intended for language bindings.
+ **/
+static void
+geocode_forward_add (GeocodeForward *forward,
+		     const char    *key,
+		     const char    *value)
+{
+	g_return_if_fail (GEOCODE_IS_FORWARD (forward));
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (value == NULL || g_utf8_validate (value, -1, NULL));
+
+	g_hash_table_insert (forward->priv->ht,
+			     g_strdup (key),
+			     g_strdup (value));
+}
+
+static void
+on_query_data_loaded (GObject      *source_forward,
+		      GAsyncResult *res,
+		      gpointer      user_data)
+{
+	GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+	GFile *query;
+	GError *error = NULL;
+	char *contents;
+	gpointer ret;
+
+	query = G_FILE (source_forward);
+	if (g_file_load_contents_finish (query,
+					 res,
+					 &contents,
+					 NULL,
+					 NULL,
+					 &error) == FALSE) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_simple_async_result_complete_in_idle (simple);
+		g_object_unref (simple);
+		g_error_free (error);
+		return;
+	}
+
+	ret = __geocode_parse_search_json (contents, &error);
+
+	if (ret == NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_simple_async_result_complete_in_idle (simple);
+		g_object_unref (simple);
+		g_error_free (error);
+		g_free (contents);
+		return;
+	}
+
+	/* Now that we can parse the result, save it to cache */
+	_geocode_glib_cache_save (query, contents);
+	g_free (contents);
+
+	g_simple_async_result_set_op_res_gpointer (simple, ret, NULL);
+	g_simple_async_result_complete_in_idle (simple);
+	g_object_unref (simple);
+}
+
+static void
+on_cache_data_loaded (GObject      *source_forward,
+		      GAsyncResult *res,
+		      gpointer      user_data)
+{
+	GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+	GCancellable *cancellable;
+	GFile *cache;
+	GError *error = NULL;
+	char *contents;
+	gpointer ret;
+
+	cache = G_FILE (source_forward);
+	cancellable = g_object_get_data (G_OBJECT (cache), "cancellable");
+	if (g_file_load_contents_finish (cache,
+					 res,
+					 &contents,
+					 NULL,
+					 NULL,
+					 NULL) == FALSE) {
+		GFile *query;
+
+		query = g_object_get_data (G_OBJECT (cache), "query");
+		g_file_load_contents_async (query,
+					    cancellable,
+					    on_query_data_loaded,
+					    simple);
+		return;
+	}
+
+	ret = __geocode_parse_search_json (contents, &error);
+	g_free (contents);
+
+	if (ret == NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_simple_async_result_complete_in_idle (simple);
+		g_object_unref (simple);
+		g_error_free (error);
+		return;
+	}
+
+	g_simple_async_result_set_op_res_gpointer (simple, ret, NULL);
+	g_simple_async_result_complete_in_idle (simple);
+	g_object_unref (simple);
+}
+
+static GFile *
+get_search_query_for_params (GeocodeForward *forward,
+			     GError        **error)
+{
+	GFile *ret;
+	GHashTable *ht;
+	char *lang;
+	char *params;
+	char *search_term;
+	char *uri;
+	const char *location;
+
+	location = g_hash_table_lookup (forward->priv->ht, "location");
+	if (location == NULL) {
+		g_set_error_literal (error, GEOCODE_ERROR, GEOCODE_ERROR_INVALID_ARGUMENTS,
+				     "No location argument set");
+		return NULL;
+	}
+
+	/* Prepare the search term */
+	search_term = soup_uri_encode (location, NULL);
+
+	/* Prepare the query parameters */
+	ht = g_hash_table_new (g_str_hash, g_str_equal);
+	g_hash_table_insert (ht, "appid", YAHOO_APPID);
+	g_hash_table_insert (ht, "format", "json");
+	lang = geocode_forward_get_lang ();
+	if (lang)
+		g_hash_table_insert (ht, "lang", lang);
+
+	params = soup_form_encode_hash (ht);
+	g_hash_table_destroy (ht);
+	g_free (lang);
+
+	uri = g_strdup_printf ("http://where.yahooapis.com/v1/places.q('%s');start=0;count=%u?%s",
+			       search_term, forward->priv->answer_count, params);
+	g_free (params);
+	g_free (search_term);
+
+	ret = g_file_new_for_uri (uri);
+	g_free (uri);
+
+	return ret;
+}
+
+/**
+ * geocode_forward_search_async:
+ * @forward: a #GeocodeForward representing a query
+ * @cancellable: optional #GCancellable forward, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously gets a list of a geocoding
+ * query using a web service. Use geocode_forward_search() to do the same
+ * thing synchronously.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * geocode_forward_search_finish() to get the result of the operation.
+ **/
+void
+geocode_forward_search_async (GeocodeForward       *forward,
+			      GCancellable        *cancellable,
+			      GAsyncReadyCallback  callback,
+			      gpointer             user_data)
+{
+	GSimpleAsyncResult *simple;
+	GFile *query;
+	char *cache_path;
+	GError *error = NULL;
+
+	g_return_if_fail (GEOCODE_IS_FORWARD (forward));
+
+	simple = g_simple_async_result_new (G_OBJECT (forward),
+					    callback,
+					    user_data,
+					    geocode_forward_search_async);
+
+	query = get_search_query_for_params (forward, &error);
+	if (!query) {
+		g_simple_async_result_take_error (simple, error);
+		g_simple_async_result_complete_in_idle (simple);
+		g_object_unref (simple);
+		return;
+	}
+
+	cache_path = _geocode_glib_cache_path_for_query (query);
+	if (cache_path == NULL) {
+		g_file_load_contents_async (query,
+					    cancellable,
+					    on_query_data_loaded,
+					    simple);
+		g_object_unref (query);
+	} else {
+		GFile *cache;
+
+		cache = g_file_new_for_path (cache_path);
+		g_object_set_data_full (G_OBJECT (cache), "query", query, (GDestroyNotify) g_object_unref);
+		g_object_set_data (G_OBJECT (cache), "cancellable", cancellable);
+		g_file_load_contents_async (cache,
+					    cancellable,
+					    on_cache_data_loaded,
+					    simple);
+		g_object_unref (cache);
+	}
+}
+
+/**
+ * geocode_forward_search_finish:
+ * @forward: a #GeocodeForward representing a query
+ * @res: a #GAsyncResult.
+ * @error: a #GError.
+ *
+ * Finishes a query operation. See geocode_forward_search_async().
+ *
+ * Returns: (element-type GHashTable) (transfer full):
+ * a #GHashTable containing the results of the query
+ * or %NULL in case of errors.
+ * Free the returned string with g_hash_table_destroy() when done.
+ **/
+GList *
+geocode_forward_search_finish (GeocodeForward       *forward,
+			       GAsyncResult        *res,
+			       GError             **error)
+{
+	GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+	GList *ret;
+
+	g_return_val_if_fail (GEOCODE_IS_FORWARD (forward), NULL);
+
+	g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == geocode_forward_search_async);
+
+	ret = NULL;
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		goto out;
+
+	ret = g_simple_async_result_get_op_res_gpointer (simple);
+
+out:
+	return ret;
+}
+
+#define IS_EL(x) (g_str_equal (element_name, x))
+
+static void
+insert_place_attr (GHashTable *ht,
+		   JsonReader *reader,
+		   const char *element_name)
+{
+	char *value;
+
+	if (json_reader_read_member (reader, element_name) == FALSE) {
+		json_reader_end_member (reader);
+		return;
+	}
+
+	/* FIXME: check all the member names against what Place Finder outputs */
+
+	if (IS_EL("woeid") ||
+	    IS_EL("popRank") ||
+	    IS_EL("areaRank")) {
+		value = g_strdup_printf ("%"G_GINT64_FORMAT, json_reader_get_int_value (reader));
+	} else if (IS_EL("centroid")) {
+		json_reader_read_member (reader, "longitude");
+		g_hash_table_insert (ht, g_strdup ("longitude"),
+				     g_strdup_printf ("%lf", json_reader_get_double_value (reader)));
+		json_reader_end_member (reader);
+		json_reader_read_member (reader, "latitude");
+		g_hash_table_insert (ht, g_strdup ("latitude"),
+				     g_strdup_printf ("%lf", json_reader_get_double_value (reader)));
+		json_reader_end_member (reader);
+		goto end;
+	} else if (g_str_has_suffix (element_name, " attrs")) {
+		g_debug ("Ignoring attributes element '%s'", element_name);
+		value = g_strdup (""); /* So that they're ignored */
+	} else if (IS_EL("boundingBox")) {
+		g_debug ("Ignoring element '%s'", element_name);
+		value = g_strdup (""); /* So that they're ignored */
+	} else {
+		value = g_strdup (json_reader_get_string_value (reader));
+	}
+
+	if (value != NULL && *value == '\0') {
+		g_clear_pointer (&value, g_free);
+		goto end;
+	}
+
+	if (value != NULL)
+		g_hash_table_insert (ht, g_strdup (element_name), value);
+	else
+		g_warning ("Ignoring element %s, don't know how to parse it", element_name);
+
+end:
+	json_reader_end_member (reader);
+}
+
+static char *
+create_description_from_attrs (GHashTable *ht,
+			       gdouble    *longitude,
+			       gdouble    *latitude)
+{
+	*longitude = strtod (g_hash_table_lookup (ht, "longitude"), NULL);
+	*latitude = strtod (g_hash_table_lookup (ht, "latitude"), NULL);
+	return g_strdup (g_hash_table_lookup (ht, "name"));
+}
+
+static GeocodeLocation *
+new_location_from_result (GHashTable *ht)
+{
+	GeocodeLocation *loc;
+	char *description;
+	gdouble longitude, latitude;
+
+	description = create_description_from_attrs (ht,
+						     &longitude,
+						     &latitude);
+	loc = geocode_location_new_with_description (longitude,
+						     latitude,
+						     description);
+	g_free (description);
+
+	return loc;
+}
+
+static GList *
+__geocode_parse_search_json (const char *contents,
+			     GError    **error)
+{
+	GList *ret;
+	JsonParser *parser;
+	JsonNode *root;
+	JsonReader *reader;
+	const GError *err = NULL;
+	int num_places, i;
+
+	ret = NULL;
+
+	parser = json_parser_new ();
+	if (json_parser_load_from_data (parser, contents, -1, error) == FALSE) {
+		g_object_unref (parser);
+		return ret;
+	}
+
+	root = json_parser_get_root (parser);
+	reader = json_reader_new (root);
+
+	if (json_reader_read_member (reader, "places") == FALSE)
+		goto parse;
+	if (json_reader_read_member (reader, "place") == FALSE)
+		goto parse;
+
+	num_places = json_reader_count_elements (reader);
+	if (num_places < 0)
+		goto parse;
+
+	for (i = 0; i < num_places; i++) {
+		GeocodeLocation *loc;
+		GHashTable *ht;
+		char **members;
+		int j;
+
+		json_reader_read_element (reader, i);
+
+		ht = g_hash_table_new_full (g_str_hash, g_str_equal,
+					    g_free, g_free);
+
+		members = json_reader_list_members (reader);
+		for (j = 0; members != NULL && members[j] != NULL; j++)
+			insert_place_attr (ht, reader, members[j]);
+		g_strfreev (members);
+
+		json_reader_end_element (reader);
+
+		loc = new_location_from_result (ht);
+
+		ret = g_list_prepend (ret, loc);
+	}
+
+	g_object_unref (parser);
+	g_object_unref (reader);
+	ret = g_list_reverse (ret);
+
+	return ret;
+parse:
+	err = json_reader_get_error (reader);
+	g_set_error_literal (error, GEOCODE_ERROR, GEOCODE_ERROR_PARSE, err->message);
+	g_object_unref (parser);
+	g_object_unref (reader);
+	return NULL;
+}
+
+/**
+ * geocode_forward_search:
+ * @forward: a #GeocodeForward representing a query
+ * @error: a #GError
+ *
+ * Gets the result of a geocoding or reverse geocoding
+ * query using a web service.
+ *
+ * Returns: (element-type GHashTable) (transfer full):
+ * a #GHashTable containing the results of the query
+ * or %NULL in case of errors.
+ * Free the returned string with g_hash_table_destroy() when done.
+ **/
+GList *
+geocode_forward_search (GeocodeForward      *forward,
+			GError             **error)
+{
+	GFile *query;
+	char *contents;
+	GList *ret;
+	gboolean to_cache = FALSE;
+
+	g_return_val_if_fail (GEOCODE_IS_FORWARD (forward), NULL);
+
+	query = get_search_query_for_params (forward, error);
+	if (!query)
+		return NULL;
+
+	if (_geocode_glib_cache_load (query, &contents) == FALSE) {
+		if (g_file_load_contents (query,
+					  NULL,
+					  &contents,
+					  NULL,
+					  NULL,
+					  error) == FALSE) {
+			/* FIXME check error value and match against
+			 * web service errors:
+			 * http://developer.yahoo.com/geo/geoplanet/guide/api_docs.html#response-errors */
+			g_object_unref (query);
+			return NULL;
+		}
+		to_cache = TRUE;
+	}
+
+	ret = __geocode_parse_search_json (contents, error);
+	if (to_cache && ret != NULL)
+		_geocode_glib_cache_save (query, contents);
+
+	g_free (contents);
+	g_object_unref (query);
+
+	return ret;
+}
+
+void
+geocode_forward_set_answer_count (GeocodeForward *forward,
+				  guint           count)
+{
+	g_return_if_fail (GEOCODE_IS_FORWARD (forward));
+
+	/* FIXME: make a property */
+	forward->priv->answer_count = count;
+}
diff --git a/geocode-glib/geocode-forward.h b/geocode-glib/geocode-forward.h
new file mode 100644
index 0000000..b25dd77
--- /dev/null
+++ b/geocode-glib/geocode-forward.h
@@ -0,0 +1,78 @@
+/*
+   Copyright (C) 2012 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301  USA.
+
+   Authors: Bastien Nocera <hadess hadess net>
+
+ */
+
+#ifndef GEOCODE_FORWARD_H
+#define GEOCODE_FORWARD_H
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <geocode-glib/geocode-glib.h>
+
+G_BEGIN_DECLS
+
+GType geocode_forward_get_type (void) G_GNUC_CONST;
+
+#define GEOCODE_TYPE_FORWARD                  (geocode_forward_get_type ())
+#define GEOCODE_FORWARD(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEOCODE_TYPE_FORWARD, GeocodeForward))
+#define GEOCODE_IS_FORWARD(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEOCODE_TYPE_FORWARD))
+#define GEOCODE_FORWARD_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GEOCODE_TYPE_FORWARD, GeocodeForwardClass))
+#define GEOCODE_IS_FORWARD_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GEOCODE_TYPE_FORWARD))
+#define GEOCODE_FORWARD_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GEOCODE_TYPE_FORWARD, GeocodeForwardClass))
+
+/**
+ * GeocodeForward:
+ *
+ * All the fields in the #GeocodeForward structure are private and should never be accessed directly.
+**/
+typedef struct _GeocodeForward        GeocodeForward;
+typedef struct _GeocodeForwardClass   GeocodeForwardClass;
+typedef struct _GeocodeForwardPrivate GeocodeForwardPrivate;
+
+struct _GeocodeForward {
+	GObject parent_instance;
+	GeocodeForwardPrivate *priv;
+};
+
+struct _GeocodeForwardClass {
+	GObjectClass parent_class;
+};
+
+GeocodeForward *geocode_forward_new_for_string     (const char *str);
+GeocodeForward *geocode_forward_new_for_params     (GHashTable *params);
+void geocode_forward_set_answer_count              (GeocodeForward *forward,
+						    guint           count);
+
+void geocode_forward_search_async  (GeocodeForward       *forward,
+				    GCancellable        *cancellable,
+				    GAsyncReadyCallback  callback,
+				    gpointer             user_data);
+
+GList *geocode_forward_search_finish (GeocodeForward  *forward,
+				      GAsyncResult    *res,
+				      GError         **error);
+
+GList *geocode_forward_search (GeocodeForward  *forward,
+			       GError         **error);
+
+G_END_DECLS
+
+#endif /* GEOCODE_FORWARD_H */
diff --git a/geocode-glib/geocode-glib.h b/geocode-glib/geocode-glib.h
index fd7b8f1..1a7509e 100644
--- a/geocode-glib/geocode-glib.h
+++ b/geocode-glib/geocode-glib.h
@@ -26,6 +26,7 @@
 #include <glib.h>
 #include <gio/gio.h>
 #include <geocode-glib/geocode-location.h>
+#include <geocode-glib/geocode-forward.h>
 #include <geocode-glib/geocode-error.h>
 
 G_BEGIN_DECLS
diff --git a/geocode-glib/geocode-glib.symbols b/geocode-glib/geocode-glib.symbols
index ac95c9c..4e99189 100644
--- a/geocode-glib/geocode-glib.symbols
+++ b/geocode-glib/geocode-glib.symbols
@@ -1,6 +1,13 @@
 geocode_location_get_type
 geocode_location_new
 geocode_location_new_with_description
+geocode_forward_get_type
+geocode_forward_new_for_string
+geocode_forward_new_for_params
+geocode_forward_set_answer_count
+geocode_forward_search_async
+geocode_forward_search_finish
+geocode_forward_search
 geocode_error_quark
 geocode_object_get_type
 geocode_object_new



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]