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