[libyelp/yelp-view] Use GAppLaunchContext instead of gdk_spawn
[yelp.git] / libyelp / yelp-simple-document.c
blobde62a9ba33e2cfb531911787e0b18bfce6f37989
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2003-2009 Shaun McCance <shaunm@gnome.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Shaun McCance <shaunm@gnome.org>
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gio/gio.h>
31 #include "yelp-document.h"
32 #include "yelp-simple-document.h"
34 typedef struct _Request Request;
35 struct _Request {
36 YelpDocument *document;
37 GCancellable *cancellable;
38 YelpDocumentCallback callback;
39 gpointer user_data;
41 gint idle_funcs;
44 struct _YelpSimpleDocumentPriv {
45 GFile *file;
46 GInputStream *stream;
48 gchar *contents;
49 gssize contents_len;
50 gssize contents_read;
51 gchar *mime_type;
52 gboolean started;
53 gboolean finished;
55 GSList *reqs;
58 #define BUFFER_SIZE 4096
60 G_DEFINE_TYPE (YelpSimpleDocument, yelp_simple_document, YELP_TYPE_DOCUMENT);
61 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_SIMPLE_DOCUMENT, YelpSimpleDocumentPriv))
63 static void yelp_simple_document_class_init (YelpSimpleDocumentClass *klass);
64 static void yelp_simple_document_init (YelpSimpleDocument *document);
65 static void yelp_simple_document_dispose (GObject *object);
66 static void yelp_simple_document_finalize (GObject *object);
68 static gboolean document_request_page (YelpDocument *document,
69 const gchar *page_id,
70 GCancellable *cancellable,
71 YelpDocumentCallback callback,
72 gpointer user_data);
73 static const gchar * document_read_contents (YelpDocument *document,
74 const gchar *page_id);
75 static void document_finish_read (YelpDocument *document,
76 const gchar *contents);
77 static gchar * document_get_mime_type (YelpDocument *document,
78 const gchar *mime_type);
79 static gboolean document_signal_all (YelpSimpleDocument *document);
81 static void file_info_cb (GFile *file,
82 GAsyncResult *result,
83 YelpSimpleDocument *document);
84 static void file_read_cb (GFile *file,
85 GAsyncResult *result,
86 YelpSimpleDocument *document);
87 static void stream_read_cb (GInputStream *stream,
88 GAsyncResult *result,
89 YelpSimpleDocument *document);
90 static void stream_close_cb (GInputStream *stream,
91 GAsyncResult *result,
92 YelpSimpleDocument *document);
94 static void request_cancel (GCancellable *cancellable,
95 Request *request);
96 static gboolean request_try_free (Request *request);
97 static void request_free (Request *request);
99 static void
100 yelp_simple_document_class_init (YelpSimpleDocumentClass *klass)
102 GObjectClass *object_class = G_OBJECT_CLASS (klass);
103 YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
105 object_class->dispose = yelp_simple_document_dispose;
106 object_class->finalize = yelp_simple_document_finalize;
108 document_class->request_page = document_request_page;
109 document_class->read_contents = document_read_contents;
110 document_class->finish_read = document_finish_read;
111 document_class->get_mime_type = document_get_mime_type;
113 g_type_class_add_private (klass, sizeof (YelpSimpleDocumentPriv));
116 static void
117 yelp_simple_document_init (YelpSimpleDocument *document)
119 document->priv = GET_PRIV (document);
121 document->priv->file = NULL;
122 document->priv->stream = NULL;
124 document->priv->started = FALSE;
125 document->priv->finished = FALSE;
126 document->priv->contents = NULL;
127 document->priv->mime_type = NULL;
130 static void
131 yelp_simple_document_dispose (GObject *object)
133 YelpSimpleDocument *document = YELP_SIMPLE_DOCUMENT (object);
135 if (document->priv->reqs) {
136 g_slist_foreach (document->priv->reqs, (GFunc) request_try_free, NULL);
137 g_slist_free (document->priv->reqs);
138 document->priv->reqs = NULL;
141 if (document->priv->file) {
142 g_object_unref (document->priv->file);
143 document->priv->file = NULL;
146 if (document->priv->stream) {
147 g_object_unref (document->priv->stream);
148 document->priv->stream = NULL;
151 G_OBJECT_CLASS (yelp_simple_document_parent_class)->dispose (object);
154 static void
155 yelp_simple_document_finalize (GObject *object)
157 YelpSimpleDocument *document = YELP_SIMPLE_DOCUMENT (object);
159 g_free (document->priv->contents);
160 g_free (document->priv->mime_type);
162 G_OBJECT_CLASS (yelp_simple_document_parent_class)->finalize (object);
165 YelpDocument *
166 yelp_simple_document_new (YelpUri *uri)
168 YelpSimpleDocument *document;
170 document = (YelpSimpleDocument *) g_object_new (YELP_TYPE_SIMPLE_DOCUMENT, NULL);
172 document->priv->file = yelp_uri_get_file (uri);
174 return (YelpDocument *) document;
177 /******************************************************************************/
179 static gboolean
180 document_request_page (YelpDocument *document,
181 const gchar *page_id,
182 GCancellable *cancellable,
183 YelpDocumentCallback callback,
184 gpointer user_data)
186 YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
187 Request *request;
188 gboolean ret = FALSE;
190 request = g_slice_new0 (Request);
192 request->document = g_object_ref (document);
193 request->callback = callback;
194 request->user_data = user_data;
196 request->cancellable = g_object_ref (cancellable);
197 g_signal_connect (cancellable, "cancelled",
198 G_CALLBACK (request_cancel), request);
200 simple->priv->reqs = g_slist_prepend (simple->priv->reqs, request);
202 if (simple->priv->finished) {
203 g_idle_add ((GSourceFunc) document_signal_all, simple);
204 ret = TRUE;
206 else if (!simple->priv->started) {
207 simple->priv->started = TRUE;
208 g_file_query_info_async (simple->priv->file,
209 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
210 G_FILE_QUERY_INFO_NONE,
211 G_PRIORITY_DEFAULT,
212 NULL,
213 (GAsyncReadyCallback) file_info_cb,
214 document);
217 return ret;
220 static const gchar *
221 document_read_contents (YelpDocument *document,
222 const gchar *page_id)
224 YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
226 return (const gchar*) simple->priv->contents;
229 static void
230 document_finish_read (YelpDocument *document,
231 const gchar *contents)
233 YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
235 if (simple->priv->reqs == NULL) {
236 g_free (simple->priv->contents);
237 simple->priv->contents = NULL;
239 g_free (simple->priv->mime_type);
240 simple->priv->mime_type = NULL;
244 static gchar *
245 document_get_mime_type (YelpDocument *document,
246 const gchar *mime_type)
248 YelpSimpleDocument *simple = YELP_SIMPLE_DOCUMENT (document);
250 if (simple->priv->mime_type)
251 return g_strdup (simple->priv->mime_type);
252 else
253 return NULL;
256 static gboolean
257 document_signal_all (YelpSimpleDocument *document)
259 GSList *cur;
260 for (cur = document->priv->reqs; cur != NULL; cur = cur->next) {
261 Request *request = (Request *) cur->data;
262 if (request->callback) {
263 request->callback (request->document,
264 YELP_DOCUMENT_SIGNAL_INFO,
265 request->user_data,
266 NULL);
267 request->callback (request->document,
268 YELP_DOCUMENT_SIGNAL_CONTENTS,
269 request->user_data,
270 NULL);
273 return FALSE;
276 /******************************************************************************/
278 static void
279 file_info_cb (GFile *file,
280 GAsyncResult *result,
281 YelpSimpleDocument *document)
283 GFileInfo *info = g_file_query_info_finish (file, result, NULL);
284 const gchar *type = g_file_info_get_attribute_string (info,
285 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
286 if (g_str_equal (type, "text/x-readme"))
287 document->priv->mime_type = g_strdup ("text/plain");
288 else
289 document->priv->mime_type = g_strdup (type);
290 g_object_unref (info);
292 if (g_str_equal (document->priv->mime_type, "text/plain")) {
293 gchar *basename = g_file_get_basename (document->priv->file);
294 yelp_document_set_page_id (YELP_DOCUMENT (document), "//index", "//index");
295 yelp_document_set_page_id (YELP_DOCUMENT (document), NULL, "//index");
296 yelp_document_set_page_title (YELP_DOCUMENT (document), "//index", basename);
297 yelp_document_set_page_icon (YELP_DOCUMENT (document), "//index", "text-x-generic");
298 g_free (basename);
301 g_file_read_async (document->priv->file,
302 G_PRIORITY_DEFAULT,
303 NULL,
304 (GAsyncReadyCallback) file_read_cb,
305 document);
308 static void
309 file_read_cb (GFile *file,
310 GAsyncResult *result,
311 YelpSimpleDocument *document)
313 GError *error = NULL;
315 document->priv->stream = (GInputStream *) g_file_read_finish (file, result, &error);
317 if (document->priv->stream == NULL) {
318 GSList *cur;
319 for (cur = document->priv->reqs; cur != NULL; cur = cur->next) {
320 Request *request = (Request *) cur->data;
321 if (request->callback) {
322 GError *err = g_error_copy (error);
323 request->callback (request->document,
324 YELP_DOCUMENT_SIGNAL_ERROR,
325 request->user_data,
326 err);
327 g_error_free (err);
330 g_error_free (error);
331 return;
334 g_assert (document->priv->contents == NULL);
335 document->priv->contents_len = BUFFER_SIZE;
336 document->priv->contents = g_realloc (document->priv->contents,
337 document->priv->contents_len);
338 document->priv->contents[0] = '\0';
339 document->priv->contents_read = 0;
340 g_input_stream_read_async (document->priv->stream,
341 document->priv->contents,
342 BUFFER_SIZE,
343 G_PRIORITY_DEFAULT,
344 NULL,
345 (GAsyncReadyCallback) stream_read_cb,
346 document);
349 static void
350 stream_read_cb (GInputStream *stream,
351 GAsyncResult *result,
352 YelpSimpleDocument *document)
354 gssize bytes;
356 bytes = g_input_stream_read_finish (stream, result, NULL);
357 document->priv->contents_read += bytes;
359 if (bytes == 0) {
360 /* If the preceding read filled contents, it was extended before the
361 read that gave us zero bytes. Otherwise, there's room for this
362 byte. I'm 99.99% certain I'm right.
364 g_assert (document->priv->contents_read < document->priv->contents_len);
365 document->priv->contents[document->priv->contents_read + 1] = '\0';
366 g_input_stream_close_async (document->priv->stream,
367 G_PRIORITY_DEFAULT,
368 NULL,
369 (GAsyncReadyCallback) stream_close_cb,
370 document);
371 return;
374 if (document->priv->contents_read == document->priv->contents_len) {
375 document->priv->contents_len = document->priv->contents_read + BUFFER_SIZE;
376 document->priv->contents = g_realloc (document->priv->contents,
377 document->priv->contents_len);
379 g_input_stream_read_async (document->priv->stream,
380 document->priv->contents + document->priv->contents_read,
381 document->priv->contents_len - document->priv->contents_read,
382 G_PRIORITY_DEFAULT,
383 NULL,
384 (GAsyncReadyCallback) stream_read_cb,
385 document);
388 static void
389 stream_close_cb (GInputStream *stream,
390 GAsyncResult *result,
391 YelpSimpleDocument *document)
393 GSList *cur;
395 document->priv->finished = TRUE;
396 document_signal_all (document);
399 /******************************************************************************/
401 static void
402 request_cancel (GCancellable *cancellable, Request *request)
404 GSList *cur;
405 YelpSimpleDocument *document = (YelpSimpleDocument *) request->document;
407 g_assert (document != NULL && YELP_IS_SIMPLE_DOCUMENT (document));
409 for (cur = document->priv->reqs; cur != NULL; cur = cur->next) {
410 if (cur->data == request) {
411 document->priv->reqs = g_slist_delete_link (document->priv->reqs, cur);
412 break;
415 request_try_free (request);
418 static gboolean
419 request_try_free (Request *request)
421 if (!g_cancellable_is_cancelled (request->cancellable))
422 g_cancellable_cancel (request->cancellable);
424 if (request->idle_funcs == 0)
425 request_free (request);
426 else
427 g_idle_add ((GSourceFunc) request_try_free, request);
428 return FALSE;
431 static void
432 request_free (Request *request)
434 g_object_unref (request->document);
435 g_object_unref (request->cancellable);
437 g_slice_free (Request, request);