]> www.fi.muni.cz Git - evince.git/blob - shell/ev-metadata-manager.c
e86da9735834ae10cf64b0ffb037b2cf15e7c420
[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 _EvMetadataManager EvMetadataManager;
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 _EvMetadataManager
58 {
59         gboolean         values_loaded; /* It is true if the file 
60                                            has been read */
61
62         guint            timeout_id;
63
64         GHashTable      *items;
65 };
66
67 static gboolean ev_metadata_manager_save (gpointer data);
68
69
70 static EvMetadataManager *ev_metadata_manager = NULL;
71
72 /**
73  * item_free:
74  * @data: a pointer to a #Item data
75  *
76  * It does free the values on the #GHashTable where data points.
77  */
78 static void
79 item_free (gpointer data)
80 {
81         Item *item;
82         
83         g_return_if_fail (data != NULL);
84
85         item = (Item *)data;
86
87         if (item->values != NULL)
88                 g_hash_table_destroy (item->values);
89
90         g_free (item);
91 }
92
93 /**
94  * ev_metadata_arm_timeout
95  *
96  * Setup a timeout for saving the metadata to disk.
97  */
98 static void
99 ev_metadata_arm_timeout(void)
100 {
101         if (ev_metadata_manager->timeout_id)
102                 return;
103         ev_metadata_manager->timeout_id = 
104                 g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
105                                     2000, /* 2 sec */
106                                     (GSourceFunc)ev_metadata_manager_save,
107                                     NULL,
108                                     NULL);
109 }
110
111 /**
112  * ev_metadata_manager_init:
113  *
114  * Creates an EvMetadataManager with default values.
115  *
116  *  values_loaded   ->  %FALSE.
117  *  timeout_id      ->  the id of the event source.
118  *  items           ->  a new full empty #GHashTable.
119  */
120 void
121 ev_metadata_manager_init (void)
122 {
123         ev_metadata_manager = g_new0 (EvMetadataManager, 1);
124
125         ev_metadata_manager->values_loaded = FALSE;
126
127         ev_metadata_manager->items = 
128                 g_hash_table_new_full (g_str_hash, 
129                                        g_str_equal, 
130                                        g_free,
131                                        item_free);
132 }
133
134 /* This function must be called before exiting ev */
135 void
136 ev_metadata_manager_shutdown (void)
137 {
138         if (ev_metadata_manager == NULL)
139                 return;
140
141         if (ev_metadata_manager->timeout_id) {
142                 g_source_remove (ev_metadata_manager->timeout_id);
143                 ev_metadata_manager->timeout_id = 0;
144                 ev_metadata_manager_save (NULL);
145         }
146
147         if (ev_metadata_manager->items != NULL)
148                 g_hash_table_destroy (ev_metadata_manager->items);
149
150         g_free (ev_metadata_manager);
151         ev_metadata_manager = NULL;
152 }
153
154 static void
155 value_free (gpointer data)
156 {
157         GValue *value = (GValue *)data;
158
159         g_value_unset (value);
160         g_free (value);
161 }
162
163 static GValue *
164 parse_value (xmlChar *value, xmlChar *type)
165 {
166         GType ret_type;
167         GValue *ret;
168
169         ret_type = g_type_from_name ((char *)type);
170         ret = g_new0 (GValue, 1);
171         g_value_init (ret, ret_type);
172
173         switch (ret_type) {
174                 case G_TYPE_STRING:
175                         g_value_set_string (ret, (char *)value);
176                         break;
177                 case G_TYPE_INT:
178                         g_value_set_int (ret, g_ascii_strtoull ((char *)value, NULL, 0));
179                         break;
180                 case G_TYPE_DOUBLE:
181                         g_value_set_double (ret, g_ascii_strtod ((char *)value, NULL));
182                         break;
183                 case G_TYPE_BOOLEAN:
184                         g_value_set_boolean (ret, g_ascii_strtoull ((char *)value, NULL, 0));
185                         break;
186         }
187
188         return ret;
189 }
190
191 static void
192 parseItem (xmlDocPtr doc, xmlNodePtr cur)
193 {
194         Item *item;
195         
196         xmlChar *uri;
197         xmlChar *atime;
198         
199         if (xmlStrcmp (cur->name, (const xmlChar *)"document") != 0)
200                         return;
201
202         uri = xmlGetProp (cur, (const xmlChar *)"uri");
203         if (uri == NULL)
204                 return;
205         
206         atime = xmlGetProp (cur, (const xmlChar *)"atime");
207         if (atime == NULL)
208         {
209                 xmlFree (uri);
210                 return;
211         }
212
213         item = g_new0 (Item, 1);
214
215         item->atime = g_ascii_strtoull((char*)atime, NULL, 0);
216         
217         item->values = g_hash_table_new_full (g_str_hash, 
218                                               g_str_equal, 
219                                               g_free, 
220                                               value_free);
221
222         cur = cur->xmlChildrenNode;
223                 
224         while (cur != NULL)
225         {
226                 if (xmlStrcmp (cur->name, (const xmlChar *)"entry") == 0)
227                 {
228                         xmlChar *key;
229                         xmlChar *xml_value;
230                         xmlChar *type;
231                         GValue  *value;
232                         
233                         key = xmlGetProp (cur, (const xmlChar *)"key");
234                         xml_value = xmlGetProp (cur, (const xmlChar *)"value");
235                         type = xmlGetProp (cur, (const xmlChar *)"type");
236                         value = parse_value (xml_value, type);
237
238                         if ((key != NULL) && (value != NULL))
239                                 g_hash_table_insert (item->values,
240                                                      xmlStrdup (key), 
241                                                      value);
242
243                         if (key != NULL)
244                                 xmlFree (key);
245                         if (type != NULL)
246                                 xmlFree (type);
247                         if (xml_value != NULL)
248                                 xmlFree (xml_value);
249                 }
250                 
251                 cur = cur->next;
252         }
253
254         g_hash_table_insert (ev_metadata_manager->items,
255                              xmlStrdup (uri),
256                              item);
257
258         xmlFree (uri);
259         xmlFree (atime);
260 }
261
262 static gboolean
263 load_values ()
264 {
265         xmlDocPtr doc;
266         xmlNodePtr cur;
267         gchar *file_name;
268
269         g_return_val_if_fail (ev_metadata_manager != NULL, FALSE);
270         g_return_val_if_fail (ev_metadata_manager->values_loaded == FALSE, FALSE);
271
272         ev_metadata_manager->values_loaded = TRUE;
273                 
274         xmlKeepBlanksDefault (0);
275
276         /* FIXME: file locking - Paolo */
277         file_name = g_build_filename (ev_dot_dir (), METADATA_FILE, NULL);
278         if (!g_file_test (file_name, G_FILE_TEST_EXISTS))
279         {
280                 g_free (file_name);
281                 return FALSE;
282         }
283
284         doc = xmlParseFile (file_name);
285         g_free (file_name);
286
287         if (doc == NULL)
288         {
289                 return FALSE;
290         }
291
292         cur = xmlDocGetRootElement (doc);
293         if (cur == NULL) 
294         {
295                 g_message ("The metadata file “%s” is empty", METADATA_FILE);
296                 xmlFreeDoc (doc);
297         
298                 return FALSE;
299         }
300
301         if (xmlStrcmp (cur->name, (const xmlChar *) "metadata")) 
302         {
303                 g_message ("File “%s” is of the wrong type", METADATA_FILE);
304                 xmlFreeDoc (doc);
305                 
306                 return FALSE;
307         }
308
309         cur = xmlDocGetRootElement (doc);
310         cur = cur->xmlChildrenNode;
311         
312         while (cur != NULL)
313         {
314                 parseItem (doc, cur);
315
316                 cur = cur->next;
317         }
318
319         xmlFreeDoc (doc);
320
321         return TRUE;
322 }
323
324 #define LAST_URI "last-used-value"
325
326 static gboolean
327 ev_metadata_manager_get_last (const gchar *key,
328                                  GValue      *value,
329                                  gboolean     ignore)
330 {
331         Item *item;
332         GValue *ret;
333
334         g_assert (ev_metadata_manager->values_loaded);
335         
336         if (ignore)
337                 return FALSE;
338
339         item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
340                                             LAST_URI);
341
342         if (item == NULL)
343                 return FALSE;
344
345         item->atime = time (NULL);
346         
347         if (item->values == NULL)
348                 return FALSE;
349         
350         ret = (GValue *)g_hash_table_lookup (item->values, key);
351
352         if (ret != NULL) {
353                 g_value_init (value, G_VALUE_TYPE (ret));
354                 g_value_copy (ret, value);
355                 return TRUE;
356         }
357
358         return FALSE;
359 }
360
361 static void
362 ev_metadata_manager_set_last (const gchar *key,
363                               const GValue *value)
364 {
365         Item *item;
366         
367         g_assert (ev_metadata_manager->values_loaded);
368
369         item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
370                                             LAST_URI);
371
372         if (item == NULL)
373         {
374                 item = g_new0 (Item, 1);
375
376                 g_hash_table_insert (ev_metadata_manager->items,
377                                      g_strdup (LAST_URI),
378                                      item);
379         }
380         
381         if (item->values == NULL)
382                  item->values = g_hash_table_new_full (g_str_hash, 
383                                                        g_str_equal, 
384                                                        g_free, 
385                                                        value_free);
386         if (value != NULL) {
387                 GValue *new;
388
389                 new = g_new0 (GValue, 1);
390                 g_value_init (new, G_VALUE_TYPE (value));
391                 g_value_copy (value, new);
392
393                 g_hash_table_insert (item->values,
394                                      g_strdup (key),
395                                      new);
396         } else {
397                 g_hash_table_remove (item->values,
398                                      key);
399         }
400
401         item->atime = time (NULL);
402         ev_metadata_arm_timeout ();
403         return;
404 }
405                                  
406 /**
407  * ev_metadata_manager_get:
408  * @uri: Uri to set data for, if @NULL, we return default value
409  * @key: Key to set uri
410  * @value: GValue struct filled up with value
411  * @ignore_last: if @TRUE, default value is ignored
412  * 
413  * Retrieve value for uri in metadata database
414  * 
415  * Returns: @TRUE if value was taken.
416  **/
417 gboolean
418 ev_metadata_manager_get (const gchar *uri,
419                          const gchar *key,
420                          GValue      *value, 
421                          gboolean     ignore_last)
422 {
423         Item *item;
424         GValue *ret;
425         
426         g_return_val_if_fail (key != NULL, FALSE);
427
428         if (ev_metadata_manager == NULL)
429                 return FALSE;
430
431         if (!ev_metadata_manager->values_loaded)
432         {
433                 gboolean res;
434
435                 res = load_values ();
436
437                 if (!res)
438                         return ev_metadata_manager_get_last (key, value, ignore_last);
439         }
440
441         if (uri == NULL)
442                 return ev_metadata_manager_get_last (key, value, ignore_last);
443
444         item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
445                                             uri);
446
447         if (item == NULL)
448                 return ev_metadata_manager_get_last (key, value, ignore_last);
449
450         item->atime = time (NULL);
451         
452         if (item->values == NULL)
453                 return ev_metadata_manager_get_last (key, value, ignore_last);
454         
455         ret = (GValue *)g_hash_table_lookup (item->values, key);
456
457         if (ret != NULL) {
458                 g_value_init (value, G_VALUE_TYPE (ret));
459                 g_value_copy (ret, value);
460                 return TRUE;
461         }
462
463         return ev_metadata_manager_get_last (key, value, ignore_last);
464 }
465
466 /**
467  * ev_metadata_manager_set:
468  * @uri: Uri to set data for, if @NULL, we set default value
469  * @key: Key to set uri
470  * @value: GValue struct containing value
471  * 
472  * Set value for key in metadata database
473  **/
474 void
475 ev_metadata_manager_set (const gchar  *uri,
476                          const gchar  *key,
477                          const GValue *value)
478 {
479         Item *item;
480
481         g_return_if_fail (key != NULL);
482
483         if (ev_metadata_manager == NULL)
484                 return;
485
486         if (!ev_metadata_manager->values_loaded)
487         {
488                 gboolean res;
489
490                 res = load_values ();
491
492                 if (!res)
493                         return;
494         }
495
496         if (uri == NULL)
497         {
498                 ev_metadata_manager_set_last (key, value);
499                 return;
500         }
501
502         item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
503                                             uri);
504
505         if (item == NULL)
506         {
507                 item = g_new0 (Item, 1);
508
509                 g_hash_table_insert (ev_metadata_manager->items,
510                                      g_strdup (uri),
511                                      item);
512         }
513         
514         if (item->values == NULL)
515                  item->values = g_hash_table_new_full (g_str_hash, 
516                                                        g_str_equal, 
517                                                        g_free, 
518                                                        value_free);
519         if (value != NULL) {
520                 GValue *new;
521
522                 new = g_new0 (GValue, 1);
523                 g_value_init (new, G_VALUE_TYPE (value));
524                 g_value_copy (value, new);
525
526                 g_hash_table_insert (item->values,
527                                      g_strdup (key),
528                                      new);
529                 ev_metadata_manager_set_last (key, value);
530         } else {
531                 g_hash_table_remove (item->values,
532                                      key);
533         }
534
535         item->atime = time (NULL);
536
537         ev_metadata_arm_timeout ();
538 }
539
540 static void
541 save_values (const gchar *key, GValue *value, xmlNodePtr parent)
542 {
543         char *string_value;
544         xmlNodePtr xml_node;
545         
546         g_return_if_fail (key != NULL);
547         
548         if (value == NULL)
549                 return;
550                 
551         xml_node = xmlNewChild (parent, NULL, (const xmlChar *)"entry", NULL);
552
553         xmlSetProp (xml_node, (const xmlChar *)"key", (const xmlChar *)key);
554         xmlSetProp (xml_node,
555                     (const xmlChar *)"type",
556                     (const xmlChar *)g_type_name (G_VALUE_TYPE (value)));
557
558         switch (G_VALUE_TYPE (value)) {
559                 case G_TYPE_STRING:
560                         string_value = g_strdup (g_value_get_string (value));
561                         break;
562                 case G_TYPE_INT:
563                         string_value = g_strdup_printf ("%d", g_value_get_int (value));
564                         break;
565                 case G_TYPE_DOUBLE:
566                         {
567                                 gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
568                                 g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, g_value_get_double (value));
569                                 string_value = g_strdup_printf ("%s", buf);
570                         }
571                         break;
572                 case G_TYPE_BOOLEAN:
573                         string_value = g_strdup_printf ("%d", g_value_get_boolean (value));
574                         break;
575                 default:
576                         string_value = NULL;
577                         g_assert_not_reached ();
578         }
579
580         xmlSetProp (xml_node, (const xmlChar *)"value", (const xmlChar *)string_value);
581
582         g_free (string_value);
583 }
584
585 static void
586 save_item (const gchar *key, const gpointer *data, xmlNodePtr parent)
587 {       
588         xmlNodePtr xml_node;
589         const Item *item = (const Item *)data;
590         gchar *atime;
591
592         g_return_if_fail (key != NULL);
593         
594         if (item == NULL)
595                 return;
596                 
597         xml_node = xmlNewChild (parent, NULL, (const xmlChar *)"document", NULL);
598         
599         xmlSetProp (xml_node, (const xmlChar *)"uri", (const xmlChar *)key);
600
601         atime = g_strdup_printf ("%ld", item->atime);
602         xmlSetProp (xml_node, (const xmlChar *)"atime", (const xmlChar *)atime);
603         g_free (atime);
604
605         g_hash_table_foreach (item->values,
606                               (GHFunc)save_values, xml_node); 
607 }
608
609 static void
610 get_oldest (const gchar *key, const gpointer value, const gchar ** key_to_remove)
611 {
612         const Item *item = (const Item *)value;
613         
614         if (*key_to_remove == NULL)
615         {
616                 *key_to_remove = key;
617         }
618         else
619         {
620                 const Item *item_to_remove = 
621                         g_hash_table_lookup (ev_metadata_manager->items,
622                                              *key_to_remove);
623
624                 g_return_if_fail (item_to_remove != NULL);
625
626                 if (item->atime < item_to_remove->atime)
627                 {
628                         *key_to_remove = key;
629                 }
630         }       
631 }
632
633 static void
634 resize_items ()
635 {
636         while (g_hash_table_size (ev_metadata_manager->items) > MAX_ITEMS)
637         {
638                 gpointer key_to_remove = NULL;
639
640                 g_hash_table_foreach (ev_metadata_manager->items,
641                                       (GHFunc)get_oldest,
642                                       &key_to_remove);
643
644                 g_return_if_fail (key_to_remove != NULL);
645                 
646                 g_hash_table_remove (ev_metadata_manager->items,
647                                      key_to_remove);
648         }
649 }
650
651 static gboolean
652 ev_metadata_manager_save (gpointer data)
653 {       
654         xmlDocPtr  doc;
655         xmlNodePtr root;
656         gchar *file_name;
657
658         ev_metadata_manager->timeout_id = 0;
659
660         resize_items ();
661                 
662         xmlIndentTreeOutput = TRUE;
663
664         doc = xmlNewDoc ((const xmlChar *)"1.0");
665         if (doc == NULL)
666                 return TRUE;
667
668         /* Create metadata root */
669         root = xmlNewDocNode (doc, NULL, (const xmlChar *)"metadata", NULL);
670         xmlDocSetRootElement (doc, root);
671
672         g_hash_table_foreach (ev_metadata_manager->items,
673                           (GHFunc)save_item, root);        
674
675         /* FIXME: lock file - Paolo */
676         file_name = g_build_filename (ev_dot_dir (), METADATA_FILE, NULL);
677         xmlSaveFormatFile (file_name, doc, 1);
678         g_free (file_name);
679         
680         xmlFreeDoc (doc); 
681
682         return FALSE;
683 }
684
685 void
686 ev_metadata_manager_set_int (const gchar *uri, const gchar *key, int value)
687 {
688         GValue val = { 0, };
689
690         g_value_init (&val, G_TYPE_INT);
691         g_value_set_int (&val, value);
692
693         ev_metadata_manager_set (uri, key, &val);
694
695         g_value_unset (&val);
696 }
697
698 void
699 ev_metadata_manager_set_double (const gchar *uri, const gchar *key, double value)
700 {
701         GValue val = { 0, };
702
703         g_value_init (&val, G_TYPE_DOUBLE);
704         g_value_set_double (&val, value);
705
706         ev_metadata_manager_set (uri, key, &val);
707
708         g_value_unset (&val);
709 }
710
711 void
712 ev_metadata_manager_set_string (const gchar *uri, const gchar *key, const gchar *value)
713 {
714         GValue val = { 0, };
715
716         g_value_init (&val, G_TYPE_STRING);
717         g_value_set_string (&val, value);
718
719         ev_metadata_manager_set (uri, key, &val);
720
721         g_value_unset (&val);
722 }
723
724 void
725 ev_metadata_manager_set_boolean (const gchar *uri, const gchar *key, gboolean value)
726 {
727         GValue val = { 0, };
728
729         g_value_init (&val, G_TYPE_BOOLEAN);
730         g_value_set_boolean (&val, value);
731
732         ev_metadata_manager_set (uri, key, &val);
733
734         g_value_unset (&val);
735 }