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