aboutsummaryrefslogtreecommitdiff
path: root/src/timezone-completion.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/timezone-completion.c')
-rw-r--r--src/timezone-completion.c227
1 files changed, 221 insertions, 6 deletions
diff --git a/src/timezone-completion.c b/src/timezone-completion.c
index d98654a..a1b4d00 100644
--- a/src/timezone-completion.c
+++ b/src/timezone-completion.c
@@ -22,6 +22,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include "config.h"
#endif
+#include <json-glib/json-glib.h>
#include <gdk/gdk.h>
#include <glib/gi18n.h>
#include "timezone-completion.h"
@@ -36,10 +37,17 @@ enum {
typedef struct _TimezoneCompletionPrivate TimezoneCompletionPrivate;
struct _TimezoneCompletionPrivate
{
- void * placeholder;
+ GtkEntry * entry;
+ guint queued_request;
+ guint changed_id;
+ GCancellable * cancel;
+ gchar * request_text;
+ GHashTable * request_table;
};
-#define TIMEZONE_COMPLETION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TIMEZONE_COMPLETION_TYPE, TimezoneCompletionPrivate))
+#define TIMEZONE_COMPLETION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), TIMEZONE_COMPLETION_TYPE, TimezoneCompletionPrivate))
+
+#define GEONAME_URL "http://geoname-lookup.ubuntu.com/?query=%s&release=%s"
/* Prototypes */
static void timezone_completion_class_init (TimezoneCompletionClass *klass);
@@ -49,13 +57,186 @@ static void timezone_completion_finalize (GObject *object);
G_DEFINE_TYPE (TimezoneCompletion, timezone_completion, GTK_TYPE_ENTRY_COMPLETION);
+static void
+json_parse_ready (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ TimezoneCompletion * completion = TIMEZONE_COMPLETION (user_data);
+ TimezoneCompletionPrivate * priv = TIMEZONE_COMPLETION_GET_PRIVATE(completion);
+ GError * error = NULL;
+
+ json_parser_load_from_stream_finish (JSON_PARSER (object), res, &error);
+
+ if (priv->cancel && (error == NULL || error->code != G_IO_ERROR_CANCELLED)) {
+ g_cancellable_reset (priv->cancel);
+ }
+
+ if (error != NULL) {
+ g_warning ("Could not parse geoname JSON data: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+g_print("got json\n");
+ GtkListStore * store = GTK_LIST_STORE (gtk_entry_completion_get_model (GTK_ENTRY_COMPLETION (completion)));
+ JsonReader * reader = json_reader_new (json_parser_get_root (JSON_PARSER (object)));
+
+ if (!json_reader_is_array (reader))
+ return;
+
+ gint i, count = json_reader_count_elements (reader);
+ for (i = 0; i < count; ++i) {
+ if (!json_reader_read_element (reader, i))
+ continue;
+
+ if (json_reader_is_object (reader)) {
+ const gchar * name = NULL;
+ const gchar * admin1 = NULL;
+ const gchar * country = NULL;
+ const gchar * longitude = NULL;
+ const gchar * latitude = NULL;
+ if (json_reader_read_member (reader, "name")) {
+ name = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+ }
+ if (json_reader_read_member (reader, "admin1")) {
+ admin1 = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+ }
+ if (json_reader_read_member (reader, "country")) {
+ country = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+ }
+ if (json_reader_read_member (reader, "longitude")) {
+ longitude = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+ }
+ if (json_reader_read_member (reader, "latitude")) {
+ latitude = json_reader_get_string_value (reader);
+ json_reader_end_member (reader);
+ }
+
+g_print("adding %s\n", name);
+ GtkTreeIter iter;
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ TIMEZONE_COMPLETION_ZONE, NULL,
+ TIMEZONE_COMPLETION_NAME, name,
+ TIMEZONE_COMPLETION_ADMIN1, admin1,
+ TIMEZONE_COMPLETION_COUNTRY, country,
+ TIMEZONE_COMPLETION_LONGITUDE, longitude,
+ TIMEZONE_COMPLETION_LATITUDE, latitude,
+ -1);
+ }
+
+ json_reader_end_element (reader);
+ }
+
+ g_hash_table_insert (priv->request_table, g_strdup (priv->request_text), NULL);
+}
+
+static void
+geonames_data_ready (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ TimezoneCompletion * completion = TIMEZONE_COMPLETION (user_data);
+ TimezoneCompletionPrivate * priv = TIMEZONE_COMPLETION_GET_PRIVATE (completion);
+ GError * error = NULL;
+ GFileInputStream * stream;
+
+ stream = g_file_read_finish (G_FILE (object), res, &error);
+
+ if (priv->cancel && (error == NULL || error->code != G_IO_ERROR_CANCELLED)) {
+ g_cancellable_reset (priv->cancel);
+ }
+
+ if (error != NULL) {
+ g_warning ("Could not connect to geoname lookup server: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ JsonParser * parser = json_parser_new ();
+ json_parser_load_from_stream_async (parser, G_INPUT_STREAM (stream), priv->cancel,
+ json_parse_ready, user_data);
+}
+
+static gboolean
+request_zones (TimezoneCompletion * completion)
+{
+ TimezoneCompletionPrivate * priv = TIMEZONE_COMPLETION_GET_PRIVATE (completion);
+
+ priv->queued_request = 0;
+
+g_print("requesting json?\n");
+ if (priv->entry == NULL) {
+ return FALSE;
+ }
+
+ const gchar * text = gtk_entry_get_text (priv->entry);
+
+ if (g_hash_table_lookup_extended (priv->request_table, text, NULL, NULL))
+ return FALSE; // already looked this up
+
+ /* Cancel any ongoing request */
+ if (priv->cancel) {
+ g_cancellable_cancel (priv->cancel);
+ g_cancellable_reset (priv->cancel);
+ }
+ g_free (priv->request_text);
+
+ priv->request_text = g_strdup (text);
+
+g_print("requesting json now\n");
+ gchar * escaped = g_uri_escape_string (text, NULL, FALSE);
+ gchar * url = g_strdup_printf (GEONAME_URL, escaped, "11.04");
+
+ GFile * file = g_file_new_for_uri (url);
+ g_file_read_async (file, G_PRIORITY_DEFAULT, priv->cancel,
+ geonames_data_ready, completion);
+
+ return FALSE;
+}
+
+static void
+entry_changed (GtkEntry * entry, TimezoneCompletion * completion)
+{
+ TimezoneCompletionPrivate * priv = TIMEZONE_COMPLETION_GET_PRIVATE (completion);
+
+ if (priv->queued_request) {
+ g_source_remove (priv->queued_request);
+ }
+ priv->queued_request = g_timeout_add (300, (GSourceFunc)request_zones, completion);
+}
+
+void
+timezone_completion_watch_entry (TimezoneCompletion * completion, GtkEntry * entry)
+{
+ TimezoneCompletionPrivate * priv = TIMEZONE_COMPLETION_GET_PRIVATE (completion);
+
+ if (priv->entry) {
+ g_source_remove (priv->changed_id);
+ g_object_remove_weak_pointer (G_OBJECT (priv->entry), (gpointer *)&priv->entry);
+ }
+
+ guint id = g_signal_connect (entry, "changed", G_CALLBACK (entry_changed), completion);
+ priv->changed_id = id;
+
+ priv->entry = entry;
+ g_object_add_weak_pointer (G_OBJECT (entry), (gpointer *)&priv->entry);
+}
+
static GtkListStore *
get_initial_model (void)
{
TzDB * db = tz_load_db ();
GPtrArray * locations = tz_get_locations (db);
- GtkListStore * store = gtk_list_store_new (TIMEZONE_COMPLETION_LAST, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ GtkListStore * store = gtk_list_store_new (TIMEZONE_COMPLETION_LAST,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
gint i;
for (i = 0; i < locations->len; ++i) {
@@ -100,23 +281,57 @@ timezone_completion_class_init (TimezoneCompletionClass *klass)
}
static void
-timezone_completion_init (TimezoneCompletion *self)
+timezone_completion_init (TimezoneCompletion * self)
{
+ TimezoneCompletionPrivate * priv = TIMEZONE_COMPLETION_GET_PRIVATE (self);
+
GtkListStore * model = get_initial_model ();
gtk_entry_completion_set_model (GTK_ENTRY_COMPLETION (self), GTK_TREE_MODEL (model));
gtk_entry_completion_set_text_column (GTK_ENTRY_COMPLETION (self), TIMEZONE_COMPLETION_NAME);
+
+ priv->cancel = g_cancellable_new ();
+
+ priv->request_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
return;
}
static void
-timezone_completion_dispose (GObject *object)
+timezone_completion_dispose (GObject * object)
{
G_OBJECT_CLASS (timezone_completion_parent_class)->dispose (object);
+
+ TimezoneCompletion * completion = TIMEZONE_COMPLETION (object);
+ TimezoneCompletionPrivate * priv = TIMEZONE_COMPLETION_GET_PRIVATE (completion);
+
+ if (priv->changed_id) {
+ g_source_remove (priv->changed_id);
+ priv->changed_id = 0;
+ }
+
+ if (priv->entry != NULL) {
+ g_object_remove_weak_pointer (G_OBJECT (priv->entry), (gpointer *)&priv->entry);
+ }
+
+ if (priv->queued_request) {
+ g_source_remove (priv->queued_request);
+ priv->queued_request = 0;
+ }
+
+ if (priv->cancel != NULL) {
+ g_cancellable_cancel (priv->cancel);
+ g_object_unref (priv->cancel);
+ priv->cancel = NULL;
+ }
+
+ g_free (priv->request_text);
+ g_hash_table_destroy (priv->request_table);
+
return;
}
static void
-timezone_completion_finalize (GObject *object)
+timezone_completion_finalize (GObject * object)
{
G_OBJECT_CLASS (timezone_completion_parent_class)->finalize (object);
return;