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