1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
28 #include <glib/gi18n.h>
31 #include "yelp-document.h"
32 #include "yelp-simple-document.h"
34 typedef struct _Request Request
;
36 YelpDocument
*document
;
37 GCancellable
*cancellable
;
38 YelpDocumentCallback callback
;
44 struct _YelpSimpleDocumentPriv
{
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
,
70 GCancellable
*cancellable
,
71 YelpDocumentCallback callback
,
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
,
83 YelpSimpleDocument
*document
);
84 static void file_read_cb (GFile
*file
,
86 YelpSimpleDocument
*document
);
87 static void stream_read_cb (GInputStream
*stream
,
89 YelpSimpleDocument
*document
);
90 static void stream_close_cb (GInputStream
*stream
,
92 YelpSimpleDocument
*document
);
94 static void request_cancel (GCancellable
*cancellable
,
96 static gboolean
request_try_free (Request
*request
);
97 static void request_free (Request
*request
);
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
));
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
;
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
);
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
);
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 /******************************************************************************/
180 document_request_page (YelpDocument
*document
,
181 const gchar
*page_id
,
182 GCancellable
*cancellable
,
183 YelpDocumentCallback callback
,
186 YelpSimpleDocument
*simple
= YELP_SIMPLE_DOCUMENT (document
);
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
);
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
,
213 (GAsyncReadyCallback
) file_info_cb
,
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
;
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
;
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
);
257 document_signal_all (YelpSimpleDocument
*document
)
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
,
267 request
->callback (request
->document
,
268 YELP_DOCUMENT_SIGNAL_CONTENTS
,
276 /******************************************************************************/
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");
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");
301 g_file_read_async (document
->priv
->file
,
304 (GAsyncReadyCallback
) file_read_cb
,
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
) {
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
,
330 g_error_free (error
);
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
,
345 (GAsyncReadyCallback
) stream_read_cb
,
350 stream_read_cb (GInputStream
*stream
,
351 GAsyncResult
*result
,
352 YelpSimpleDocument
*document
)
356 bytes
= g_input_stream_read_finish (stream
, result
, NULL
);
357 document
->priv
->contents_read
+= bytes
;
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
,
369 (GAsyncReadyCallback
) stream_close_cb
,
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
,
384 (GAsyncReadyCallback
) stream_read_cb
,
389 stream_close_cb (GInputStream
*stream
,
390 GAsyncResult
*result
,
391 YelpSimpleDocument
*document
)
395 document
->priv
->finished
= TRUE
;
396 document_signal_all (document
);
399 /******************************************************************************/
402 request_cancel (GCancellable
*cancellable
, Request
*request
)
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
);
415 request_try_free (request
);
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
);
427 g_idle_add ((GSourceFunc
) request_try_free
, request
);
432 request_free (Request
*request
)
434 g_object_unref (request
->document
);
435 g_object_unref (request
->cancellable
);
437 g_slice_free (Request
, request
);