Provide xslt translation domain for install link text
[yelp.git] / libyelp / yelp-info-document.c
blob136b27472631422fa862477d060192b03974de84
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2007 Don Scorgie <dscorgie@svn.gnome.org>
4 * Copyright (C) 2010 Shaun McCance <shaunm@gnome.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
21 * Author: Don Scorgie <dscorgie@svn.gnome.org>
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include <glib.h>
29 #include <glib/gi18n.h>
30 #include <gtk/gtk.h>
31 #include <libxml/tree.h>
33 #include "yelp-error.h"
34 #include "yelp-info-document.h"
35 #include "yelp-info-parser.h"
36 #include "yelp-transform.h"
37 #include "yelp-debug.h"
38 #include "yelp-settings.h"
40 #define STYLESHEET DATADIR"/yelp/xslt/info2html.xsl"
42 typedef enum {
43 INFO_STATE_BLANK, /* Brand new, run transform as needed */
44 INFO_STATE_PARSING, /* Parsing/transforming document, please wait */
45 INFO_STATE_PARSED, /* All done, if we ain't got it, it ain't here */
46 INFO_STATE_STOP /* Stop everything now, object to be disposed */
47 } InfoState;
49 typedef struct _YelpInfoDocumentPrivate YelpInfoDocumentPrivate;
50 struct _YelpInfoDocumentPrivate {
51 YelpUri *uri;
52 InfoState state;
54 GMutex *mutex;
55 GThread *thread;
57 xmlDocPtr xmldoc;
58 GtkTreeModel *sections;
60 gboolean process_running;
61 gboolean transform_running;
63 YelpTransform *transform;
64 guint chunk_ready;
65 guint finished;
66 guint error;
68 gchar *root_id;
69 gchar *visit_prev_id;
73 static void yelp_info_document_class_init (YelpInfoDocumentClass *klass);
74 static void yelp_info_document_init (YelpInfoDocument *info);
75 static void yelp_info_document_dispose (GObject *object);
76 static void yelp_info_document_finalize (GObject *object);
78 /* YelpDocument */
79 static gboolean info_request_page (YelpDocument *document,
80 const gchar *page_id,
81 GCancellable *cancellable,
82 YelpDocumentCallback callback,
83 gpointer user_data);
85 /* YelpTransform */
86 static void transform_chunk_ready (YelpTransform *transform,
87 gchar *chunk_id,
88 YelpInfoDocument *info);
89 static void transform_finished (YelpTransform *transform,
90 YelpInfoDocument *info);
91 static void transform_error (YelpTransform *transform,
92 YelpInfoDocument *info);
93 static void transform_finalized (YelpInfoDocument *info,
94 gpointer transform);
96 static void info_document_process (YelpInfoDocument *info);
97 static gboolean info_sections_visit (GtkTreeModel *model,
98 GtkTreePath *path,
99 GtkTreeIter *iter,
100 YelpInfoDocument *info);
101 static void info_document_disconnect (YelpInfoDocument *info);
104 G_DEFINE_TYPE (YelpInfoDocument, yelp_info_document, YELP_TYPE_DOCUMENT);
105 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_INFO_DOCUMENT, YelpInfoDocumentPrivate))
107 static void
108 yelp_info_document_class_init (YelpInfoDocumentClass *klass)
110 GObjectClass *object_class = G_OBJECT_CLASS (klass);
111 YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
113 object_class->dispose = yelp_info_document_dispose;
114 object_class->finalize = yelp_info_document_finalize;
116 document_class->request_page = info_request_page;
118 g_type_class_add_private (klass, sizeof (YelpInfoDocumentPrivate));
121 static void
122 yelp_info_document_init (YelpInfoDocument *info)
124 YelpInfoDocumentPrivate *priv = GET_PRIV (info);
126 priv->state = INFO_STATE_BLANK;
127 priv->xmldoc = NULL;
128 priv->mutex = g_mutex_new ();
131 static void
132 yelp_info_document_dispose (GObject *object)
134 YelpInfoDocumentPrivate *priv = GET_PRIV (object);
136 if (priv->uri) {
137 g_object_unref (priv->uri);
138 priv->uri = NULL;
141 if (priv->sections) {
142 g_object_unref (priv->sections);
143 priv->sections = NULL;
146 if (priv->transform) {
147 g_object_unref (priv->transform);
148 priv->transform = NULL;
151 G_OBJECT_CLASS (yelp_info_document_parent_class)->dispose (object);
154 static void
155 yelp_info_document_finalize (GObject *object)
157 YelpInfoDocumentPrivate *priv = GET_PRIV (object);
159 if (priv->xmldoc)
160 xmlFreeDoc (priv->xmldoc);
162 g_free (priv->root_id);
163 g_free (priv->visit_prev_id);
165 g_mutex_free (priv->mutex);
167 G_OBJECT_CLASS (yelp_info_document_parent_class)->finalize (object);
170 /******************************************************************************/
172 YelpDocument *
173 yelp_info_document_new (YelpUri *uri)
175 YelpInfoDocument *info;
176 YelpInfoDocumentPrivate *priv;
177 gchar *doc_uri;
179 g_return_val_if_fail (uri != NULL, NULL);
181 doc_uri = yelp_uri_get_document_uri (uri);
182 info = (YelpInfoDocument *) g_object_new (YELP_TYPE_INFO_DOCUMENT,
183 "document-uri", doc_uri,
184 NULL);
185 g_free (doc_uri);
186 priv = GET_PRIV (info);
188 priv->uri = g_object_ref (uri);
190 return (YelpDocument *) info;
194 /******************************************************************************/
195 /** YelpDocument **************************************************************/
197 static gboolean
198 info_request_page (YelpDocument *document,
199 const gchar *page_id,
200 GCancellable *cancellable,
201 YelpDocumentCallback callback,
202 gpointer user_data)
204 YelpInfoDocumentPrivate *priv = GET_PRIV (document);
205 gchar *docuri;
206 GError *error;
207 gboolean handled;
209 if (page_id == NULL)
210 page_id = priv->root_id;
212 handled =
213 YELP_DOCUMENT_CLASS (yelp_info_document_parent_class)->request_page (document,
214 page_id,
215 cancellable,
216 callback,
217 user_data);
218 if (handled) {
219 return TRUE;
222 g_mutex_lock (priv->mutex);
224 switch (priv->state) {
225 case INFO_STATE_BLANK:
226 priv->state = INFO_STATE_PARSING;
227 priv->process_running = TRUE;
228 g_object_ref (document);
229 priv->thread = g_thread_create ((GThreadFunc) info_document_process,
230 document, FALSE, NULL);
231 break;
232 case INFO_STATE_PARSING:
233 break;
234 case INFO_STATE_PARSED:
235 case INFO_STATE_STOP:
236 docuri = yelp_uri_get_document_uri (priv->uri);
237 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
238 _("The page ‘%s’ was not found in the document ‘%s’."),
239 page_id, docuri);
240 g_free (docuri);
241 yelp_document_signal (document, page_id,
242 YELP_DOCUMENT_SIGNAL_ERROR,
243 error);
244 g_error_free (error);
245 break;
248 g_mutex_unlock (priv->mutex);
249 return TRUE;
253 /******************************************************************************/
254 /** YelpTransform *************************************************************/
256 static void
257 transform_chunk_ready (YelpTransform *transform,
258 gchar *chunk_id,
259 YelpInfoDocument *info)
261 YelpInfoDocumentPrivate *priv = GET_PRIV (info);
262 gchar *content;
264 g_assert (transform == priv->transform);
266 if (priv->state == INFO_STATE_STOP) {
267 info_document_disconnect (info);
268 return;
271 content = yelp_transform_take_chunk (transform, chunk_id);
272 yelp_document_give_contents (YELP_DOCUMENT (info),
273 chunk_id,
274 content,
275 "application/xhtml+xml");
277 yelp_document_signal (YELP_DOCUMENT (info),
278 chunk_id,
279 YELP_DOCUMENT_SIGNAL_INFO,
280 NULL);
281 yelp_document_signal (YELP_DOCUMENT (info),
282 chunk_id,
283 YELP_DOCUMENT_SIGNAL_CONTENTS,
284 NULL);
287 static void
288 transform_finished (YelpTransform *transform,
289 YelpInfoDocument *info)
291 YelpInfoDocumentPrivate *priv = GET_PRIV (info);
292 gchar *docuri;
293 GError *error;
295 g_assert (transform == priv->transform);
297 if (priv->state == INFO_STATE_STOP) {
298 info_document_disconnect (info);
299 return;
302 info_document_disconnect (info);
303 priv->state = INFO_STATE_PARSED;
305 /* We want to free priv->xmldoc, but we can't free it before transform
306 is finalized. Otherwise, we could crash when YelpTransform frees
307 its libxslt resources.
309 g_object_weak_ref ((GObject *) transform,
310 (GWeakNotify) transform_finalized,
311 info);
313 docuri = yelp_uri_get_document_uri (priv->uri);
314 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
315 _("The requested page was not found in the document ‘%s’."),
316 docuri);
317 g_free (docuri);
318 yelp_document_error_pending ((YelpDocument *) info, error);
319 g_error_free (error);
322 static void
323 transform_error (YelpTransform *transform,
324 YelpInfoDocument *info)
326 YelpInfoDocumentPrivate *priv = GET_PRIV (info);
327 GError *error;
329 g_assert (transform == priv->transform);
331 if (priv->state == INFO_STATE_STOP) {
332 info_document_disconnect (info);
333 return;
336 error = yelp_transform_get_error (transform);
337 yelp_document_error_pending ((YelpDocument *) info, error);
338 g_error_free (error);
340 info_document_disconnect (info);
343 static void
344 transform_finalized (YelpInfoDocument *info,
345 gpointer transform)
347 YelpInfoDocumentPrivate *priv = GET_PRIV (info);
349 if (priv->xmldoc)
350 xmlFreeDoc (priv->xmldoc);
351 priv->xmldoc = NULL;
356 /******************************************************************************/
357 /** Threaded ******************************************************************/
359 static void
360 info_document_process (YelpInfoDocument *info)
362 YelpInfoDocumentPrivate *priv = GET_PRIV (info);
363 GFile *file = NULL;
364 gchar *filepath = NULL;
365 GError *error;
366 gint params_i = 0;
367 gchar **params = NULL;
369 file = yelp_uri_get_file (priv->uri);
370 if (file == NULL) {
371 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
372 _("The file does not exist."));
373 yelp_document_error_pending ((YelpDocument *) info, error);
374 g_error_free (error);
375 goto done;
378 filepath = g_file_get_path (file);
379 g_object_unref (file);
380 if (!g_file_test (filepath, G_FILE_TEST_IS_REGULAR)) {
381 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
382 _("The file ‘%s’ does not exist."),
383 filepath);
384 yelp_document_error_pending ((YelpDocument *) info, error);
385 g_error_free (error);
386 goto done;
389 priv->sections = (GtkTreeModel *) yelp_info_parser_parse_file (filepath);
390 gtk_tree_model_foreach (priv->sections,
391 (GtkTreeModelForeachFunc) info_sections_visit,
392 info);
393 priv->xmldoc = yelp_info_parser_parse_tree ((GtkTreeStore *) priv->sections);
395 if (priv->xmldoc == NULL) {
396 error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING,
397 _("The file ‘%s’ could not be parsed because it is"
398 " not a well-formed info page."),
399 filepath);
400 yelp_document_error_pending ((YelpDocument *) info, error);
401 goto done;
404 g_mutex_lock (priv->mutex);
405 if (priv->state == INFO_STATE_STOP) {
406 g_mutex_unlock (priv->mutex);
407 goto done;
410 priv->transform = yelp_transform_new (STYLESHEET);
411 priv->chunk_ready =
412 g_signal_connect (priv->transform, "chunk-ready",
413 (GCallback) transform_chunk_ready,
414 info);
415 priv->finished =
416 g_signal_connect (priv->transform, "finished",
417 (GCallback) transform_finished,
418 info);
419 priv->error =
420 g_signal_connect (priv->transform, "error",
421 (GCallback) transform_error,
422 info);
424 params = yelp_settings_get_all_params (yelp_settings_get_default (), 0, &params_i);
426 priv->transform_running = TRUE;
427 yelp_transform_start (priv->transform,
428 priv->xmldoc,
429 NULL,
430 (const gchar * const *) params);
431 g_strfreev (params);
432 g_mutex_unlock (priv->mutex);
434 done:
435 g_free (filepath);
436 priv->process_running = FALSE;
437 g_object_unref (info);
440 static gboolean
441 info_sections_visit (GtkTreeModel *model,
442 GtkTreePath *path,
443 GtkTreeIter *iter,
444 YelpInfoDocument *info)
446 YelpInfoDocumentPrivate *priv = GET_PRIV (info);
447 gchar *page_id, *title;
449 gtk_tree_model_get (model, iter,
450 INFO_PARSER_COLUMN_PAGE_NO, &page_id,
451 INFO_PARSER_COLUMN_PAGE_NAME, &title,
452 -1);
453 yelp_document_set_page_id ((YelpDocument *) info, page_id, page_id);
454 yelp_document_set_page_title ((YelpDocument *) info, page_id, title);
456 if (priv->root_id == NULL) {
457 priv->root_id = g_strdup (page_id);
458 yelp_document_set_page_id ((YelpDocument *) info, NULL, page_id);
460 yelp_document_set_root_id ((YelpDocument *) info, page_id, priv->root_id);
462 if (priv->visit_prev_id != NULL) {
463 yelp_document_set_prev_id ((YelpDocument *) info, page_id, priv->visit_prev_id);
464 yelp_document_set_next_id ((YelpDocument *) info, priv->visit_prev_id, page_id);
465 g_free (priv->visit_prev_id);
467 priv->visit_prev_id = page_id;
468 g_free (title);
469 return FALSE;
472 static void
473 info_document_disconnect (YelpInfoDocument *info)
475 YelpInfoDocumentPrivate *priv = GET_PRIV (info);
476 if (priv->chunk_ready) {
477 g_signal_handler_disconnect (priv->transform, priv->chunk_ready);
478 priv->chunk_ready = 0;
480 if (priv->finished) {
481 g_signal_handler_disconnect (priv->transform, priv->finished);
482 priv->finished = 0;
484 if (priv->error) {
485 g_signal_handler_disconnect (priv->transform, priv->error);
486 priv->error = 0;
488 yelp_transform_cancel (priv->transform);
489 g_object_unref (priv->transform);
490 priv->transform = NULL;
491 priv->transform_running = FALSE;