1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2002 CodeFactory AB
4 * Copyright (C) 2002 Mikael Hallendal <micke@imendio.com>
5 * Copyright (C) 2004-2008 Imendio AB
6 * Copyright (C) 2010 Lanedo GmbH
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23
24 #include "config.h"
25 #include <string.h>
26
27 #include "dh-link.h"
28 #include "dh-util.h"
29 #include "dh-book.h"
30 #include "dh-book-manager.h"
31 #include "dh-marshal.h"
32
33 typedef struct {
34 /* The list of all DhBooks found in the system */
35 GList *books;
36 } DhBookManagerPriv;
37
38 enum {
39 DISABLED_BOOK_LIST_UPDATED,
40 LAST_SIGNAL
41 };
42
43 static gint signals[LAST_SIGNAL] = { 0 };
44
45 G_DEFINE_TYPE (DhBookManager, dh_book_manager, G_TYPE_OBJECT);
46
47 #define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
48 (instance, DH_TYPE_BOOK_MANAGER, DhBookManagerPriv)
49
50 static void dh_book_manager_init (DhBookManager *book_manager);
51 static void dh_book_manager_class_init (DhBookManagerClass *klass);
52
53 static void book_manager_add_from_filepath (DhBookManager *book_manager,
54 const gchar *book_path);
55 static void book_manager_add_from_dir (DhBookManager *book_manager,
56 const gchar *dir_path);
57
58 #ifdef GDK_WINDOWING_QUARTZ
59 static void book_manager_add_from_xcode_docset (DhBookManager *book_manager,
60 const gchar *dir_path);
61 #endif
62
63 static void
book_manager_finalize(GObject * object)64 book_manager_finalize (GObject *object)
65 {
66 DhBookManagerPriv *priv;
67 GList *l;
68
69 priv = GET_PRIVATE (object);
70
71 /* Destroy all books */
72 for (l = priv->books; l; l = g_list_next (l)) {
73 g_object_unref (l->data);
74 }
75 g_list_free (priv->books);
76
77 G_OBJECT_CLASS (dh_book_manager_parent_class)->finalize (object);
78 }
79
80 static void
dh_book_manager_class_init(DhBookManagerClass * klass)81 dh_book_manager_class_init (DhBookManagerClass *klass)
82 {
83 GObjectClass *object_class = G_OBJECT_CLASS (klass);
84
85 object_class->finalize = book_manager_finalize;
86
87 signals[DISABLED_BOOK_LIST_UPDATED] =
88 g_signal_new ("disabled-book-list-updated",
89 G_TYPE_FROM_CLASS (klass),
90 G_SIGNAL_RUN_LAST,
91 G_STRUCT_OFFSET (DhBookManagerClass, disabled_book_list_updated),
92 NULL, NULL,
93 _dh_marshal_VOID__VOID,
94 G_TYPE_NONE,
95 0);
96
97 g_type_class_add_private (klass, sizeof (DhBookManagerPriv));
98 }
99
100 static void
dh_book_manager_init(DhBookManager * book_manager)101 dh_book_manager_init (DhBookManager *book_manager)
102 {
103 DhBookManagerPriv *priv = GET_PRIVATE (book_manager);
104
105 priv->books = NULL;
106 }
107
108 static void
book_manager_clean_list_of_books_disabled(GSList * books_disabled)109 book_manager_clean_list_of_books_disabled (GSList *books_disabled)
110 {
111 GSList *sl;
112
113 for (sl = books_disabled; sl; sl = g_slist_next (sl)) {
114 g_free (sl->data);
115 }
116 g_slist_free (sl);
117 }
118
119 static void
book_manager_check_status_from_conf(DhBookManager * book_manager)120 book_manager_check_status_from_conf (DhBookManager *book_manager)
121 {
122 GSList *books_disabled, *sl;
123
124 books_disabled = dh_util_state_load_books_disabled ();
125
126 for (sl = books_disabled; sl; sl = g_slist_next (sl)) {
127 DhBook *book;
128
129 book = dh_book_manager_get_book_by_name (book_manager,
130 (const gchar *)sl->data);
131 if (book) {
132 dh_book_set_enabled (book, FALSE);
133 }
134 }
135
136 book_manager_clean_list_of_books_disabled (books_disabled);
137 }
138
139 static void
book_manager_add_books_in_data_dir(DhBookManager * book_manager,const gchar * data_dir)140 book_manager_add_books_in_data_dir (DhBookManager *book_manager,
141 const gchar *data_dir)
142 {
143 gchar *dir;
144
145 dir = g_build_filename (data_dir, "gtk-doc", "html", NULL);
146 book_manager_add_from_dir (book_manager, dir);
147 g_free (dir);
148
149 dir = g_build_filename (data_dir, "devhelp", "books", NULL);
150 book_manager_add_from_dir (book_manager, dir);
151 g_free (dir);
152 }
153
154 void
dh_book_manager_populate(DhBookManager * book_manager)155 dh_book_manager_populate (DhBookManager *book_manager)
156 {
157 const gchar * const * system_dirs;
158
159 book_manager_add_books_in_data_dir (book_manager,
160 g_get_user_data_dir ());
161
162 system_dirs = g_get_system_data_dirs ();
163 while (*system_dirs) {
164 book_manager_add_books_in_data_dir (book_manager,
165 *system_dirs);
166 system_dirs++;
167 }
168
169 #ifdef GDK_WINDOWING_QUARTZ
170 book_manager_add_from_xcode_docset (
171 book_manager,
172 "/Library/Developer/Shared/Documentation/DocSets");
173 #endif
174
175 /* Once all books are loaded, check enabled status from conf */
176 book_manager_check_status_from_conf (book_manager);
177 }
178
179 static gchar *
book_manager_get_book_path(const gchar * base_path,const gchar * name)180 book_manager_get_book_path (const gchar *base_path,
181 const gchar *name)
182 {
183 static const gchar *suffixes[] = {
184 "devhelp2",
185 "devhelp2.gz",
186 "devhelp",
187 "devhelp.gz",
188 NULL
189 };
190 gchar *tmp;
191 gchar *book_path;
192 guint i;
193
194 for (i = 0; suffixes[i]; i++) {
195 tmp = g_build_filename (base_path, name, name, NULL);
196 book_path = g_strconcat (tmp, ".", suffixes[i], NULL);
197 g_free (tmp);
198
199 if (g_file_test (book_path, G_FILE_TEST_EXISTS)) {
200 return book_path;;
201 }
202 g_free (book_path);
203 }
204 return NULL;
205 }
206
207 static void
book_manager_add_from_dir(DhBookManager * book_manager,const gchar * dir_path)208 book_manager_add_from_dir (DhBookManager *book_manager,
209 const gchar *dir_path)
210 {
211 GDir *dir;
212 const gchar *name;
213
214 g_return_if_fail (book_manager);
215 g_return_if_fail (dir_path);
216
217 /* Open directory */
218 dir = g_dir_open (dir_path, 0, NULL);
219 if (!dir) {
220 return;
221 }
222
223 /* And iterate it */
224 while ((name = g_dir_read_name (dir)) != NULL) {
225 gchar *book_path;
226
227 book_path = book_manager_get_book_path (dir_path, name);
228 if (book_path) {
229 /* Add book from filepath */
230 book_manager_add_from_filepath (book_manager,
231 book_path);
232 g_free (book_path);
233 }
234 }
235
236 g_dir_close (dir);
237 }
238
239 #ifdef GDK_WINDOWING_QUARTZ
240 static gboolean
seems_docset_dir(const gchar * path)241 seems_docset_dir (const gchar *path)
242 {
243 gchar *tmp;
244 gboolean seems_like_devhelp = FALSE;
245
246 g_return_val_if_fail (path, FALSE);
247
248 /* Do some sanity checking on the directory first so we don't have
249 * to go through several hundreds of files in every docset.
250 */
251 tmp = g_build_filename (path, "style.css", NULL);
252 if (g_file_test (tmp, G_FILE_TEST_EXISTS)) {
253 gchar *tmp;
254
255 tmp = g_build_filename (path, "index.sgml", NULL);
256 if (g_file_test (tmp, G_FILE_TEST_EXISTS)) {
257 seems_like_devhelp = TRUE;
258 }
259 g_free (tmp);
260 }
261 g_free (tmp);
262
263 return seems_like_devhelp;
264 }
265
266 static void
book_manager_add_from_xcode_docset(DhBookManager * book_manager,const gchar * dir_path)267 book_manager_add_from_xcode_docset (DhBookManager *book_manager,
268 const gchar *dir_path)
269 {
270 GDir *dir;
271 const gchar *name;
272
273 g_return_if_fail (book_manager);
274 g_return_if_fail (dir_path);
275
276 if (!seems_docset_dir (dir_path)) {
277 return;
278 }
279
280 /* Open directory */
281 dir = g_dir_open (dir_path, 0, NULL);
282 if (!dir) {
283 return;
284 }
285
286 /* And iterate it, looking for files ending with .devhelp2 */
287 while ((name = g_dir_read_name (dir)) != NULL) {
288 if (g_strcmp0 (strrchr (name, '.'),
289 ".devhelp2") == 0) {
290 gchar *book_path;
291
292 book_path = g_build_filename (path, name, NULL);
293 /* Add book from filepath */
294 book_manager_add_from_filepath (book_manager,
295 book_path);
296 g_free (book_path);
297 }
298 }
299
300 g_dir_close (dir);
301 }
302 #endif
303
304 static void
book_manager_add_from_filepath(DhBookManager * book_manager,const gchar * book_path)305 book_manager_add_from_filepath (DhBookManager *book_manager,
306 const gchar *book_path)
307 {
308 DhBookManagerPriv *priv;
309 DhBook *book;
310
311 g_return_if_fail (book_manager);
312 g_return_if_fail (book_path);
313
314 priv = GET_PRIVATE (book_manager);
315
316 /* Allocate new book struct */
317 book = dh_book_new (book_path);
318
319 /* Check if book with same path was already loaded in the manager */
320 if (g_list_find_custom (priv->books,
321 book,
322 (GCompareFunc)dh_book_cmp_by_path)) {
323 g_object_unref (book);
324 return;
325 }
326
327 /* Check if book with same bookname was already loaded in the manager
328 * (we need to force unique book names) */
329 if (g_list_find_custom (priv->books,
330 book,
331 (GCompareFunc)dh_book_cmp_by_name)) {
332 g_object_unref (book);
333 return;
334 }
335
336 /* Add the book to the book list */
337 priv->books = g_list_insert_sorted (priv->books,
338 book,
339 (GCompareFunc)dh_book_cmp_by_title);
340 }
341
342 GList *
dh_book_manager_get_books(DhBookManager * book_manager)343 dh_book_manager_get_books (DhBookManager *book_manager)
344 {
345 g_return_val_if_fail (book_manager, NULL);
346
347 return GET_PRIVATE (book_manager)->books;
348 }
349
350 DhBook *
dh_book_manager_get_book_by_name(DhBookManager * book_manager,const gchar * name)351 dh_book_manager_get_book_by_name (DhBookManager *book_manager,
352 const gchar *name)
353 {
354 DhBook *book = NULL;
355 GList *l;
356
357 g_return_val_if_fail (book_manager, NULL);
358
359 for (l = GET_PRIVATE (book_manager)->books;
360 l && !book;
361 l = g_list_next (l)) {
362 if (g_strcmp0 (name,
363 dh_book_get_name (DH_BOOK (l->data))) == 0) {
364 book = l->data;
365 }
366 }
367
368 return book;
369 }
370
371 void
dh_book_manager_update(DhBookManager * book_manager)372 dh_book_manager_update (DhBookManager *book_manager)
373 {
374 DhBookManagerPriv *priv;
375 GSList *books_disabled = NULL;
376 GList *l;
377
378 g_return_if_fail (book_manager);
379
380 priv = GET_PRIVATE (book_manager);
381
382 /* Create list of disabled books */
383 for (l = priv->books; l; l = g_list_next (l)) {
384 DhBook *book = DH_BOOK (l->data);
385
386 if (!dh_book_get_enabled (book)) {
387 books_disabled = g_slist_append (books_disabled,
388 g_strdup (dh_book_get_name (book)));
389 }
390 }
391
392 /* Store in conf */
393 dh_util_state_store_books_disabled (books_disabled);
394
395 /* Emit signal to notify others */
396 g_signal_emit (book_manager,
397 signals[DISABLED_BOOK_LIST_UPDATED],
398 0);
399
400 book_manager_clean_list_of_books_disabled (books_disabled);
401 }
402
403 DhBookManager *
dh_book_manager_new(void)404 dh_book_manager_new (void)
405 {
406 return g_object_new (DH_TYPE_BOOK_MANAGER, NULL);
407 }
408
409