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