[yelp/yelp-3-0] [libyelp] YelpView basic implementation, YelpSimpleDocument for (X)HTML
- From: Shaun McCance <shaunm src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [yelp/yelp-3-0] [libyelp] YelpView basic implementation, YelpSimpleDocument for (X)HTML
- Date: Thu, 24 Sep 2009 04:55:29 +0000 (UTC)
commit 2962b08821183df2f8d4dbfcc4f451e5c2ca89a1
Author: Shaun McCance <shaunm gnome org>
Date: Wed Sep 23 23:45:34 2009 -0500
[libyelp] YelpView basic implementation, YelpSimpleDocument for (X)HTML
libyelp/Makefile.am | 2 +
libyelp/yelp-document.c | 1072 ++++++++++++++++++++++++++++++++++++++++
libyelp/yelp-document.h | 151 ++++++
libyelp/yelp-simple-document.c | 419 ++++++++++++++++
libyelp/yelp-simple-document.h | 55 ++
libyelp/yelp-uri.c | 61 ++--
libyelp/yelp-view.c | 147 ++++--
libyelp/yelp-view.h | 25 +-
src/yelp-document.c | 931 ----------------------------------
src/yelp-document.h | 137 -----
tests/Makefile.am | 6 +-
tests/test-view.c | 61 +++
12 files changed, 1912 insertions(+), 1155 deletions(-)
---
diff --git a/libyelp/Makefile.am b/libyelp/Makefile.am
index 7ee9080..ad40c35 100644
--- a/libyelp/Makefile.am
+++ b/libyelp/Makefile.am
@@ -2,7 +2,9 @@ noinst_LTLIBRARIES = libyelp.la
libyelp_la_SOURCES = \
yelp-debug.c \
+ yelp-document.c \
yelp-location-entry.c \
+ yelp-simple-document.c \
yelp-uri.c \
yelp-view.c
diff --git a/libyelp/yelp-document.c b/libyelp/yelp-document.c
new file mode 100644
index 0000000..2f9df0d
--- /dev/null
+++ b/libyelp/yelp-document.c
@@ -0,0 +1,1072 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance <shaunm gnome org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "yelp-document.h"
+
+typedef struct _Request Request;
+struct _Request {
+ YelpDocument *document;
+ gchar *page_id;
+ GCancellable *cancellable;
+ YelpDocumentCallback callback;
+ gpointer user_data;
+
+ gint idle_funcs;
+};
+
+struct _YelpDocumentPriv {
+ GMutex *mutex;
+
+ GSList *reqs_all; /* Holds canonical refs, only free from here */
+ GHashTable *reqs_by_page_id; /* Indexed by page ID, contains GSList */
+ GSList *reqs_pending; /* List of requests that need a page */
+
+ /* Real page IDs map to themselves, so this list doubles
+ * as a list of all valid page IDs.
+ */
+ GHashTable *page_ids; /* Mapping of fragment IDs to real page IDs */
+ GHashTable *titles; /* Mapping of page IDs to titles */
+ GHashTable *mime_types; /* Mapping of page IDs to mime types */
+ GHashTable *contents; /* Mapping of page IDs to string content */
+
+ GHashTable *root_ids; /* Mapping of page IDs to "previous page" IDs */
+ GHashTable *prev_ids; /* Mapping of page IDs to "previous page" IDs */
+ GHashTable *next_ids; /* Mapping of page IDs to "next page" IDs */
+ GHashTable *up_ids; /* Mapping of page IDs to "up page" IDs */
+
+ GMutex *str_mutex;
+ GHashTable *str_refs;
+};
+
+G_DEFINE_TYPE (YelpDocument, yelp_document, G_TYPE_OBJECT);
+
+#define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCUMENT, YelpDocumentPriv))
+
+static void yelp_document_class_init (YelpDocumentClass *klass);
+static void yelp_document_init (YelpDocument *document);
+static void yelp_document_dispose (GObject *object);
+static void yelp_document_finalize (GObject *object);
+
+static gboolean document_request_page (YelpDocument *document,
+ const gchar *page_id,
+ GCancellable *cancellable,
+ YelpDocumentCallback callback,
+ gpointer user_data);
+static const gchar * document_read_contents (YelpDocument *document,
+ const gchar *page_id);
+static void document_finish_read (YelpDocument *document,
+ const gchar *contents);
+static gchar * document_get_mime_type (YelpDocument *document,
+ const gchar *mime_type);
+
+static void request_cancel (Request *request);
+static gboolean request_idle_contents (Request *request);
+static gboolean request_idle_info (Request *request);
+static void request_try_free (Request *request);
+static void request_free (Request *request);
+
+static const gchar * str_ref (const gchar *str);
+static void str_unref (const gchar *str);
+static void hash_slist_insert (GHashTable *hash,
+ const gchar *key,
+ gpointer value);
+static void hash_slist_remove (GHashTable *hash,
+ const gchar *key,
+ gpointer value);
+
+#if 0
+static gboolean request_idle_error (Request *request);
+static gboolean request_idle_final (YelpDocument *document);
+
+#endif
+
+GStaticMutex str_mutex = G_STATIC_MUTEX_INIT;
+GHashTable *str_refs = NULL;
+
+static void
+yelp_document_class_init (YelpDocumentClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = yelp_document_finalize;
+ object_class->finalize = yelp_document_finalize;
+
+ klass->request_page = document_request_page;
+ klass->read_contents = document_read_contents;
+ klass->finish_read = document_finish_read;
+ klass->get_mime_type = document_get_mime_type;
+
+ g_type_class_add_private (klass, sizeof (YelpDocumentPriv));
+}
+
+static void
+yelp_document_init (YelpDocument *document)
+{
+ YelpDocumentPriv *priv;
+
+ document->priv = priv = GET_PRIV (document);
+
+ priv->mutex = g_mutex_new ();
+
+ priv->reqs_by_page_id =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) g_slist_free);
+ priv->reqs_all = NULL;
+ priv->reqs_pending = NULL;
+
+ priv->page_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->titles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->mime_types = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->contents = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) str_unref);
+
+ priv->root_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->prev_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->next_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->up_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+static void
+yelp_document_dispose (GObject *object)
+{
+ YelpDocument *document = YELP_DOCUMENT (object);
+
+ if (document->priv->reqs_all) {
+ g_slist_foreach (document->priv->reqs_all, request_try_free, NULL);
+ g_slist_free (document->priv->reqs_all);
+ document->priv->reqs_all = NULL;
+ }
+
+ G_OBJECT_CLASS (yelp_document_parent_class)->dispose (object);
+}
+
+static void
+yelp_document_finalize (GObject *object)
+{
+ YelpDocument *document = YELP_DOCUMENT (object);
+
+ g_slist_free (document->priv->reqs_pending);
+ g_hash_table_destroy (document->priv->reqs_by_page_id);
+
+ g_hash_table_destroy (document->priv->page_ids);
+ g_hash_table_destroy (document->priv->titles);
+ g_hash_table_destroy (document->priv->mime_types);
+
+ g_hash_table_destroy (document->priv->contents);
+
+ g_hash_table_destroy (document->priv->root_ids);
+ g_hash_table_destroy (document->priv->prev_ids);
+ g_hash_table_destroy (document->priv->next_ids);
+ g_hash_table_destroy (document->priv->up_ids);
+
+ g_mutex_free (document->priv->mutex);
+
+ G_OBJECT_CLASS (yelp_document_parent_class)->finalize (object);
+}
+
+/******************************************************************************/
+
+gchar *
+yelp_document_get_page_id (YelpDocument *document,
+ const gchar *id)
+{
+ gchar *ret = NULL;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ ret = g_hash_table_lookup (document->priv->page_ids, id);
+ if (ret)
+ ret = g_strdup (ret);
+
+ g_mutex_unlock (document->priv->mutex);
+
+ return ret;
+}
+
+void
+yelp_document_set_page_id (YelpDocument *document,
+ const gchar *id,
+ const gchar *page_id)
+{
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+
+ g_hash_table_replace (document->priv->root_ids, g_strdup (id), g_strdup (page_id));
+
+ if (!g_str_equal (id, page_id)) {
+ GSList *reqs, *cur;
+ reqs = g_hash_table_lookup (document->priv->reqs_by_page_id, id);
+ for (cur = reqs; cur != NULL; cur = cur->next) {
+ Request *request = (Request *) cur->data;
+ g_free (request->page_id);
+ request->page_id = g_strdup (page_id);
+ hash_slist_insert (document->priv->reqs_by_page_id, page_id, request);
+ }
+ if (reqs)
+ g_hash_table_remove (document->priv->reqs_by_page_id, id);
+ }
+
+ g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_root_id (YelpDocument *document,
+ const gchar *page_id)
+{
+ gchar *real, *ret = NULL;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ real = g_hash_table_lookup (document->priv->page_ids, page_id);
+ if (real) {
+ ret = g_hash_table_lookup (document->priv->root_ids, real);
+ if (ret)
+ ret = g_strdup (ret);
+ }
+ g_mutex_unlock (document->priv->mutex);
+
+ return ret;
+}
+
+void
+yelp_document_set_root_id (YelpDocument *document,
+ const gchar *page_id,
+ const gchar *root_id)
+{
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ g_hash_table_replace (document->priv->root_ids, g_strdup (page_id), g_strdup (root_id));
+ g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_prev_id (YelpDocument *document,
+ const gchar *page_id)
+{
+ gchar *real, *ret = NULL;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ real = g_hash_table_lookup (document->priv->page_ids, page_id);
+ if (real) {
+ ret = g_hash_table_lookup (document->priv->prev_ids, real);
+ if (ret)
+ ret = g_strdup (ret);
+ }
+ g_mutex_unlock (document->priv->mutex);
+
+ return ret;
+}
+
+void
+yelp_document_set_prev_id (YelpDocument *document,
+ const gchar *page_id,
+ const gchar *prev_id)
+{
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ g_hash_table_replace (document->priv->prev_ids, g_strdup (page_id), g_strdup (prev_id));
+ g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_next_id (YelpDocument *document,
+ const gchar *page_id)
+{
+ gchar *real, *ret = NULL;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ real = g_hash_table_lookup (document->priv->page_ids, page_id);
+ if (real) {
+ ret = g_hash_table_lookup (document->priv->next_ids, real);
+ if (ret)
+ ret = g_strdup (ret);
+ }
+ g_mutex_unlock (document->priv->mutex);
+
+ return ret;
+}
+
+void
+yelp_document_set_next_id (YelpDocument *document,
+ const gchar *page_id,
+ const gchar *next_id)
+{
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ g_hash_table_replace (document->priv->next_ids, g_strdup (page_id), g_strdup (next_id));
+ g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_up_id (YelpDocument *document,
+ const gchar *page_id)
+{
+ gchar *real, *ret = NULL;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ real = g_hash_table_lookup (document->priv->page_ids, page_id);
+ if (real) {
+ ret = g_hash_table_lookup (document->priv->up_ids, real);
+ if (ret)
+ ret = g_strdup (ret);
+ }
+ g_mutex_unlock (document->priv->mutex);
+
+ return ret;
+}
+
+void
+yelp_document_set_up_id (YelpDocument *document,
+ const gchar *page_id,
+ const gchar *up_id)
+{
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ g_hash_table_replace (document->priv->up_ids, g_strdup (page_id), g_strdup (up_id));
+ g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_title (YelpDocument *document,
+ const gchar *page_id)
+{
+ gchar *real, *ret = NULL;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ real = g_hash_table_lookup (document->priv->page_ids, page_id);
+ if (real) {
+ ret = g_hash_table_lookup (document->priv->titles, real);
+ if (ret)
+ ret = g_strdup (ret);
+ }
+ g_mutex_unlock (document->priv->mutex);
+
+ return ret;
+}
+
+void
+yelp_document_set_title (YelpDocument *document,
+ const gchar *page_id,
+ const gchar *title)
+{
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+ g_hash_table_replace (document->priv->titles, g_strdup (page_id), g_strdup (title));
+ g_mutex_unlock (document->priv->mutex);
+}
+
+/******************************************************************************/
+
+gboolean
+yelp_document_request_page (YelpDocument *document,
+ const gchar *page_id,
+ GCancellable *cancellable,
+ YelpDocumentCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (YELP_IS_DOCUMENT (document));
+ g_return_if_fail (YELP_DOCUMENT_GET_CLASS (document)->request_page != NULL);
+
+ return YELP_DOCUMENT_GET_CLASS (document)->request_page (document,
+ page_id,
+ cancellable,
+ callback,
+ user_data);
+}
+
+static gboolean
+document_request_page (YelpDocument *document,
+ const gchar *page_id,
+ GCancellable *cancellable,
+ YelpDocumentCallback callback,
+ gpointer user_data)
+{
+ Request *request;
+ gchar *real_id;
+ gboolean ret = FALSE;
+
+ request = g_slice_new0 (Request);
+ request->document = g_object_ref (document);
+
+ real_id = g_hash_table_lookup (document->priv->page_ids, page_id);
+ if (real_id)
+ request->page_id = g_strdup (real_id);
+ else
+ request->page_id = g_strdup (page_id);
+
+ request->cancellable = g_object_ref (cancellable);
+ g_signal_connect (cancellable, "cancelled",
+ G_CALLBACK (request_cancel), request);
+
+ request->callback = callback;
+ request->user_data = user_data;
+ request->idle_funcs = 0;
+
+ g_mutex_lock (document->priv->mutex);
+
+ hash_slist_insert (document->priv->reqs_by_page_id,
+ request->page_id,
+ request);
+
+ document->priv->reqs_all = g_slist_prepend (document->priv->reqs_all, request);
+ document->priv->reqs_pending = g_slist_prepend (document->priv->reqs_pending, request);
+
+ if (g_hash_table_lookup (document->priv->titles, request->page_id)) {
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_info, request);
+ }
+
+ if (g_hash_table_lookup (document->priv->contents, request->page_id)) {
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_contents, request);
+ ret = TRUE;
+ }
+
+ g_mutex_unlock (document->priv->mutex);
+
+ return ret;
+}
+
+/******************************************************************************/
+
+const gchar *
+yelp_document_read_contents (YelpDocument *document,
+ const gchar *page_id)
+{
+ g_return_val_if_fail (YELP_IS_DOCUMENT (document), NULL);
+ g_return_val_if_fail (YELP_DOCUMENT_GET_CLASS (document)->read_contents != NULL, NULL);
+
+ YELP_DOCUMENT_GET_CLASS (document)->read_contents (document, page_id);
+}
+
+static const gchar *
+document_read_contents (YelpDocument *document,
+ const gchar *page_id)
+{
+ gchar *real, *str;
+
+ g_mutex_lock (document->priv->mutex);
+
+ real = g_hash_table_lookup (document->priv->page_ids, page_id);
+ str = g_hash_table_lookup (document->priv->contents, real);
+ if (str)
+ str_ref (str);
+
+ g_mutex_unlock (document->priv->mutex);
+
+ return (const gchar *) str;
+}
+
+void
+yelp_document_finish_read (YelpDocument *document,
+ const gchar *contents)
+{
+ g_return_if_fail (YELP_IS_DOCUMENT (document));
+ g_return_if_fail (YELP_DOCUMENT_GET_CLASS (document)->finish_read != NULL);
+
+ YELP_DOCUMENT_GET_CLASS (document)->finish_read (document, contents);
+}
+
+static void
+document_finish_read (YelpDocument *document,
+ const gchar *contents)
+{
+ str_unref (contents);
+}
+
+void
+yelp_document_take_contents (YelpDocument *document,
+ const gchar *page_id,
+ gchar *contents,
+ const gchar *mime)
+{
+ g_return_if_fail (YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+
+ g_hash_table_replace (document->priv->contents,
+ g_strdup (page_id),
+ (gpointer) str_ref (contents));
+
+ g_hash_table_replace (document->priv->mime_types,
+ g_strdup (page_id),
+ g_strdup (mime));
+
+ g_mutex_unlock (document->priv->mutex);
+}
+
+gchar *
+yelp_document_get_mime_type (YelpDocument *document,
+ const gchar *page_id)
+{
+ g_return_if_fail (YELP_IS_DOCUMENT (document));
+ g_return_if_fail (YELP_DOCUMENT_GET_CLASS (document)->get_mime_type != NULL);
+
+ YELP_DOCUMENT_GET_CLASS (document)->get_mime_type (document, page_id);
+}
+
+static gchar *
+document_get_mime_type (YelpDocument *document,
+ const gchar *page_id)
+{
+ gchar *real, *ret = NULL;
+
+ g_mutex_lock (document->priv->mutex);
+ real = g_hash_table_lookup (document->priv->page_ids, page_id);
+ if (real) {
+ ret = g_hash_table_lookup (document->priv->mime_types, real);
+ if (ret)
+ ret = g_strdup (ret);
+ }
+ g_mutex_unlock (document->priv->mutex);
+
+ return ret;
+}
+
+/******************************************************************************/
+
+void
+yelp_document_signal (YelpDocument *document,
+ const gchar *page_id,
+ YelpDocumentSignal signal,
+ GError *error)
+{
+ GSList *reqs, *cur;
+
+ g_return_if_fail (YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+
+ reqs = g_hash_table_lookup (document->priv->reqs_by_page_id, page_id);
+ for (cur = reqs; cur != NULL; cur = cur->next) {
+ Request *request = (Request *) cur->data;
+ if (!request)
+ continue;
+ switch (signal) {
+ case YELP_DOCUMENT_SIGNAL_CONTENTS:
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_contents, request);
+ break;
+ case YELP_DOCUMENT_SIGNAL_INFO:
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_info, request);
+ break;
+ case YELP_DOCUMENT_SIGNAL_ERROR:
+ /* FIXME */
+ default:
+ break;
+ }
+ }
+
+ g_mutex_unlock (document->priv->mutex);
+}
+
+static void
+request_cancel (Request *request)
+{
+ GSList *cur;
+ YelpDocument *document = request->document;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ g_mutex_lock (document->priv->mutex);
+
+ document->priv->reqs_pending = g_slist_remove (document->priv->reqs_pending,
+ (gconstpointer) request);
+ hash_slist_remove (document->priv->reqs_by_page_id,
+ request->page_id,
+ request);
+ for (cur = document->priv->reqs_all; cur != NULL; cur = cur->next) {
+ if (cur->data == request) {
+ document->priv->reqs_all = g_slist_delete_link (document->priv->reqs_all, cur);
+ break;
+ }
+ }
+ request_try_free (request);
+
+ g_mutex_unlock (document->priv->mutex);
+}
+
+static gboolean
+request_idle_contents (Request *request)
+{
+ YelpDocument *document;
+ YelpDocumentCallback callback = NULL;
+ gpointer user_data = user_data;
+
+ g_assert (request != NULL && YELP_IS_DOCUMENT (request->document));
+
+ if (g_cancellable_is_cancelled (request->cancellable)) {
+ request->idle_funcs--;
+ return FALSE;
+ }
+
+ document = g_object_ref (request->document);
+
+ g_mutex_lock (document->priv->mutex);
+
+ callback = request->callback;
+ user_data = request->user_data;
+ request->idle_funcs--;
+
+ g_mutex_unlock (document->priv->mutex);
+
+ if (callback)
+ callback (document, YELP_DOCUMENT_SIGNAL_CONTENTS, user_data, NULL);
+
+ g_object_unref (document);
+ return FALSE;
+}
+
+static gboolean
+request_idle_info (Request *request)
+{
+ YelpDocument *document;
+ YelpDocumentCallback callback = NULL;
+ gpointer user_data = user_data;
+
+ g_assert (request != NULL && YELP_IS_DOCUMENT (request->document));
+
+ if (g_cancellable_is_cancelled (request->cancellable)) {
+ request->idle_funcs--;
+ return FALSE;
+ }
+
+ document = g_object_ref (request->document);
+
+ g_mutex_lock (document->priv->mutex);
+
+ callback = request->callback;
+ user_data = request->user_data;
+ request->idle_funcs--;
+
+ g_mutex_unlock (document->priv->mutex);
+
+ if (callback)
+ callback (document, YELP_DOCUMENT_SIGNAL_INFO, user_data, NULL);
+
+ g_object_unref (document);
+ return FALSE;
+}
+
+static void
+request_try_free (Request *request)
+{
+ if (!g_cancellable_is_cancelled (request->cancellable))
+ g_cancellable_cancel (request->cancellable);
+
+ if (request->idle_funcs == 0)
+ request_free (request);
+ else
+ g_idle_add ((GSourceFunc) request_try_free, request);
+}
+
+static void
+request_free (Request *request)
+{
+ g_object_unref (request->document);
+ g_free (request->page_id);
+ g_object_unref (request->cancellable);
+
+ g_slice_free (Request, request);
+}
+
+/******************************************************************************/
+
+static const gchar *
+str_ref (const gchar *str)
+{
+ gpointer p;
+ guint i;
+
+ g_static_mutex_lock (&str_mutex);
+ if (str_refs == NULL)
+ str_refs = g_hash_table_new (g_direct_hash, g_direct_equal);
+ p = g_hash_table_lookup (str_refs, str);
+
+ i = GPOINTER_TO_UINT (p);
+ i++;
+ p = GUINT_TO_POINTER (i);
+
+ g_hash_table_insert (str_refs, (gpointer) str, p);
+ g_static_mutex_unlock (&str_mutex);
+
+ return str;
+}
+
+static void
+str_unref (const gchar *str)
+{
+ gpointer p;
+ guint i;
+
+ g_static_mutex_lock (&str_mutex);
+ p = g_hash_table_lookup (str_refs, str);
+
+ i = GPOINTER_TO_UINT (p);
+ i--;
+ p = GUINT_TO_POINTER (i);
+
+ if (i > 0) {
+ g_hash_table_insert (str_refs, (gpointer) str, p);
+ }
+ else {
+ g_hash_table_remove (str_refs, (gpointer) str);
+ g_free ((gchar *) str);
+ }
+
+ g_static_mutex_unlock (&str_mutex);
+}
+
+static void
+hash_slist_insert (GHashTable *hash,
+ const gchar *key,
+ gpointer value)
+{
+ GSList *list;
+ list = g_hash_table_lookup (hash, key);
+ if (list) {
+ list->next = g_slist_prepend (list->next, value);
+ } else {
+ list = g_slist_prepend (NULL, value);
+ list = g_slist_prepend (list, NULL);
+ g_hash_table_insert (hash, g_strdup (key), list);
+ }
+}
+
+static void
+hash_slist_remove (GHashTable *hash,
+ const gchar *key,
+ gpointer value)
+{
+ GSList *list;
+ list = g_hash_table_lookup (hash, key);
+ if (list) {
+ list = g_slist_remove (list, value);
+ if (list->next == NULL)
+ g_hash_table_remove (hash, key);
+ }
+}
+
+#if 0
+
+void
+yelp_document_add_page (YelpDocument *document, gchar *page_id, const gchar *contents)
+{
+ GSList *reqs, *cur;
+ Request *request;
+ YelpDocumentPriv *priv;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_ARG, " page_id = \"%s\"\n", page_id);
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ g_hash_table_replace (priv->contents,
+ g_strdup (page_id),
+ str_ref ((gchar *) contents));
+
+ reqs = g_hash_table_lookup (priv->reqs_by_page_id, page_id);
+ for (cur = reqs; cur != NULL; cur = cur->next) {
+ if (cur->data) {
+ request = (Request *) cur->data;
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_page, request);
+ }
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+gboolean
+yelp_document_has_page (YelpDocument *document, gchar *page_id)
+{
+ gchar *content;
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+ content = g_hash_table_lookup (document->priv->contents, page_id);
+ return !(content == NULL);
+}
+
+void
+yelp_document_error_request (YelpDocument *document, gint req_id, YelpError *error)
+{
+ Request *request;
+ YelpDocumentPriv *priv;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ request = g_hash_table_lookup (priv->reqs_by_req_id,
+ GINT_TO_POINTER (req_id));
+ if (request) {
+ request->error = error;
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_error, request);
+ } else {
+ yelp_error_free (error);
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+void
+yelp_document_error_page (YelpDocument *document, gchar *page_id, YelpError *error)
+{
+ GSList *requests;
+ Request *request = NULL;
+ YelpDocumentPriv *priv;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ priv = document->priv;
+ g_mutex_lock (priv->mutex);
+
+ requests = g_hash_table_lookup (priv->reqs_by_page_id, page_id);
+ while (requests) {
+ request = (Request *) requests->data;
+ if (request && request->error == NULL) {
+ request->error = yelp_error_copy (error);
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_error, request);
+ }
+ requests = requests->next;
+ }
+
+ yelp_error_free (error);
+
+ g_mutex_unlock (priv->mutex);
+}
+
+void
+yelp_document_error_pending (YelpDocument *document, YelpError *error)
+{
+ GSList *cur;
+ Request *request;
+ YelpDocumentPriv *priv;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ if (priv->reqs_pending) {
+ for (cur = priv->reqs_pending; cur; cur = cur->next) {
+ request = cur->data;
+ if (cur->next)
+ request->error = yelp_error_copy (error);
+ else
+ request->error = error;
+ request->idle_funcs++;
+ g_idle_add ((GSourceFunc) request_idle_error, request);
+ }
+
+ g_slist_free (priv->reqs_pending);
+ priv->reqs_pending = NULL;
+ } else {
+ yelp_error_free (error);
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+void
+yelp_document_final_pending (YelpDocument *document, YelpError *error)
+{
+ YelpDocumentPriv *priv;
+
+ g_assert (document != NULL && YELP_IS_DOCUMENT (document));
+
+ debug_print (DB_FUNCTION, "entering\n");
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+ if (priv->reqs_pending) {
+ priv->final_error = error;
+ g_idle_add ((GSourceFunc) request_idle_final, document);
+ } else {
+ yelp_error_free (error);
+ }
+
+ g_mutex_unlock (priv->mutex);
+}
+
+
+/******************************************************************************/
+
+static gboolean
+request_idle_error (Request *request)
+{
+ YelpDocument *document;
+ YelpDocumentPriv *priv;
+ YelpDocumentFunc func = NULL;
+ YelpError *error = NULL;
+ gint req_id = 0;
+ gpointer user_data = user_data;
+
+ g_assert (request != NULL && YELP_IS_DOCUMENT (request->document));
+
+ if (request->cancel) {
+ request->idle_funcs--;
+ return FALSE;
+ }
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ document = g_object_ref (request->document);
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ if (request->error) {
+ func = request->func;
+ req_id = request->req_id;
+ user_data = request->user_data;
+ error = request->error;
+ request->error = NULL;
+
+ priv->reqs_pending = g_slist_remove (priv->reqs_pending, request);
+ }
+
+ request->idle_funcs--;
+ g_mutex_unlock (priv->mutex);
+
+ if (func)
+ func (document,
+ YELP_DOCUMENT_SIGNAL_ERROR,
+ req_id,
+ error,
+ user_data);
+
+ g_object_unref (document);
+ return FALSE;
+}
+
+
+static gboolean
+request_idle_final (YelpDocument *document)
+{
+ YelpDocumentPriv *priv;
+ YelpDocumentFunc func = NULL;
+ YelpError *error = NULL;
+ gint req_id = 0;
+ gpointer user_data = user_data;
+ Request *request = NULL;
+ GSList *cur = NULL;
+
+ debug_print (DB_FUNCTION, "entering\n");
+
+ priv = document->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ if (priv->reqs_pending == NULL) {
+ /*
+ Time to bail as we shouldn't be here anyway.
+ */
+ g_mutex_unlock (priv->mutex);
+ return FALSE;
+ }
+
+ for (cur = priv->reqs_pending; cur; cur = cur->next) {
+ request = cur->data;
+ if (request->idle_funcs != 0) {
+ /*
+ While there are outstanding requests, we should wait for them
+ to complete before signalling the error
+ */
+ request->idle_funcs++;
+ g_mutex_unlock (priv->mutex);
+ return TRUE;
+ }
+ }
+
+ for (cur = priv->reqs_pending; cur; cur = cur->next) {
+ request = cur->data;
+
+ if (cur->next)
+ request->error = yelp_error_copy (priv->final_error);
+ else
+ request->error = error;
+
+ if (request->error) {
+ func = request->func;
+ req_id = request->req_id;
+ user_data = request->user_data;
+ error = request->error;
+ request->error = NULL;
+
+ priv->reqs_pending = g_slist_remove (priv->reqs_pending, request);
+ }
+
+
+ if (func)
+ func (document,
+ YELP_DOCUMENT_SIGNAL_ERROR,
+ req_id,
+ error,
+ user_data);
+ }
+ g_mutex_unlock (priv->mutex);
+
+ g_object_unref (document);
+ return FALSE;
+}
+
+/******************************************************************************/
+
+
+#endif
diff --git a/libyelp/yelp-document.h b/libyelp/yelp-document.h
new file mode 100644
index 0000000..e86d4f3
--- /dev/null
+++ b/libyelp/yelp-document.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance <shaunm gnome org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+#ifndef __YELP_DOCUMENT_H__
+#define __YELP_DOCUMENT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define YELP_TYPE_DOCUMENT (yelp_document_get_type ())
+#define YELP_DOCUMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_DOCUMENT, YelpDocument))
+#define YELP_DOCUMENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_DOCUMENT, YelpDocumentClass))
+#define YELP_IS_DOCUMENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_DOCUMENT))
+#define YELP_IS_DOCUMENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_DOCUMENT))
+#define YELP_DOCUMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_DOCUMENT, YelpDocumentClass))
+
+typedef struct _YelpDocument YelpDocument;
+typedef struct _YelpDocumentClass YelpDocumentClass;
+typedef struct _YelpDocumentPriv YelpDocumentPriv;
+
+typedef enum {
+ YELP_DOCUMENT_SIGNAL_CONTENTS,
+ YELP_DOCUMENT_SIGNAL_INFO,
+ YELP_DOCUMENT_SIGNAL_ERROR
+} YelpDocumentSignal;
+
+typedef void (*YelpDocumentCallback) (YelpDocument *document,
+ YelpDocumentSignal signal,
+ gpointer user_data,
+ GError *error);
+
+struct _YelpDocument {
+ GObject parent;
+ YelpDocumentPriv *priv;
+};
+
+struct _YelpDocumentClass {
+ GObjectClass parent_class;
+
+ /* Virtual Functions */
+ gboolean (*request_page) (YelpDocument *document,
+ const gchar *page_id,
+ GCancellable *cancellable,
+ YelpDocumentCallback callback,
+ gpointer user_data);
+ const gchar * (*read_contents) (YelpDocument *document,
+ const gchar *page_id);
+ void (*finish_read) (YelpDocument *document,
+ const gchar *contents);
+ gchar * (*get_mime_type) (YelpDocument *document,
+ const gchar *mime_type);
+
+};
+
+GType yelp_document_get_type (void);
+
+gboolean yelp_document_request_page (YelpDocument *document,
+ const gchar *page_id,
+ GCancellable *cancellable,
+ YelpDocumentCallback callback,
+ gpointer user_data);
+
+void yelp_document_take_contents (YelpDocument *document,
+ const gchar *page_id,
+ gchar *contents,
+ const gchar *mime);
+gchar * yelp_document_get_mime_type (YelpDocument *document,
+ const gchar *page_id);
+const gchar * yelp_document_read_contents (YelpDocument *document,
+ const gchar *page_id);
+void yelp_document_finish_read (YelpDocument *document,
+ const gchar *contents);
+
+gchar * yelp_document_get_page_id (YelpDocument *document,
+ const gchar *id);
+void yelp_document_set_page_id (YelpDocument *document,
+ const gchar *id,
+ const gchar *page_id);
+
+gchar * yelp_document_get_root_id (YelpDocument *document,
+ const gchar *page_id);
+void yelp_document_set_root_id (YelpDocument *document,
+ const gchar *page_id,
+ const gchar *root_id);
+
+gchar * yelp_document_get_prev_id (YelpDocument *document,
+ const gchar *page_id);
+void yelp_document_set_prev_id (YelpDocument *document,
+ const gchar *page_id,
+ const gchar *prev_id);
+
+char * yelp_document_get_next_id (YelpDocument *document,
+ const gchar *page_id);
+void yelp_document_set_next_id (YelpDocument *document,
+ const gchar *page_id,
+ const gchar *next_id);
+
+gchar * yelp_document_get_up_id (YelpDocument *document,
+ const gchar *page_id);
+void yelp_document_set_up_id (YelpDocument *document,
+ const gchar *page_id,
+ const gchar *up_id);
+
+gchar * yelp_document_get_title (YelpDocument *document,
+ const gchar *page_id);
+void yelp_document_set_title (YelpDocument *document,
+ const gchar *page_id,
+ const gchar *title);
+
+gboolean yelp_document_has_page (YelpDocument *document,
+ const gchar *page_id);
+
+void yelp_document_signal (YelpDocument *document,
+ const gchar *page_id,
+ YelpDocumentSignal signal,
+ GError *error);
+/* FIXME */
+/*
+void yelp_document_error_request (YelpDocument *document,
+ gint req_id,
+ YelpError *error);
+void yelp_document_error_page (YelpDocument *document,
+ gchar *page_id,
+ YelpError *error);
+void yelp_document_error_pending (YelpDocument *document,
+ YelpError *error);
+void yelp_document_final_pending (YelpDocument *document,
+ YelpError *error);
+*/
+
+
+#endif /* __YELP_DOCUMENT_H__ */
diff --git a/libyelp/yelp-simple-document.c b/libyelp/yelp-simple-document.c
new file mode 100644
index 0000000..099d456
--- /dev/null
+++ b/libyelp/yelp-simple-document.c
@@ -0,0 +1,419 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance <shaunm gnome org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "yelp-document.h"
+#include "yelp-simple-document.h"
+
+typedef struct _Request Request;
+struct _Request {
+ YelpDocument *document;
+ GCancellable *cancellable;
+ YelpDocumentCallback callback;
+ gpointer user_data;
+
+ gint idle_funcs;
+};
+
+struct _YelpSimpleDocumentPriv {
+ gchar *base_uri;
+
+ GFile *file;
+ GInputStream *stream;
+
+ gchar *contents;
+ gssize contents_len;
+ gssize contents_read;
+ gchar *mime_type;
+ gboolean finished;
+
+ GSList *reqs;
+};
+
+#define BUFFER_SIZE 4096
+
+G_DEFINE_TYPE (YelpSimpleDocument, yelp_simple_document, YELP_TYPE_DOCUMENT);
+#define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_SIMPLE_DOCUMENT, YelpSimpleDocumentPriv))
+
+static void yelp_simple_document_class_init (YelpSimpleDocumentClass *klass);
+static void yelp_simple_document_init (YelpSimpleDocument *document);
+static void yelp_simple_document_dispose (GObject *object);
+static void yelp_simple_document_finalize (GObject *object);
+
+static gboolean document_request_page (YelpDocument *document,
+ const gchar *page_id,
+ GCancellable *cancellable,
+ YelpDocumentCallback callback,
+ gpointer user_data);
+static const gchar * document_read_contents (YelpDocument *document,
+ const gchar *page_id);
+static void document_finish_read (YelpDocument *document,
+ const gchar *contents);
+static gchar * document_get_mime_type (YelpDocument *document,
+ const gchar *mime_type);
+static void document_signal_all (YelpSimpleDocument *document);
+
+static void file_info_cb (GFile *file,
+ GAsyncResult *result,
+ YelpSimpleDocument *document);
+static void file_read_cb (GFile *file,
+ GAsyncResult *result,
+ YelpSimpleDocument *document);
+static void stream_read_cb (GInputStream *stream,
+ GAsyncResult *result,
+ YelpSimpleDocument *document);
+static void stream_close_cb (GInputStream *stream,
+ GAsyncResult *result,
+ YelpSimpleDocument *document);
+
+static void request_cancel (Request *request);
+static void request_try_free (Request *request);
+static void request_free (Request *request);
+
+static void
+yelp_simple_document_class_init (YelpSimpleDocumentClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
+
+ object_class->dispose = yelp_simple_document_dispose;
+ object_class->finalize = yelp_simple_document_finalize;
+
+ document_class->request_page = document_request_page;
+ document_class->read_contents = document_read_contents;
+ document_class->finish_read = document_finish_read;
+ document_class->get_mime_type = document_get_mime_type;
+
+ g_type_class_add_private (klass, sizeof (YelpSimpleDocumentPriv));
+}
+
+static void
+yelp_simple_document_init (YelpSimpleDocument *document)
+{
+ document->priv = GET_PRIV (document);
+
+ document->priv->file = NULL;
+ document->priv->stream = NULL;
+
+ document->priv->finished = FALSE;
+ document->priv->contents = NULL;
+ document->priv->mime_type = NULL;
+}
+
+static void
+yelp_simple_document_dispose (GObject *object)
+{
+ YelpSimpleDocument *document = YELP_SIMPLE_DOCUMENT (object);
+
+ if (document->priv->reqs) {
+ g_slist_foreach (document->priv->reqs, (GFunc) request_try_free, NULL);
+ g_slist_free (document->priv->reqs);
+ document->priv->reqs = NULL;
+ }
+
+ if (document->priv->file) {
+ g_object_unref (document->priv->file);
+ document->priv->file = NULL;
+ }
+
+ if (document->priv->stream) {
+ g_object_unref (document->priv->stream);
+ document->priv->stream = NULL;
+ }
+
+ G_OBJECT_CLASS (yelp_simple_document_parent_class)->dispose (object);
+}
+
+static void
+yelp_simple_document_finalize (GObject *object)
+{
+ YelpSimpleDocument *document = YELP_SIMPLE_DOCUMENT (object);
+
+ g_free (document->priv->base_uri);
+
+ g_free (document->priv->contents);
+ g_free (document->priv->mime_type);
+
+ G_OBJECT_CLASS (yelp_simple_document_parent_class)->finalize (object);
+}
+
+YelpDocument *
+yelp_simple_document_new (YelpUri *uri)
+{
+ YelpSimpleDocument *document;
+
+ document = (YelpSimpleDocument *) g_object_new (YELP_TYPE_SIMPLE_DOCUMENT, NULL);
+
+ document->priv->base_uri = yelp_uri_get_base_uri (uri);
+
+ return (YelpDocument *) document;
+}
+
+/******************************************************************************/
+
+static gboolean
+document_request_page (YelpDocument *document,
+ const gchar *page_id,
+ GCancellable *cancellable,
+ YelpDocumentCallback callback,
+ gpointer user_data)
+{
+ YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
+ Request *request;
+ gboolean ret = FALSE;
+
+ request = g_slice_new0 (Request);
+
+ request->document = g_object_ref (document);
+ request->callback = callback;
+ request->user_data = user_data;
+
+ request->cancellable = g_object_ref (cancellable);
+ g_signal_connect (cancellable, "cancelled",
+ G_CALLBACK (request_cancel), request);
+
+ simple->priv->reqs = g_slist_prepend (simple->priv->reqs, request);
+
+ if (simple->priv->finished) {
+ g_idle_add ((GSourceFunc) document_signal_all, simple);
+ ret = TRUE;
+ }
+ else if (simple->priv->file == NULL) {
+ simple->priv->file = g_file_new_for_uri (simple->priv->base_uri);
+ g_file_query_info_async (simple->priv->file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback) file_info_cb,
+ document);
+ }
+
+ return ret;
+}
+
+static const gchar *
+document_read_contents (YelpDocument *document,
+ const gchar *page_id)
+{
+ YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
+
+ return (const gchar*) simple->priv->contents;
+}
+
+static void
+document_finish_read (YelpDocument *document,
+ const gchar *contents)
+{
+ YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
+
+ if (simple->priv->reqs == NULL) {
+ g_free (simple->priv->contents);
+ simple->priv->contents = NULL;
+
+ g_free (simple->priv->mime_type);
+ simple->priv->mime_type = NULL;
+ }
+}
+
+static gchar *
+document_get_mime_type (YelpDocument *document,
+ const gchar *mime_type)
+{
+ YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
+
+ if (simple->priv->mime_type)
+ return g_strdup (simple->priv->mime_type);
+ else
+ return NULL;
+}
+
+static void
+document_signal_all (YelpSimpleDocument *document)
+{
+ GSList *cur;
+ for (cur = document->priv->reqs; cur != NULL; cur = cur->next) {
+ Request *request = (Request *) cur->data;
+ if (request->callback)
+ request->callback (request->document,
+ YELP_DOCUMENT_SIGNAL_CONTENTS,
+ request->user_data,
+ NULL);
+ }
+}
+
+/******************************************************************************/
+
+static void
+file_info_cb (GFile *file,
+ GAsyncResult *result,
+ YelpSimpleDocument *document)
+{
+ GFileInfo *info = g_file_query_info_finish (file, result, NULL);
+ const gchar *type = g_file_info_get_attribute_string (info,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+ document->priv->mime_type = g_strdup (type);
+ g_object_unref (info);
+ g_file_read_async (document->priv->file,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback) file_read_cb,
+ document);
+}
+
+static void
+file_read_cb (GFile *file,
+ GAsyncResult *result,
+ YelpSimpleDocument *document)
+{
+ GError *error = NULL;
+
+ document->priv->stream = (GInputStream *) g_file_read_finish (file, result, &error);
+
+ if (document->priv->stream == NULL) {
+ GSList *cur;
+ for (cur = document->priv->reqs; cur != NULL; cur = cur->next) {
+ Request *request = (Request *) cur->data;
+ if (request->callback) {
+ GError *err = g_error_copy (error);
+ request->callback (request->document,
+ YELP_DOCUMENT_SIGNAL_ERROR,
+ request->user_data,
+ err);
+ g_error_free (err);
+ }
+ }
+ g_error_free (error);
+ return;
+ }
+
+ g_assert (document->priv->contents == NULL);
+ document->priv->contents_len = BUFFER_SIZE;
+ document->priv->contents = g_realloc (document->priv->contents,
+ document->priv->contents_len);
+ document->priv->contents[0] = '\0';
+ document->priv->contents_read = 0;
+ g_input_stream_read_async (document->priv->stream,
+ document->priv->contents,
+ BUFFER_SIZE,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback) stream_read_cb,
+ document);
+}
+
+static void
+stream_read_cb (GInputStream *stream,
+ GAsyncResult *result,
+ YelpSimpleDocument *document)
+{
+ gssize bytes;
+
+ bytes = g_input_stream_read_finish (stream, result, NULL);
+ document->priv->contents_read += bytes;
+
+ if (bytes == 0) {
+ /* If the preceding read filled contents, it was extended before the
+ read that gave us zero bytes. Otherwise, there's room for this
+ byte. I'm 99.99% certain I'm right.
+ */
+ g_assert (document->priv->contents_read < document->priv->contents_len);
+ document->priv->contents[document->priv->contents_read + 1] = '\0';
+ g_input_stream_close_async (document->priv->stream,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback) stream_close_cb,
+ document);
+ return;
+ }
+
+ if (document->priv->contents_read == document->priv->contents_len) {
+ document->priv->contents_len = document->priv->contents_read + BUFFER_SIZE;
+ document->priv->contents = g_realloc (document->priv->contents,
+ document->priv->contents_len);
+ }
+ g_input_stream_read_async (document->priv->stream,
+ document->priv->contents + document->priv->contents_read,
+ document->priv->contents_len - document->priv->contents_read,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback) stream_read_cb,
+ document);
+}
+
+static void
+stream_close_cb (GInputStream *stream,
+ GAsyncResult *result,
+ YelpSimpleDocument *document)
+{
+ GSList *cur;
+
+ document->priv->finished = TRUE;
+ document_signal_all (document);
+}
+
+/******************************************************************************/
+
+static void
+request_cancel (Request *request)
+{
+ GSList *cur;
+ YelpSimpleDocument *document = request->document;
+
+ g_assert (document != NULL && YELP_IS_SIMPLE_DOCUMENT (document));
+
+ for (cur = document->priv->reqs; cur != NULL; cur = cur->next) {
+ if (cur->data == request) {
+ document->priv->reqs = g_slist_delete_link (document->priv->reqs, cur);
+ break;
+ }
+ }
+ request_try_free (request);
+}
+
+static void
+request_try_free (Request *request)
+{
+ if (!g_cancellable_is_cancelled (request->cancellable))
+ g_cancellable_cancel (request->cancellable);
+
+ if (request->idle_funcs == 0)
+ request_free (request);
+ else
+ g_idle_add ((GSourceFunc) request_try_free, request);
+}
+
+static void
+request_free (Request *request)
+{
+ g_object_unref (request->document);
+ g_object_unref (request->cancellable);
+
+ g_slice_free (Request, request);
+}
diff --git a/libyelp/yelp-simple-document.h b/libyelp/yelp-simple-document.h
new file mode 100644
index 0000000..aec9182
--- /dev/null
+++ b/libyelp/yelp-simple-document.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance <shaunm gnome org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+#ifndef __YELP_SIMPLE_DOCUMENT_H__
+#define __YELP_SIMPLE_DOCUMENT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "yelp-document.h"
+#include "yelp-uri.h"
+
+#define YELP_TYPE_SIMPLE_DOCUMENT (yelp_simple_document_get_type ())
+#define YELP_SIMPLE_DOCUMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_SIMPLE_DOCUMENT, YelpSimpleDocument))
+#define YELP_SIMPLE_DOCUMENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_SIMPLE_DOCUMENT, YelpSimpleDocumentClass))
+#define YELP_IS_SIMPLE_DOCUMENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_SIMPLE_DOCUMENT))
+#define YELP_IS_SIMPLE_DOCUMENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_SIMPLE_DOCUMENT))
+#define YELP_SIMPLE_DOCUMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_SIMPLE_DOCUMENT, YelpSimpleDocumentClass))
+
+typedef struct _YelpSimpleDocument YelpSimpleDocument;
+typedef struct _YelpSimpleDocumentClass YelpSimpleDocumentClass;
+typedef struct _YelpSimpleDocumentPriv YelpSimpleDocumentPriv;
+
+struct _YelpSimpleDocument {
+ YelpDocument parent;
+ YelpSimpleDocumentPriv *priv;
+};
+
+struct _YelpSimpleDocumentClass {
+ YelpDocumentClass parent_class;
+};
+
+GType yelp_simple_document_get_type (void);
+YelpDocument * yelp_simple_document_new (YelpUri *uri);
+
+#endif /* __YELP_SIMPLE_DOCUMENT_H__ */
diff --git a/libyelp/yelp-uri.c b/libyelp/yelp-uri.c
index a2f0ddb..580e533 100644
--- a/libyelp/yelp-uri.c
+++ b/libyelp/yelp-uri.c
@@ -32,6 +32,7 @@
#include "yelp-uri.h"
#include "yelp-debug.h"
+G_DEFINE_TYPE (YelpUri, yelp_uri, G_TYPE_OBJECT);
#define YELP_URI_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_URI, YelpUriPriv))
struct _YelpUriPriv {
@@ -42,9 +43,10 @@ struct _YelpUriPriv {
gchar *frag_id;
};
-static void uri_class_init (YelpUriClass *klass);
-static void uri_init (YelpUri *uri);
-static void uri_dispose (GObject *object);
+static void yelp_uri_class_init (YelpUriClass *klass);
+static void yelp_uri_init (YelpUri *uri);
+static void yelp_uri_dispose (GObject *object);
+static void yelp_uri_finalize (GObject *object);
static void resolve_file_uri (YelpUri *ret,
gchar *arg);
@@ -88,54 +90,49 @@ static const gchar *mancats[] = {
/******************************************************************************/
-GType
-yelp_uri_get_type (void)
-{
- static GType type = 0;
- if (!type) {
- static const GTypeInfo info = {
- sizeof (YelpUriClass),
- NULL, NULL,
- (GClassInitFunc) uri_class_init,
- NULL, NULL,
- sizeof (YelpUri),
- 0,
- (GInstanceInitFunc) uri_init,
- };
- type = g_type_register_static (G_TYPE_OBJECT,
- "YelpUri",
- &info, 0);
- }
- return type;
-}
-
static void
-uri_class_init (YelpUriClass *klass)
+yelp_uri_class_init (YelpUriClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- parent_class = g_type_class_peek_parent (klass);
- object_class->dispose = uri_dispose;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = yelp_uri_dispose;
+ object_class->finalize = yelp_uri_finalize;
+
g_type_class_add_private (klass, sizeof (YelpUriPriv));
}
static void
-uri_init (YelpUri *uri)
+yelp_uri_init (YelpUri *uri)
{
uri->priv = YELP_URI_GET_PRIVATE (uri);
}
static void
-uri_dispose (GObject *object)
+yelp_uri_dispose (GObject *object)
{
YelpUri *uri = YELP_URI (object);
- if (uri->priv->gfile)
+ if (uri->priv->gfile) {
g_object_unref (uri->priv->gfile);
+ uri->priv->gfile = NULL;
+ }
+ g_strfreev (uri->priv->search_path);
+ g_free (uri->priv->page_id);
+ g_free (uri->priv->frag_id);
+
+ G_OBJECT_CLASS (yelp_uri_parent_class)->dispose (object);
+}
+
+static void
+yelp_uri_finalize (GObject *object)
+{
+ YelpUri *uri = YELP_URI (object);
+
g_strfreev (uri->priv->search_path);
g_free (uri->priv->page_id);
g_free (uri->priv->frag_id);
- parent_class->dispose (object);
+ G_OBJECT_CLASS (yelp_uri_parent_class)->finalize (object);
}
/******************************************************************************/
diff --git a/libyelp/yelp-view.c b/libyelp/yelp-view.c
index 2cc83cf..4403388 100644
--- a/libyelp/yelp-view.c
+++ b/libyelp/yelp-view.c
@@ -31,20 +31,28 @@
#include "yelp-view.h"
-static void view_init (YelpView *view);
-static void view_class_init (YelpViewClass *klass);
+static void yelp_view_init (YelpView *view);
+static void yelp_view_class_init (YelpViewClass *klass);
+static void yelp_view_dispose (GObject *object);
+static void yelp_view_finalize (GObject *object);
+
+static void document_callback (YelpDocument *document,
+ YelpDocumentSignal signal,
+ YelpView *view,
+ GError *error);
enum {
NEW_VIEW_REQUESTED,
LAST_SIGNAL
};
static gint signals[LAST_SIGNAL] = { 0 };
-static GObjectClass *parent_class = NULL;
-#define YELP_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_VIEW, YelpViewPriv))
+G_DEFINE_TYPE (YelpView, yelp_view, WEBKIT_TYPE_WEB_VIEW);
+#define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_VIEW, YelpViewPriv))
struct _YelpViewPriv {
- WebKitWebView *webview;
+ YelpUri *uri;
+ GCancellable *cancellable;
};
#define TARGET_TYPE_URI_LIST "text/uri-list"
@@ -52,64 +60,48 @@ enum {
TARGET_URI_LIST
};
-GType
-yelp_view_get_type (void)
-{
- static GType view_type = 0;
-
- if (!view_type) {
- static const GTypeInfo view_info = {
- sizeof (YelpViewClass),
- NULL,
- NULL,
- (GClassInitFunc) view_class_init,
- NULL,
- NULL,
- sizeof (YelpView),
- 0,
- (GInstanceInitFunc) view_init,
- };
- view_type = g_type_register_static (YELP_TYPE_VIEW,
- "YelpView",
- &view_info, 0);
- }
- return view_type;
-}
-
static void
-view_init (YelpView *view)
+yelp_view_init (YelpView *view)
{
- view->priv = YELP_VIEW_GET_PRIVATE (view);
- view->priv->webview = (WebKitWebView *) webkit_web_view_new ();
- gtk_container_add (GTK_CONTAINER (view), (GtkWidget *) view->priv->webview);
+ view->priv = GET_PRIV (view);
+
+ view->priv->cancellable = NULL;
}
static void
-view_dispose (GObject *object)
+yelp_view_dispose (GObject *object)
{
- parent_class->dispose (object);
+ YelpView *view = YELP_VIEW (object);
+
+ if (view->priv->uri) {
+ g_object_unref (view->priv->uri);
+ view->priv->uri = NULL;
+ }
+
+ if (view->priv->cancellable) {
+ g_cancellable_cancel (view->priv->cancellable);
+ g_object_unref (view->priv->cancellable);
+ view->priv->cancellable = NULL;
+ }
+
+ G_OBJECT_CLASS (yelp_view_parent_class)->dispose (object);
}
static void
-view_finalize (GObject *object)
+yelp_view_finalize (GObject *object)
{
YelpView *view = YELP_VIEW (object);
- YelpViewPriv *priv = view->priv;
-
- /* FIXME Free stuff */
- parent_class->finalize (object);
+ G_OBJECT_CLASS (yelp_view_parent_class)->finalize (object);
}
static void
-view_class_init (YelpViewClass *klass)
+yelp_view_class_init (YelpViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- parent_class = (GObjectClass *) g_type_class_peek_parent (klass);
-
- object_class->dispose = view_dispose;
- object_class->finalize = view_finalize;
+ object_class->dispose = yelp_view_dispose;
+ object_class->finalize = yelp_view_finalize;
signals[NEW_VIEW_REQUESTED] =
g_signal_new ("new_view_requested",
@@ -121,3 +113,68 @@ view_class_init (YelpViewClass *klass)
g_type_class_add_private (klass, sizeof (YelpViewPriv));
}
+
+/******************************************************************************/
+
+GtkWidget *
+yelp_view_new (void)
+{
+ return (GtkWidget *) g_object_new (YELP_TYPE_VIEW, NULL);
+}
+
+void
+yelp_view_load (YelpView *view,
+ YelpUri *uri)
+{
+ /* FIXME: get a document and call load_document */
+}
+
+void
+yelp_view_load_document (YelpView *view,
+ YelpUri *uri,
+ YelpDocument *document)
+{
+ gchar *page_id;
+
+ /* FIXME: unset previous load */
+
+ page_id = yelp_uri_get_page_id (uri);
+ view->priv->uri = g_object_ref (uri);
+ view->priv->cancellable = g_cancellable_new ();
+ yelp_document_request_page (document,
+ page_id,
+ view->priv->cancellable,
+ (YelpDocumentCallback) document_callback,
+ view);
+
+ g_free (page_id);
+}
+
+static void
+document_callback (YelpDocument *document,
+ YelpDocumentSignal signal,
+ YelpView *view,
+ GError *error)
+{
+ if (signal == YELP_DOCUMENT_SIGNAL_INFO) {
+ }
+ else if (signal == YELP_DOCUMENT_SIGNAL_CONTENTS) {
+ const gchar *contents = yelp_document_read_contents (document, NULL);
+ gchar *base_uri, *mime_type, *page_id;
+ base_uri = yelp_uri_get_base_uri (view->priv->uri);
+ page_id = yelp_uri_get_page_id (view->priv->uri);
+ mime_type = yelp_document_get_mime_type (document, page_id);
+ webkit_web_view_load_string (WEBKIT_WEB_VIEW (view),
+ contents,
+ mime_type,
+ "UTF-8",
+ base_uri);
+ g_free (page_id);
+ g_free (mime_type);
+ g_free (base_uri);
+ yelp_document_finish_read (document, contents);
+ }
+ else if (signal == YELP_DOCUMENT_SIGNAL_ERROR) {
+ printf ("ERROR: %s\n", error->message);
+ }
+}
diff --git a/libyelp/yelp-view.h b/libyelp/yelp-view.h
index 0b1e3df..a5bafd8 100644
--- a/libyelp/yelp-view.h
+++ b/libyelp/yelp-view.h
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
- * Copyright (C) 2001-2002 Mikael Hallendal <micke imendio com>
+ * Copyright (C) 2009 Shaun McCance <shaunm gnome org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -17,13 +17,17 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
- * Author: Mikael Hallendal <micke imendio com>
+ * Author: Shaun McCance <shaunm gnome org>
*/
#ifndef __YELP_VIEW_H__
#define __YELP_VIEW_H__
#include <gtk/gtk.h>
+#include <webkit/webkit.h>
+
+#include "yelp-document.h"
+#include "yelp-uri.h"
#define YELP_TYPE_VIEW (yelp_view_get_type ())
#define YELP_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YELP_TYPE_VIEW, YelpView))
@@ -37,19 +41,22 @@ typedef struct _YelpViewPriv YelpViewPriv;
struct _YelpView
{
- GtkContainer parent;
- YelpViewPriv *priv;
+ WebKitWebView parent;
+ YelpViewPriv *priv;
};
struct _YelpViewClass
{
- GtkContainerClass parent_class;
+ WebKitWebViewClass parent_class;
};
GType yelp_view_get_type (void);
-GtkWidget * yelp_view_new (GNode *doc_tree,
- GList *index);
-void yelp_view_load (YelpView *view,
- const gchar *uri);
+GtkWidget * yelp_view_new (void);
+
+void yelp_view_load (YelpView *view,
+ YelpUri *uri);
+void yelp_view_load_document (YelpView *view,
+ YelpUri *uri,
+ YelpDocument *document);
#endif /* __YELP_VIEW_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b1efc6a..d9cfd0f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -9,10 +9,14 @@ YELP_COMMON_LDADD = \
check_PROGRAMS = \
test-location-entry \
- test-uri
+ test-uri \
+ test-view
test_location_entry_CFLAGS = $(YELP_COMMON_CFLAGS)
test_location_entry_LDADD = $(YELP_COMMON_LDADD)
test_uri_CFLAGS = $(YELP_COMMON_CFLAGS)
test_uri_LDADD = $(YELP_COMMON_LDADD)
+
+test_view_CFLAGS = $(YELP_COMMON_CFLAGS)
+test_view_LDADD = $(YELP_COMMON_LDADD)
diff --git a/tests/test-view.c b/tests/test-view.c
new file mode 100644
index 0000000..99a70f0
--- /dev/null
+++ b/tests/test-view.c
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance <shaunm gnome org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+#include <gtk/gtk.h>
+#include <webkit/webkit.h>
+#include "yelp-view.h"
+#include "yelp-uri.h"
+#include "yelp-simple-document.h"
+
+int
+main (int argc, char **argv)
+{
+ GtkWidget *window, *scroll, *view;
+ YelpUri *uri;
+ YelpDocument *document;
+ GCancellable *cancellable;
+
+ gtk_init (&argc, &argv);
+
+ g_thread_init (NULL);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
+
+ scroll = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_container_add (GTK_CONTAINER (window), scroll);
+
+ view = yelp_view_new ();
+ gtk_container_add (GTK_CONTAINER (scroll), view);
+
+ g_assert (argc >= 2);
+ uri = yelp_uri_resolve (argv[1]);
+ document = yelp_simple_document_new (uri);
+ yelp_view_load_document (view, uri, document);
+
+ gtk_widget_show_all (GTK_WIDGET (window));
+
+ gtk_main ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]