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-debug.h"
32 #include "yelp-document.h"
33 #include "yelp-error.h"
34 #include "yelp-docbook-document.h"
35 #include "yelp-help-list.h"
36 #include "yelp-info-document.h"
37 #include "yelp-mallard-document.h"
38 #include "yelp-man-document.h"
39 #include "yelp-settings.h"
40 #include "yelp-simple-document.h"
41 #include "yelp-storage.h"
49 typedef struct _Request Request
;
51 YelpDocument
*document
;
53 GCancellable
*cancellable
;
54 YelpDocumentCallback callback
;
61 typedef struct _Hash Hash
;
65 GDestroyNotify destroy
;
68 struct _YelpDocumentPriv
{
71 GSList
*reqs_all
; /* Holds canonical refs, only free from here */
72 Hash
*reqs_by_page_id
; /* Indexed by page ID, contains GSList */
73 GSList
*reqs_pending
; /* List of requests that need a page */
75 GSList
*reqs_search
; /* Pending search requests, not in reqs_all */
80 /* Real page IDs map to themselves, so this list doubles
81 * as a list of all valid page IDs.
83 GHashTable
*core_ids
; /* Mapping of real IDs to themselves, for a set */
84 Hash
*page_ids
; /* Mapping of fragment IDs to real page IDs */
85 Hash
*titles
; /* Mapping of page IDs to titles */
86 Hash
*descs
; /* Mapping of page IDs to descs */
87 Hash
*icons
; /* Mapping of page IDs to icons */
88 Hash
*mime_types
; /* Mapping of page IDs to mime types */
89 Hash
*contents
; /* Mapping of page IDs to string content */
91 Hash
*root_ids
; /* Mapping of page IDs to "root page" IDs */
92 Hash
*prev_ids
; /* Mapping of page IDs to "previous page" IDs */
93 Hash
*next_ids
; /* Mapping of page IDs to "next page" IDs */
94 Hash
*up_ids
; /* Mapping of page IDs to "up page" IDs */
99 G_DEFINE_TYPE (YelpDocument
, yelp_document
, G_TYPE_OBJECT
);
101 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCUMENT, YelpDocumentPriv))
103 static void yelp_document_class_init (YelpDocumentClass
*klass
);
104 static void yelp_document_init (YelpDocument
*document
);
105 static void yelp_document_dispose (GObject
*object
);
106 static void yelp_document_finalize (GObject
*object
);
107 static void document_get_property (GObject
*object
,
111 static void document_set_property (GObject
*object
,
115 static gboolean
document_request_page (YelpDocument
*document
,
116 const gchar
*page_id
,
117 GCancellable
*cancellable
,
118 YelpDocumentCallback callback
,
120 static void document_indexed (YelpDocument
*document
);
121 static const gchar
* document_read_contents (YelpDocument
*document
,
122 const gchar
*page_id
);
123 static void document_finish_read (YelpDocument
*document
,
124 const gchar
*contents
);
125 static gchar
* document_get_mime_type (YelpDocument
*document
,
126 const gchar
*mime_type
);
127 static void document_index (YelpDocument
*document
);
129 static Hash
* hash_new (GDestroyNotify destroy
);
130 static void hash_free (Hash
*hash
);
131 static gpointer
hash_lookup (Hash
*hash
,
133 static void hash_replace (Hash
*hash
,
136 static void hash_remove (Hash
*hash
,
138 static void hash_slist_insert (Hash
*hash
,
141 static void hash_slist_remove (Hash
*hash
,
145 static void request_cancel (GCancellable
*cancellable
,
147 static gboolean
request_idle_contents (Request
*request
);
148 static gboolean
request_idle_info (Request
*request
);
149 static gboolean
request_idle_error (Request
*request
);
150 static gboolean
request_try_free (Request
*request
);
151 static void request_free (Request
*request
);
153 static const gchar
* str_ref (const gchar
*str
);
154 static void str_unref (const gchar
*str
);
156 GStaticMutex str_mutex
= G_STATIC_MUTEX_INIT
;
157 GHashTable
*str_refs
= NULL
;
159 /******************************************************************************/
162 yelp_document_get_for_uri (YelpUri
*uri
)
164 static GHashTable
*documents
= NULL
;
165 gchar
*docuri
= NULL
;
166 gchar
*page_id
, *tmp
;
167 YelpDocument
*document
= NULL
;
169 if (documents
== NULL
)
170 documents
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
171 g_free
, g_object_unref
);
173 g_return_val_if_fail (yelp_uri_is_resolved (uri
), NULL
);
175 switch (yelp_uri_get_document_type (uri
)) {
176 case YELP_URI_DOCUMENT_TYPE_TEXT
:
177 case YELP_URI_DOCUMENT_TYPE_HTML
:
178 case YELP_URI_DOCUMENT_TYPE_XHTML
:
179 /* We use YelpSimpleDocument for these, which is a single-file
180 * responder. But the document URI may be set to the directory
181 * holding the file, to allow a directory of HTML files to act
182 * as a single document. So we cache these by a fuller URI.
184 docuri
= yelp_uri_get_document_uri (uri
);
185 page_id
= yelp_uri_get_page_id (uri
);
186 tmp
= g_strconcat (docuri
, "/", page_id
, NULL
);
191 case YELP_URI_DOCUMENT_TYPE_MAN
:
192 /* The document URI for man pages is just man:, so we use the
193 * full canonical URI to look these up.
195 docuri
= yelp_uri_get_canonical_uri (uri
);
198 docuri
= yelp_uri_get_document_uri (uri
);
204 document
= g_hash_table_lookup (documents
, docuri
);
206 if (document
!= NULL
) {
208 return g_object_ref (document
);
211 switch (yelp_uri_get_document_type (uri
)) {
212 case YELP_URI_DOCUMENT_TYPE_TEXT
:
213 case YELP_URI_DOCUMENT_TYPE_HTML
:
214 case YELP_URI_DOCUMENT_TYPE_XHTML
:
215 document
= yelp_simple_document_new (uri
);
217 case YELP_URI_DOCUMENT_TYPE_DOCBOOK
:
218 document
= yelp_docbook_document_new (uri
);
220 case YELP_URI_DOCUMENT_TYPE_MALLARD
:
221 document
= yelp_mallard_document_new (uri
);
223 case YELP_URI_DOCUMENT_TYPE_MAN
:
224 document
= yelp_man_document_new (uri
);
226 case YELP_URI_DOCUMENT_TYPE_INFO
:
227 document
= yelp_info_document_new (uri
);
229 case YELP_URI_DOCUMENT_TYPE_HELP_LIST
:
230 document
= yelp_help_list_new (uri
);
232 case YELP_URI_DOCUMENT_TYPE_NOT_FOUND
:
233 case YELP_URI_DOCUMENT_TYPE_EXTERNAL
:
234 case YELP_URI_DOCUMENT_TYPE_ERROR
:
238 if (document
!= NULL
) {
239 g_hash_table_insert (documents
, docuri
, document
);
240 return g_object_ref (document
);
247 /******************************************************************************/
250 yelp_document_class_init (YelpDocumentClass
*klass
)
252 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
254 object_class
->dispose
= yelp_document_dispose
;
255 object_class
->finalize
= yelp_document_finalize
;
256 object_class
->get_property
= document_get_property
;
257 object_class
->set_property
= document_set_property
;
259 klass
->request_page
= document_request_page
;
260 klass
->read_contents
= document_read_contents
;
261 klass
->finish_read
= document_finish_read
;
262 klass
->get_mime_type
= document_get_mime_type
;
263 klass
->index
= document_index
;
265 g_object_class_install_property (object_class
,
267 g_param_spec_boolean ("indexed",
269 N_("Whether the document content has been indexed"),
271 G_PARAM_CONSTRUCT
| G_PARAM_READWRITE
|
272 G_PARAM_STATIC_STRINGS
));
274 g_object_class_install_property (object_class
,
276 g_param_spec_string ("document-uri",
278 N_("The URI which identifies the document"),
280 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
281 G_PARAM_STATIC_STRINGS
));
283 g_type_class_add_private (klass
, sizeof (YelpDocumentPriv
));
287 yelp_document_init (YelpDocument
*document
)
289 YelpDocumentPriv
*priv
;
291 document
->priv
= priv
= GET_PRIV (document
);
293 priv
->mutex
= g_mutex_new ();
295 priv
->reqs_by_page_id
= hash_new ((GDestroyNotify
) g_slist_free
);
296 priv
->reqs_all
= NULL
;
297 priv
->reqs_pending
= NULL
;
298 priv
->reqs_search
= NULL
;
300 priv
->core_ids
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, NULL
);
301 priv
->page_ids
= hash_new (g_free
);
302 priv
->titles
= hash_new (g_free
);
303 priv
->descs
= hash_new (g_free
);
304 priv
->icons
= hash_new (g_free
);
305 priv
->mime_types
= hash_new (g_free
);
306 priv
->contents
= hash_new ((GDestroyNotify
) str_unref
);
308 priv
->root_ids
= hash_new (g_free
);
309 priv
->prev_ids
= hash_new (g_free
);
310 priv
->next_ids
= hash_new (g_free
);
311 priv
->up_ids
= hash_new (g_free
);
315 yelp_document_dispose (GObject
*object
)
317 YelpDocument
*document
= YELP_DOCUMENT (object
);
319 if (document
->priv
->reqs_all
) {
320 g_slist_foreach (document
->priv
->reqs_all
,
321 (GFunc
) request_try_free
,
323 g_slist_free (document
->priv
->reqs_all
);
324 document
->priv
->reqs_all
= NULL
;
327 if (document
->priv
->reqs_search
) {
328 g_slist_foreach (document
->priv
->reqs_search
,
329 (GFunc
) request_try_free
,
331 g_slist_free (document
->priv
->reqs_search
);
332 document
->priv
->reqs_search
= NULL
;
335 G_OBJECT_CLASS (yelp_document_parent_class
)->dispose (object
);
339 yelp_document_finalize (GObject
*object
)
341 YelpDocument
*document
= YELP_DOCUMENT (object
);
343 g_slist_free (document
->priv
->reqs_pending
);
344 hash_free (document
->priv
->reqs_by_page_id
);
346 hash_free (document
->priv
->page_ids
);
347 hash_free (document
->priv
->titles
);
348 hash_free (document
->priv
->descs
);
349 hash_free (document
->priv
->icons
);
350 hash_free (document
->priv
->mime_types
);
352 hash_free (document
->priv
->contents
);
354 hash_free (document
->priv
->root_ids
);
355 hash_free (document
->priv
->prev_ids
);
356 hash_free (document
->priv
->next_ids
);
357 hash_free (document
->priv
->up_ids
);
359 g_hash_table_destroy (document
->priv
->core_ids
);
361 g_mutex_free (document
->priv
->mutex
);
363 G_OBJECT_CLASS (yelp_document_parent_class
)->finalize (object
);
367 document_get_property (GObject
*object
,
372 YelpDocument
*document
= YELP_DOCUMENT (object
);
376 g_value_set_boolean (value
, document
->priv
->indexed
);
379 g_value_set_string (value
, document
->priv
->doc_uri
);
382 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
388 document_set_property (GObject
*object
,
393 YelpDocument
*document
= YELP_DOCUMENT (object
);
397 document
->priv
->indexed
= g_value_get_boolean (value
);
398 if (document
->priv
->indexed
)
399 g_idle_add ((GSourceFunc
) document_indexed
, document
);
402 if (document
->priv
->doc_uri
!= NULL
)
403 g_free (document
->priv
->doc_uri
);
404 document
->priv
->doc_uri
= g_value_dup_string (value
);
407 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
412 /******************************************************************************/
415 yelp_document_list_page_ids (YelpDocument
*document
)
421 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
423 g_mutex_lock (document
->priv
->mutex
);
425 lst
= g_hash_table_get_keys (document
->priv
->core_ids
);
426 ret
= g_new0 (gchar
*, g_list_length (lst
) + 1);
428 for (cur
= lst
; cur
!= NULL
; cur
= cur
->next
) {
429 ret
[i
] = g_strdup ((gchar
*) cur
->data
);
434 g_mutex_unlock (document
->priv
->mutex
);
440 yelp_document_get_page_id (YelpDocument
*document
,
445 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
447 if (id
!= NULL
&& g_str_has_prefix (id
, "search="))
448 return g_strdup (id
);
450 g_mutex_lock (document
->priv
->mutex
);
451 ret
= hash_lookup (document
->priv
->page_ids
, id
);
453 ret
= g_strdup (ret
);
455 g_mutex_unlock (document
->priv
->mutex
);
461 yelp_document_set_page_id (YelpDocument
*document
,
463 const gchar
*page_id
)
465 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
467 g_mutex_lock (document
->priv
->mutex
);
469 hash_replace (document
->priv
->page_ids
, id
, g_strdup (page_id
));
471 if (id
== NULL
|| !g_str_equal (id
, page_id
)) {
473 reqs
= hash_lookup (document
->priv
->reqs_by_page_id
, id
);
474 for (cur
= reqs
; cur
!= NULL
; cur
= cur
->next
) {
475 Request
*request
= (Request
*) cur
->data
;
478 g_free (request
->page_id
);
479 request
->page_id
= g_strdup (page_id
);
480 hash_slist_insert (document
->priv
->reqs_by_page_id
, page_id
, request
);
483 hash_remove (document
->priv
->reqs_by_page_id
, id
);
487 if (page_id
!= NULL
) {
488 if (g_hash_table_lookup (document
->priv
->core_ids
, page_id
) == NULL
) {
489 gchar
*ins
= g_strdup (page_id
);
490 g_hash_table_insert (document
->priv
->core_ids
, ins
, ins
);
494 g_mutex_unlock (document
->priv
->mutex
);
498 yelp_document_get_root_id (YelpDocument
*document
,
499 const gchar
*page_id
)
501 gchar
*real
, *ret
= NULL
;
503 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
505 g_mutex_lock (document
->priv
->mutex
);
506 if (page_id
!= NULL
&& g_str_has_prefix (page_id
, "search="))
507 real
= hash_lookup (document
->priv
->page_ids
, NULL
);
509 real
= hash_lookup (document
->priv
->page_ids
, page_id
);
511 ret
= hash_lookup (document
->priv
->root_ids
, real
);
513 ret
= g_strdup (ret
);
515 g_mutex_unlock (document
->priv
->mutex
);
521 yelp_document_set_root_id (YelpDocument
*document
,
522 const gchar
*page_id
,
523 const gchar
*root_id
)
525 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
527 g_mutex_lock (document
->priv
->mutex
);
528 hash_replace (document
->priv
->root_ids
, page_id
, g_strdup (root_id
));
529 g_mutex_unlock (document
->priv
->mutex
);
533 yelp_document_get_prev_id (YelpDocument
*document
,
534 const gchar
*page_id
)
536 gchar
*real
, *ret
= NULL
;
538 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
540 g_mutex_lock (document
->priv
->mutex
);
541 real
= hash_lookup (document
->priv
->page_ids
, page_id
);
543 ret
= hash_lookup (document
->priv
->prev_ids
, real
);
545 ret
= g_strdup (ret
);
547 g_mutex_unlock (document
->priv
->mutex
);
553 yelp_document_set_prev_id (YelpDocument
*document
,
554 const gchar
*page_id
,
555 const gchar
*prev_id
)
557 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
559 g_mutex_lock (document
->priv
->mutex
);
560 hash_replace (document
->priv
->prev_ids
, page_id
, g_strdup (prev_id
));
561 g_mutex_unlock (document
->priv
->mutex
);
565 yelp_document_get_next_id (YelpDocument
*document
,
566 const gchar
*page_id
)
568 gchar
*real
, *ret
= NULL
;
570 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
572 g_mutex_lock (document
->priv
->mutex
);
573 real
= hash_lookup (document
->priv
->page_ids
, page_id
);
575 ret
= hash_lookup (document
->priv
->next_ids
, real
);
577 ret
= g_strdup (ret
);
579 g_mutex_unlock (document
->priv
->mutex
);
585 yelp_document_set_next_id (YelpDocument
*document
,
586 const gchar
*page_id
,
587 const gchar
*next_id
)
589 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
591 g_mutex_lock (document
->priv
->mutex
);
592 hash_replace (document
->priv
->next_ids
, page_id
, g_strdup (next_id
));
593 g_mutex_unlock (document
->priv
->mutex
);
597 yelp_document_get_up_id (YelpDocument
*document
,
598 const gchar
*page_id
)
600 gchar
*real
, *ret
= NULL
;
602 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
604 g_mutex_lock (document
->priv
->mutex
);
605 real
= hash_lookup (document
->priv
->page_ids
, page_id
);
607 ret
= hash_lookup (document
->priv
->up_ids
, real
);
609 ret
= g_strdup (ret
);
611 g_mutex_unlock (document
->priv
->mutex
);
617 yelp_document_set_up_id (YelpDocument
*document
,
618 const gchar
*page_id
,
621 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
623 g_mutex_lock (document
->priv
->mutex
);
624 hash_replace (document
->priv
->up_ids
, page_id
, g_strdup (up_id
));
625 g_mutex_unlock (document
->priv
->mutex
);
629 yelp_document_get_root_title (YelpDocument
*document
,
630 const gchar
*page_id
)
632 gchar
*real
, *root
, *ret
= NULL
;
634 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
636 g_mutex_lock (document
->priv
->mutex
);
637 if (page_id
!= NULL
&& g_str_has_prefix (page_id
, "search=")) {
638 ret
= yelp_storage_get_root_title (yelp_storage_get_default (),
639 document
->priv
->doc_uri
);
642 real
= hash_lookup (document
->priv
->page_ids
, page_id
);
644 root
= hash_lookup (document
->priv
->root_ids
, real
);
646 ret
= hash_lookup (document
->priv
->titles
, root
);
648 ret
= g_strdup (ret
);
653 g_mutex_unlock (document
->priv
->mutex
);
659 yelp_document_get_page_title (YelpDocument
*document
,
660 const gchar
*page_id
)
662 gchar
*real
, *ret
= NULL
;
664 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
666 if (page_id
!= NULL
&& g_str_has_prefix (page_id
, "search=")) {
667 ret
= g_uri_unescape_string (page_id
+ 7, NULL
);
671 g_mutex_lock (document
->priv
->mutex
);
672 real
= hash_lookup (document
->priv
->page_ids
, page_id
);
674 ret
= hash_lookup (document
->priv
->titles
, real
);
676 ret
= g_strdup (ret
);
678 g_mutex_unlock (document
->priv
->mutex
);
684 yelp_document_set_page_title (YelpDocument
*document
,
685 const gchar
*page_id
,
688 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
690 g_mutex_lock (document
->priv
->mutex
);
691 hash_replace (document
->priv
->titles
, page_id
, g_strdup (title
));
692 g_mutex_unlock (document
->priv
->mutex
);
696 yelp_document_get_page_desc (YelpDocument
*document
,
697 const gchar
*page_id
)
699 gchar
*real
, *ret
= NULL
;
701 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
703 if (page_id
!= NULL
&& g_str_has_prefix (page_id
, "search="))
704 return yelp_document_get_root_title (document
, page_id
);
706 g_mutex_lock (document
->priv
->mutex
);
707 real
= hash_lookup (document
->priv
->page_ids
, page_id
);
709 ret
= hash_lookup (document
->priv
->descs
, real
);
711 ret
= g_strdup (ret
);
713 g_mutex_unlock (document
->priv
->mutex
);
719 yelp_document_set_page_desc (YelpDocument
*document
,
720 const gchar
*page_id
,
723 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
725 g_mutex_lock (document
->priv
->mutex
);
726 hash_replace (document
->priv
->descs
, page_id
, g_strdup (desc
));
727 g_mutex_unlock (document
->priv
->mutex
);
731 yelp_document_get_page_icon (YelpDocument
*document
,
732 const gchar
*page_id
)
734 gchar
*real
, *ret
= NULL
;
736 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
738 if (page_id
!= NULL
&& g_str_has_prefix (page_id
, "search="))
739 return g_strdup ("yelp-page-search-symbolic");
741 g_mutex_lock (document
->priv
->mutex
);
742 real
= hash_lookup (document
->priv
->page_ids
, page_id
);
744 ret
= hash_lookup (document
->priv
->icons
, real
);
746 ret
= g_strdup (ret
);
748 g_mutex_unlock (document
->priv
->mutex
);
751 ret
= g_strdup ("yelp-page-symbolic");
757 yelp_document_set_page_icon (YelpDocument
*document
,
758 const gchar
*page_id
,
761 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
763 g_mutex_lock (document
->priv
->mutex
);
764 hash_replace (document
->priv
->icons
, page_id
, g_strdup (icon
));
765 g_mutex_unlock (document
->priv
->mutex
);
769 document_indexed (YelpDocument
*document
)
771 g_mutex_lock (document
->priv
->mutex
);
772 while (document
->priv
->reqs_search
!= NULL
) {
773 Request
*request
= (Request
*) document
->priv
->reqs_search
->data
;
774 request
->idle_funcs
++;
775 g_idle_add ((GSourceFunc
) request_idle_info
, request
);
776 g_idle_add ((GSourceFunc
) request_idle_contents
, request
);
777 document
->priv
->reqs_search
= g_slist_delete_link (document
->priv
->reqs_search
,
778 document
->priv
->reqs_search
);
780 g_mutex_unlock (document
->priv
->mutex
);
783 /******************************************************************************/
786 yelp_document_request_page (YelpDocument
*document
,
787 const gchar
*page_id
,
788 GCancellable
*cancellable
,
789 YelpDocumentCallback callback
,
792 g_return_val_if_fail (YELP_IS_DOCUMENT (document
), FALSE
);
793 g_return_val_if_fail (YELP_DOCUMENT_GET_CLASS (document
)->request_page
!= NULL
, FALSE
);
795 debug_print (DB_FUNCTION
, "entering\n");
797 return YELP_DOCUMENT_GET_CLASS (document
)->request_page (document
,
805 document_request_page (YelpDocument
*document
,
806 const gchar
*page_id
,
807 GCancellable
*cancellable
,
808 YelpDocumentCallback callback
,
813 gboolean ret
= FALSE
;
815 request
= g_slice_new0 (Request
);
816 request
->document
= g_object_ref (document
);
818 real_id
= hash_lookup (document
->priv
->page_ids
, page_id
);
820 request
->page_id
= g_strdup (real_id
);
822 request
->page_id
= g_strdup (page_id
);
824 request
->cancellable
= g_object_ref (cancellable
);
825 g_signal_connect (cancellable
, "cancelled",
826 G_CALLBACK (request_cancel
), request
);
828 request
->callback
= callback
;
829 request
->user_data
= user_data
;
830 request
->idle_funcs
= 0;
832 g_mutex_lock (document
->priv
->mutex
);
834 if (g_str_has_prefix (page_id
, "search=")) {
835 document
->priv
->reqs_search
= g_slist_prepend (document
->priv
->reqs_search
, request
);
836 if (document
->priv
->indexed
)
837 g_idle_add ((GSourceFunc
) document_indexed
, document
);
839 yelp_document_index (document
);
840 g_mutex_unlock (document
->priv
->mutex
);
844 hash_slist_insert (document
->priv
->reqs_by_page_id
,
848 document
->priv
->reqs_all
= g_slist_prepend (document
->priv
->reqs_all
, request
);
849 document
->priv
->reqs_pending
= g_slist_prepend (document
->priv
->reqs_pending
, request
);
851 if (hash_lookup (document
->priv
->titles
, request
->page_id
)) {
852 request
->idle_funcs
++;
853 g_idle_add ((GSourceFunc
) request_idle_info
, request
);
856 if (hash_lookup (document
->priv
->contents
, request
->page_id
)) {
857 request
->idle_funcs
++;
858 g_idle_add ((GSourceFunc
) request_idle_contents
, request
);
862 g_mutex_unlock (document
->priv
->mutex
);
868 yelp_document_clear_contents (YelpDocument
*document
)
870 g_mutex_lock (document
->priv
->mutex
);
872 if (document
->priv
->contents
->null
) {
873 str_unref (document
->priv
->contents
->null
);
874 document
->priv
->contents
->null
= NULL
;
876 g_hash_table_remove_all (document
->priv
->contents
->hash
);
878 g_mutex_unlock (document
->priv
->mutex
);
882 yelp_document_get_requests (YelpDocument
*document
)
888 g_mutex_lock (document
->priv
->mutex
);
890 reqs
= g_hash_table_get_keys (document
->priv
->reqs_by_page_id
->hash
);
891 ret
= g_new0 (gchar
*, g_list_length (reqs
) + 1);
892 for (cur
= reqs
, i
= 0; cur
; cur
= cur
->next
, i
++) {
893 ret
[i
] = g_strdup ((gchar
*) cur
->data
);
897 g_mutex_unlock (document
->priv
->mutex
);
902 /******************************************************************************/
905 yelp_document_read_contents (YelpDocument
*document
,
906 const gchar
*page_id
)
908 g_return_val_if_fail (YELP_IS_DOCUMENT (document
), NULL
);
909 g_return_val_if_fail (YELP_DOCUMENT_GET_CLASS (document
)->read_contents
!= NULL
, NULL
);
911 return YELP_DOCUMENT_GET_CLASS (document
)->read_contents (document
, page_id
);
915 document_read_contents (YelpDocument
*document
,
916 const gchar
*page_id
)
918 gchar
*real
, *str
, **colors
;
920 g_mutex_lock (document
->priv
->mutex
);
922 if (page_id
!= NULL
&& g_str_has_prefix (page_id
, "search=")) {
923 gchar
*tmp
, *tmp2
, *txt
;
926 gchar
*url
, *title
, *desc
, *icon
; /* do not free */
928 GString
*ret
= g_string_new ("<html><head><style type='text/css'>");
930 colors
= yelp_settings_get_colors (yelp_settings_get_default ());
931 g_string_append_printf (ret
,
932 "html { height: 100%; } "
933 "body { margin: 0; padding: 0;"
934 " background-color: %s; color: %s;"
936 "div.header { margin-bottom: 1em; } "
938 " margin: 0; padding: 0.2em 12px 0 12px;"
939 " background-color: %s;"
940 " border-bottom: solid 1px %s; } "
941 "div.trail { text-indent: -1em;"
942 " margin: 0 1em 0.2em 1em; padding: 0; color: %s; } "
943 "div.body { margin: 0 12px 0 12px; padding: 0 0 12px 0; max-width: 60em; } "
944 "div, p { margin: 1em 0 0 0; padding: 0; } "
945 "div:first-child, p:first-child { margin-top: 0; } "
946 "h1 { margin: 0; padding: 0; color: %s; font-size: 1.44em; } "
947 "a { color: %s; text-decoration: none; } "
948 "a.linkdiv { display: block; } "
949 "div.linkdiv { margin: 0; padding: 0.5em; }"
950 "a:hover div.linkdiv {"
951 " outline: solid 1px %s;"
952 " background: -webkit-gradient(linear, left top, left 80, from(%s), to(%s)); } "
953 "div.title { margin-bottom: 0.2em; font-weight: bold; } "
954 "div.desc { margin: 0; color: %s; } "
955 "</style></head><body><div class='header'>",
956 colors
[YELP_SETTINGS_COLOR_BASE
],
957 colors
[YELP_SETTINGS_COLOR_TEXT
],
958 (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL
? "rtl" : "ltr"),
959 colors
[YELP_SETTINGS_COLOR_GRAY_BASE
],
960 colors
[YELP_SETTINGS_COLOR_GRAY_BORDER
],
961 colors
[YELP_SETTINGS_COLOR_TEXT_LIGHT
],
962 colors
[YELP_SETTINGS_COLOR_TEXT_LIGHT
],
963 colors
[YELP_SETTINGS_COLOR_LINK
],
964 colors
[YELP_SETTINGS_COLOR_BLUE_BASE
],
965 colors
[YELP_SETTINGS_COLOR_BLUE_BASE
],
966 colors
[YELP_SETTINGS_COLOR_BASE
],
967 colors
[YELP_SETTINGS_COLOR_TEXT_LIGHT
]
970 index_title
= yelp_storage_get_root_title (yelp_storage_get_default (),
971 document
->priv
->doc_uri
);
972 if (index_title
!= NULL
) {
973 tmp
= g_markup_printf_escaped ("<div class='trails'><div class='trail'>"
974 "<a href='xref:'>%s</a> %s "
977 (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL
? "«" : "»")
979 g_string_append (ret
, tmp
);
983 g_string_append (ret
, "</div><div class='body'>");
986 str
= hash_lookup (document
->priv
->contents
, real
);
989 g_mutex_unlock (document
->priv
->mutex
);
990 return (const gchar
*) str
;
993 txt
= g_uri_unescape_string (page_id
+ 7, NULL
);
994 tmp2
= g_strdup_printf (_("Search results for “%s”"), txt
);
995 tmp
= g_markup_printf_escaped ("<h1>%s</h1>", tmp2
);
996 g_string_append (ret
, tmp
);
1000 value
= yelp_storage_search (yelp_storage_get_default (),
1001 document
->priv
->doc_uri
,
1003 iter
= g_variant_iter_new (value
);
1004 if (g_variant_iter_n_children (iter
) == 0) {
1005 if (index_title
!= NULL
) {
1006 gchar
*t
= g_strdup_printf (_("No matching help pages found in “%s”."), index_title
);
1007 tmp
= g_markup_printf_escaped ("<p>%s</p>", t
);
1011 tmp
= g_markup_printf_escaped ("<p>%s</p>",
1012 _("No matching help pages found."));
1014 g_string_append (ret
, tmp
);
1018 while (g_variant_iter_loop (iter
, "(&s&s&s&s)", &url
, &title
, &desc
, &icon
)) {
1019 tmp
= g_markup_printf_escaped ("<div><a class='linkdiv' href='%s'><div class='linkdiv'>"
1020 "<div class='title'>%s</div>"
1021 "<div class='desc'>%s</div>"
1024 g_string_append (ret
, tmp
);
1028 g_variant_iter_free (iter
);
1029 g_variant_unref (value
);
1031 if (index_title
!= NULL
)
1032 g_free (index_title
);
1034 g_string_append (ret
, "</div></body></html>");
1035 g_mutex_unlock (document
->priv
->mutex
);
1037 hash_replace (document
->priv
->contents
, page_id
, g_string_free (ret
, FALSE
));
1038 str
= hash_lookup (document
->priv
->contents
, page_id
);
1040 g_mutex_unlock (document
->priv
->mutex
);
1041 return (const gchar
*) str
;
1044 real
= hash_lookup (document
->priv
->page_ids
, page_id
);
1045 str
= hash_lookup (document
->priv
->contents
, real
);
1049 g_mutex_unlock (document
->priv
->mutex
);
1051 return (const gchar
*) str
;
1055 yelp_document_finish_read (YelpDocument
*document
,
1056 const gchar
*contents
)
1058 g_return_if_fail (YELP_IS_DOCUMENT (document
));
1059 g_return_if_fail (YELP_DOCUMENT_GET_CLASS (document
)->finish_read
!= NULL
);
1061 YELP_DOCUMENT_GET_CLASS (document
)->finish_read (document
, contents
);
1065 document_finish_read (YelpDocument
*document
,
1066 const gchar
*contents
)
1068 str_unref (contents
);
1072 yelp_document_give_contents (YelpDocument
*document
,
1073 const gchar
*page_id
,
1077 g_return_if_fail (YELP_IS_DOCUMENT (document
));
1079 debug_print (DB_FUNCTION
, "entering\n");
1080 debug_print (DB_ARG
, " page_id = \"%s\"\n", page_id
);
1082 g_mutex_lock (document
->priv
->mutex
);
1084 hash_replace (document
->priv
->contents
,
1086 (gpointer
) str_ref (contents
));
1088 hash_replace (document
->priv
->mime_types
,
1092 g_mutex_unlock (document
->priv
->mutex
);
1096 yelp_document_get_mime_type (YelpDocument
*document
,
1097 const gchar
*page_id
)
1099 g_return_val_if_fail (YELP_IS_DOCUMENT (document
), NULL
);
1100 g_return_val_if_fail (YELP_DOCUMENT_GET_CLASS (document
)->get_mime_type
!= NULL
, NULL
);
1102 return YELP_DOCUMENT_GET_CLASS (document
)->get_mime_type (document
, page_id
);
1106 document_get_mime_type (YelpDocument
*document
,
1107 const gchar
*page_id
)
1109 gchar
*real
, *ret
= NULL
;
1111 g_mutex_lock (document
->priv
->mutex
);
1112 real
= hash_lookup (document
->priv
->page_ids
, page_id
);
1114 ret
= hash_lookup (document
->priv
->mime_types
, real
);
1116 ret
= g_strdup (ret
);
1118 g_mutex_unlock (document
->priv
->mutex
);
1123 /******************************************************************************/
1126 yelp_document_index (YelpDocument
*document
)
1128 g_return_if_fail (YELP_IS_DOCUMENT (document
));
1129 g_return_if_fail (YELP_DOCUMENT_GET_CLASS (document
)->index
!= NULL
);
1131 YELP_DOCUMENT_GET_CLASS (document
)->index (document
);
1135 document_index (YelpDocument
*document
)
1137 g_object_set (document
, "indexed", TRUE
, NULL
);
1140 /******************************************************************************/
1143 yelp_document_signal (YelpDocument
*document
,
1144 const gchar
*page_id
,
1145 YelpDocumentSignal signal
,
1146 const GError
*error
)
1150 g_return_if_fail (YELP_IS_DOCUMENT (document
));
1152 g_mutex_lock (document
->priv
->mutex
);
1154 reqs
= hash_lookup (document
->priv
->reqs_by_page_id
, page_id
);
1155 for (cur
= reqs
; cur
!= NULL
; cur
= cur
->next
) {
1156 Request
*request
= (Request
*) cur
->data
;
1160 case YELP_DOCUMENT_SIGNAL_CONTENTS
:
1161 request
->idle_funcs
++;
1162 g_idle_add ((GSourceFunc
) request_idle_contents
, request
);
1164 case YELP_DOCUMENT_SIGNAL_INFO
:
1165 request
->idle_funcs
++;
1166 g_idle_add ((GSourceFunc
) request_idle_info
, request
);
1168 case YELP_DOCUMENT_SIGNAL_ERROR
:
1169 request
->idle_funcs
++;
1170 request
->error
= yelp_error_copy ((GError
*) error
);
1171 g_idle_add ((GSourceFunc
) request_idle_error
, request
);
1178 g_mutex_unlock (document
->priv
->mutex
);
1182 yelp_document_error_pending_idle (YelpDocument
*document
)
1184 YelpDocumentPriv
*priv
= GET_PRIV (document
);
1188 g_mutex_lock (priv
->mutex
);
1190 if (priv
->reqs_pending
) {
1191 for (cur
= priv
->reqs_pending
; cur
; cur
= cur
->next
) {
1192 request
= cur
->data
;
1193 request
->error
= yelp_error_copy ((GError
*) priv
->idle_error
);
1194 request
->idle_funcs
++;
1195 g_idle_add ((GSourceFunc
) request_idle_error
, request
);
1198 g_slist_free (priv
->reqs_pending
);
1199 priv
->reqs_pending
= NULL
;
1202 g_mutex_unlock (priv
->mutex
);
1204 g_object_unref (document
);
1209 yelp_document_error_pending (YelpDocument
*document
,
1210 const GError
*error
)
1212 YelpDocumentPriv
*priv
= GET_PRIV (document
);
1214 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
1216 g_object_ref (document
);
1217 priv
->idle_error
= g_error_copy (error
);
1218 g_idle_add ((GSourceFunc
) yelp_document_error_pending_idle
, document
);
1221 /******************************************************************************/
1224 hash_new (GDestroyNotify destroy
)
1226 Hash
*hash
= g_new0 (Hash
, 1);
1227 hash
->destroy
= destroy
;
1228 hash
->hash
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
1234 hash_free (Hash
*hash
)
1237 hash
->destroy (hash
->null
);
1238 g_hash_table_destroy (hash
->hash
);
1243 hash_lookup (Hash
*hash
, const gchar
*key
)
1248 return g_hash_table_lookup (hash
->hash
, key
);
1252 hash_replace (Hash
*hash
,
1258 hash
->destroy (hash
->null
);
1262 g_hash_table_replace (hash
->hash
, g_strdup (key
), value
);
1266 hash_remove (Hash
*hash
,
1271 hash
->destroy (hash
->null
);
1276 g_hash_table_remove (hash
->hash
, key
);
1280 hash_slist_insert (Hash
*hash
,
1285 list
= hash_lookup (hash
, key
);
1287 list
->next
= g_slist_prepend (list
->next
, value
);
1289 list
= g_slist_prepend (NULL
, value
);
1290 list
= g_slist_prepend (list
, NULL
);
1294 g_hash_table_insert (hash
->hash
, g_strdup (key
), list
);
1299 hash_slist_remove (Hash
*hash
,
1304 list
= hash_lookup (hash
, key
);
1306 list
= g_slist_remove (list
, value
);
1307 if (list
->next
== NULL
)
1308 hash_remove (hash
, key
);
1312 /******************************************************************************/
1315 request_cancel (GCancellable
*cancellable
, Request
*request
)
1318 YelpDocument
*document
= request
->document
;
1319 gboolean found
= FALSE
;
1321 g_assert (document
!= NULL
&& YELP_IS_DOCUMENT (document
));
1323 g_mutex_lock (document
->priv
->mutex
);
1325 document
->priv
->reqs_pending
= g_slist_remove (document
->priv
->reqs_pending
,
1326 (gconstpointer
) request
);
1327 hash_slist_remove (document
->priv
->reqs_by_page_id
,
1330 for (cur
= document
->priv
->reqs_all
; cur
!= NULL
; cur
= cur
->next
) {
1331 if (cur
->data
== request
) {
1332 document
->priv
->reqs_all
= g_slist_delete_link (document
->priv
->reqs_all
, cur
);
1338 for (cur
= document
->priv
->reqs_search
; cur
!= NULL
; cur
= cur
->next
) {
1339 if (cur
->data
== request
) {
1340 document
->priv
->reqs_search
= g_slist_delete_link (document
->priv
->reqs_search
, cur
);
1345 request_try_free (request
);
1347 g_mutex_unlock (document
->priv
->mutex
);
1351 request_idle_contents (Request
*request
)
1353 YelpDocument
*document
;
1354 YelpDocumentPriv
*priv
;
1355 YelpDocumentCallback callback
= NULL
;
1356 gpointer user_data
= user_data
;
1358 g_assert (request
!= NULL
&& YELP_IS_DOCUMENT (request
->document
));
1360 if (g_cancellable_is_cancelled (request
->cancellable
)) {
1361 request
->idle_funcs
--;
1365 document
= g_object_ref (request
->document
);
1366 priv
= GET_PRIV (document
);
1368 g_mutex_lock (document
->priv
->mutex
);
1370 priv
->reqs_pending
= g_slist_remove (priv
->reqs_pending
, request
);
1372 callback
= request
->callback
;
1373 user_data
= request
->user_data
;
1374 request
->idle_funcs
--;
1376 g_mutex_unlock (document
->priv
->mutex
);
1379 callback (document
, YELP_DOCUMENT_SIGNAL_CONTENTS
, user_data
, NULL
);
1381 g_object_unref (document
);
1386 request_idle_info (Request
*request
)
1388 YelpDocument
*document
;
1389 YelpDocumentCallback callback
= NULL
;
1392 g_assert (request
!= NULL
&& YELP_IS_DOCUMENT (request
->document
));
1394 if (g_cancellable_is_cancelled (request
->cancellable
)) {
1395 request
->idle_funcs
--;
1399 document
= g_object_ref (request
->document
);
1401 g_mutex_lock (document
->priv
->mutex
);
1403 callback
= request
->callback
;
1404 user_data
= request
->user_data
;
1405 request
->idle_funcs
--;
1407 g_mutex_unlock (document
->priv
->mutex
);
1410 callback (document
, YELP_DOCUMENT_SIGNAL_INFO
, user_data
, NULL
);
1412 g_object_unref (document
);
1417 request_idle_error (Request
*request
)
1419 YelpDocument
*document
;
1420 YelpDocumentPriv
*priv
;
1421 YelpDocumentCallback callback
= NULL
;
1422 GError
*error
= NULL
;
1423 gpointer user_data
= user_data
;
1425 g_assert (request
!= NULL
&& YELP_IS_DOCUMENT (request
->document
));
1427 if (g_cancellable_is_cancelled (request
->cancellable
)) {
1428 request
->idle_funcs
--;
1432 document
= g_object_ref (request
->document
);
1433 priv
= GET_PRIV (document
);
1435 g_mutex_lock (priv
->mutex
);
1437 if (request
->error
) {
1438 callback
= request
->callback
;
1439 user_data
= request
->user_data
;
1440 error
= request
->error
;
1441 request
->error
= NULL
;
1442 priv
->reqs_pending
= g_slist_remove (priv
->reqs_pending
, request
);
1445 request
->idle_funcs
--;
1446 g_mutex_unlock (priv
->mutex
);
1450 YELP_DOCUMENT_SIGNAL_ERROR
,
1454 g_object_unref (document
);
1459 request_try_free (Request
*request
)
1461 if (!g_cancellable_is_cancelled (request
->cancellable
))
1462 g_cancellable_cancel (request
->cancellable
);
1464 if (request
->idle_funcs
== 0)
1465 request_free (request
);
1467 g_idle_add ((GSourceFunc
) request_try_free
, request
);
1473 request_free (Request
*request
)
1475 g_object_unref (request
->document
);
1476 g_free (request
->page_id
);
1477 g_object_unref (request
->cancellable
);
1480 g_error_free (request
->error
);
1482 g_slice_free (Request
, request
);
1485 /******************************************************************************/
1487 static const gchar
*
1488 str_ref (const gchar
*str
)
1493 g_static_mutex_lock (&str_mutex
);
1494 if (str_refs
== NULL
)
1495 str_refs
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1496 p
= g_hash_table_lookup (str_refs
, str
);
1498 i
= GPOINTER_TO_UINT (p
);
1500 p
= GUINT_TO_POINTER (i
);
1502 g_hash_table_insert (str_refs
, (gpointer
) str
, p
);
1503 g_static_mutex_unlock (&str_mutex
);
1509 str_unref (const gchar
*str
)
1514 g_static_mutex_lock (&str_mutex
);
1515 p
= g_hash_table_lookup (str_refs
, str
);
1517 i
= GPOINTER_TO_UINT (p
);
1519 p
= GUINT_TO_POINTER (i
);
1522 g_hash_table_insert (str_refs
, (gpointer
) str
, p
);
1525 g_hash_table_remove (str_refs
, (gpointer
) str
);
1526 g_free ((gchar
*) str
);
1529 g_static_mutex_unlock (&str_mutex
);