]> www.fi.muni.cz Git - evince.git/blob - shell/ev-metadata-manager.c
Fix compilation
[evince.git] / shell / ev-metadata-manager.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * ev-metadata-manager.c
4  * This file is part of ev
5  *
6  * Copyright (C) 2003  Paolo Maggi 
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (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
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, 
21  * Boston, MA 02111-1307, USA. 
22  */
23  
24 /*
25  * Modified by the ev Team, 2003. See the AUTHORS file for a 
26  * list of people on the ev Team.  
27  * See the ChangeLog files for a list of changes. 
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <time.h>
35 #include <stdlib.h>
36
37 #include <libxml/xmlreader.h>
38
39 #include "ev-metadata-manager.h"
40 #include "ev-file-helpers.h"
41
42 #define METADATA_FILE   "ev-metadata.xml"
43
44 #define MAX_ITEMS       50
45
46 typedef struct _GeditMetadataManager GeditMetadataManager;
47
48 typedef struct _Item Item;
49
50 struct _Item
51 {
52         time_t           atime; /* time of last access */
53
54         GHashTable      *values;
55 };
56         
57 struct _GeditMetadataManager
58 {
59         gboolean         values_loaded; /* It is true if the file 
60                                            has been read */
61
62         gboolean         modified;      /* It is true if the file 
63                                            has top be written */
64
65         guint            timeout_id;
66
67         GHashTable      *items;
68 };
69
70 static void ev_metadata_manager_save (gpointer data);
71
72
73 static GeditMetadataManager *ev_metadata_manager = NULL;
74
75 static void
76 item_free (gpointer data)
77 {
78         Item *item;
79         
80         g_return_if_fail (data != NULL);
81
82         item = (Item *)data;
83
84         if (item->values != NULL)
85                 g_hash_table_destroy (item->values);
86
87         g_free (item);
88 }
89
90 static gboolean
91 ev_metadata_manager_init (void)
92 {
93         if (ev_metadata_manager != NULL)
94                 return TRUE;
95
96         ev_metadata_manager = g_new0 (GeditMetadataManager, 1);
97
98         ev_metadata_manager->values_loaded = FALSE;
99         ev_metadata_manager->modified = FALSE;
100
101         ev_metadata_manager->items = 
102                 g_hash_table_new_full (g_str_hash, 
103                                        g_str_equal, 
104                                        g_free,
105                                        item_free);
106
107         ev_metadata_manager->timeout_id = 
108                 g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
109                                     2000, /* 2 sec */
110                                     (GSourceFunc)ev_metadata_manager_save,
111                                     NULL,
112                                     NULL);
113         
114         return TRUE;
115 }
116
117 /* This function must be called before exiting ev */
118 void
119 ev_metadata_manager_shutdown (void)
120 {
121         if (ev_metadata_manager == NULL)
122                 return;
123
124         g_source_remove (ev_metadata_manager->timeout_id);
125
126         ev_metadata_manager_save (NULL);
127
128         if (ev_metadata_manager->items != NULL)
129                 g_hash_table_destroy (ev_metadata_manager->items);
130
131         g_free (ev_metadata_manager);
132         ev_metadata_manager = NULL;
133 }
134
135 static GValue *
136 parse_value (xmlChar *value, xmlChar *type)
137 {
138         GType ret_type;
139         GValue *ret;
140
141         ret_type = g_type_from_name ((char *)type);
142         ret = g_new0 (GValue, 1);
143         g_value_init (ret, ret_type);
144
145         switch (ret_type) {
146                 case G_TYPE_STRING:
147                         g_value_set_string (ret, (char *)value);
148                         break;
149                 case G_TYPE_INT:
150                         g_value_set_int (ret, atoi ((char *)value));
151                         break;
152         }
153
154         return ret;
155 }
156
157 static void
158 parseItem (xmlDocPtr doc, xmlNodePtr cur)
159 {
160         Item *item;
161         
162         xmlChar *uri;
163         xmlChar *atime;
164         
165         if (xmlStrcmp (cur->name, (const xmlChar *)"document") != 0)
166                         return;
167
168         uri = xmlGetProp (cur, (const xmlChar *)"uri");
169         if (uri == NULL)
170                 return;
171         
172         atime = xmlGetProp (cur, (const xmlChar *)"atime");
173         if (atime == NULL)
174         {
175                 xmlFree (uri);
176                 return;
177         }
178
179         item = g_new0 (Item, 1);
180
181         item->atime = atol ((char *)atime);
182         
183         item->values = g_hash_table_new_full (g_str_hash, 
184                                               g_str_equal, 
185                                               g_free, 
186                                               g_free);
187
188         cur = cur->xmlChildrenNode;
189                 
190         while (cur != NULL)
191         {
192                 if (xmlStrcmp (cur->name, (const xmlChar *)"entry") == 0)
193                 {
194                         xmlChar *key;
195                         xmlChar *xml_value;
196                         xmlChar *type;
197                         GValue *value;
198                         
199                         key = xmlGetProp (cur, (const xmlChar *)"key");
200                         xml_value = xmlGetProp (cur, (const xmlChar *)"value");
201                         type = xmlGetProp (cur, (const xmlChar *)"type");
202                         value = parse_value (xml_value, type);
203
204                         if ((key != NULL) && (value != NULL))
205                                 g_hash_table_insert (item->values,
206                                                      xmlStrdup (key), 
207                                                      value);
208
209                         if (key != NULL)
210                                 xmlFree (key);
211                         if (xml_value != NULL)
212                                 xmlFree (xml_value);
213                 }
214                 
215                 cur = cur->next;
216         }
217
218         g_hash_table_insert (ev_metadata_manager->items,
219                              xmlStrdup (uri),
220                              item);
221
222         xmlFree (uri);
223         xmlFree (atime);
224 }
225
226 static gboolean
227 load_values ()
228 {
229         xmlDocPtr doc;
230         xmlNodePtr cur;
231         gchar *file_name;
232
233         g_return_val_if_fail (ev_metadata_manager != NULL, FALSE);
234         g_return_val_if_fail (ev_metadata_manager->values_loaded == FALSE, FALSE);
235
236         ev_metadata_manager->values_loaded = TRUE;
237                 
238         xmlKeepBlanksDefault (0);
239
240         /* FIXME: file locking - Paolo */
241         file_name = g_build_filename (ev_dot_dir (), METADATA_FILE, NULL);
242         if (!g_file_test (file_name, G_FILE_TEST_EXISTS))
243         {
244                 g_free (file_name);
245                 return FALSE;
246         }
247
248         doc = xmlParseFile (file_name);
249         g_free (file_name);
250
251         if (doc == NULL)
252         {
253                 return FALSE;
254         }
255
256         cur = xmlDocGetRootElement (doc);
257         if (cur == NULL) 
258         {
259                 g_message ("The metadata file '%s' is empty", METADATA_FILE);
260                 xmlFreeDoc (doc);
261         
262                 return FALSE;
263         }
264
265         if (xmlStrcmp (cur->name, (const xmlChar *) "metadata")) 
266         {
267                 g_message ("File '%s' is of the wrong type", METADATA_FILE);
268                 xmlFreeDoc (doc);
269                 
270                 return FALSE;
271         }
272
273         cur = xmlDocGetRootElement (doc);
274         cur = cur->xmlChildrenNode;
275         
276         while (cur != NULL)
277         {
278                 parseItem (doc, cur);
279
280                 cur = cur->next;
281         }
282
283         xmlFreeDoc (doc);
284
285         return TRUE;
286 }
287
288 gboolean
289 ev_metadata_manager_get (const gchar *uri,
290                          const gchar *key,
291                          GValue      *value)
292 {
293         Item *item;
294         GValue *ret;
295         
296         g_return_val_if_fail (uri != NULL, FALSE);
297         g_return_val_if_fail (key != NULL, FALSE);
298
299         if (ev_metadata_manager == NULL)
300                 ev_metadata_manager_init ();
301
302         if (!ev_metadata_manager->values_loaded)
303         {
304                 gboolean res;
305
306                 res = load_values ();
307
308                 if (!res)
309                         return FALSE;
310         }
311
312         item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
313                                             uri);
314
315         if (item == NULL)
316                 return FALSE;
317
318         item->atime = time (NULL);
319         
320         if (item->values == NULL)
321                 return FALSE;
322         
323         ret = (GValue *)g_hash_table_lookup (item->values, key);
324
325         if (ret == NULL) {
326                 return FALSE;
327         } else {
328                 g_value_init (value, G_VALUE_TYPE (ret));
329                 g_value_copy (ret, value);
330
331                 return TRUE;
332         }
333 }
334
335 static void
336 value_free (gpointer data)
337 {
338         GValue *value = (GValue *)data;
339
340         g_value_unset (value);
341         g_free (value);
342 }
343
344 void
345 ev_metadata_manager_set (const gchar  *uri,
346                          const gchar  *key,
347                          const GValue *value)
348 {
349         Item *item;
350
351         g_return_if_fail (uri != NULL);
352         g_return_if_fail (key != NULL);
353
354         if (ev_metadata_manager == NULL)
355                 ev_metadata_manager_init ();
356
357         if (!ev_metadata_manager->values_loaded)
358         {
359                 gboolean res;
360
361                 res = load_values ();
362
363                 if (!res)
364                         return;
365         }
366
367         item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
368                                             uri);
369
370         if (item == NULL)
371         {
372                 item = g_new0 (Item, 1);
373
374                 g_hash_table_insert (ev_metadata_manager->items,
375                                      g_strdup (uri),
376                                      item);
377         }
378         
379         if (item->values == NULL)
380                  item->values = g_hash_table_new_full (g_str_hash, 
381                                                        g_str_equal, 
382                                                        g_free, 
383                                                        value_free);
384         if (value != NULL) {
385                 GValue *new;
386
387                 new = g_new0 (GValue, 1);
388                 g_value_init (new, G_VALUE_TYPE (value));
389                 g_value_copy (value, new);
390
391                 g_hash_table_insert (item->values,
392                                      g_strdup (key),
393                                      new);
394         } else {
395                 g_hash_table_remove (item->values,
396                                      key);
397         }
398
399         item->atime = time (NULL);
400
401         ev_metadata_manager->modified = TRUE;
402 }
403
404 static void
405 save_values (const gchar *key, GValue *value, xmlNodePtr parent)
406 {
407         char *string_value;
408         xmlNodePtr xml_node;
409         
410         g_return_if_fail (key != NULL);
411         
412         if (value == NULL)
413                 return;
414                 
415         xml_node = xmlNewChild (parent, NULL, (const xmlChar *)"entry", NULL);
416
417         xmlSetProp (xml_node, (const xmlChar *)"key", (const xmlChar *)key);
418         xmlSetProp (xml_node,
419                     (const xmlChar *)"type",
420                     (const xmlChar *)g_type_name (G_VALUE_TYPE (value)));
421
422         switch (G_VALUE_TYPE (value)) {
423                 case G_TYPE_STRING:
424                         string_value = g_strdup (g_value_get_string (value));
425                         break;
426                 case G_TYPE_INT:
427                         string_value = g_strdup_printf ("%d", g_value_get_int (value));
428                         break;
429                 default:
430                         string_value = NULL;
431                         g_assert_not_reached ();
432         }
433
434         xmlSetProp (xml_node, (const xmlChar *)"value", (const xmlChar *)string_value);
435
436         g_free (string_value);
437 }
438
439 static void
440 save_item (const gchar *key, const gpointer *data, xmlNodePtr parent)
441 {       
442         xmlNodePtr xml_node;
443         const Item *item = (const Item *)data;
444         gchar *atime;
445
446         g_return_if_fail (key != NULL);
447         
448         if (item == NULL)
449                 return;
450                 
451         xml_node = xmlNewChild (parent, NULL, (const xmlChar *)"document", NULL);
452         
453         xmlSetProp (xml_node, (const xmlChar *)"uri", (const xmlChar *)key);
454
455         /* FIXME: is the cast right? - Paolo */
456         atime = g_strdup_printf ("%d", (int)item->atime);
457         xmlSetProp (xml_node, (const xmlChar *)"atime", (const xmlChar *)atime);        
458
459         g_free (atime);
460
461         g_hash_table_foreach (item->values,
462                               (GHFunc)save_values, xml_node); 
463 }
464
465 static void
466 get_oldest (const gchar *key, const gpointer value, const gchar ** key_to_remove)
467 {
468         const Item *item = (const Item *)value;
469         
470         if (*key_to_remove == NULL)
471         {
472                 *key_to_remove = key;
473         }
474         else
475         {
476                 const Item *item_to_remove = 
477                         g_hash_table_lookup (ev_metadata_manager->items,
478                                              *key_to_remove);
479
480                 g_return_if_fail (item_to_remove != NULL);
481
482                 if (item->atime < item_to_remove->atime)
483                 {
484                         *key_to_remove = key;
485                 }
486         }       
487 }
488
489 static void
490 resize_items ()
491 {
492         while (g_hash_table_size (ev_metadata_manager->items) > MAX_ITEMS)
493         {
494                 gpointer key_to_remove = NULL;
495
496                 g_hash_table_foreach (ev_metadata_manager->items,
497                                       (GHFunc)get_oldest,
498                                       &key_to_remove);
499
500                 g_return_if_fail (key_to_remove != NULL);
501                 
502                 g_hash_table_remove (ev_metadata_manager->items,
503                                      key_to_remove);
504         }
505 }
506
507 static void
508 ev_metadata_manager_save (gpointer data)
509 {       
510         xmlDocPtr  doc;
511         xmlNodePtr root;
512         gchar *file_name;
513
514         if (!ev_metadata_manager->modified)
515                 return;
516
517         resize_items ();
518                 
519         xmlIndentTreeOutput = TRUE;
520
521         doc = xmlNewDoc ((const xmlChar *)"1.0");
522         if (doc == NULL)
523                 return;
524
525         /* Create metadata root */
526         root = xmlNewDocNode (doc, NULL, (const xmlChar *)"metadata", NULL);
527         xmlDocSetRootElement (doc, root);
528
529         g_hash_table_foreach (ev_metadata_manager->items,
530                           (GHFunc)save_item, root);        
531
532         /* FIXME: lock file - Paolo */
533         file_name = g_build_filename (ev_dot_dir (), METADATA_FILE, NULL);
534         xmlSaveFormatFile (file_name, doc, 1);
535         g_free (file_name);
536         
537         xmlFreeDoc (doc); 
538
539         ev_metadata_manager->modified = FALSE;
540 }
541
542 void
543 ev_metadata_manager_set_int (const gchar *uri, const gchar *key, int value)
544 {
545         GValue val = { 0, };
546
547         g_value_init (&val, G_TYPE_INT);
548         g_value_set_int (&val, value);
549
550         ev_metadata_manager_set (uri, key, &val);
551 }