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