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