]> www.fi.muni.cz Git - evince.git/blob - shell/ev-metadata-manager.c
(ev_metadata_manager_set_int) (ev_metadata_manager_set_double,
[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 _GeditMetadataManager GeditMetadataManager;
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 _GeditMetadataManager
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 void ev_metadata_manager_save (gpointer data);
71
72
73 static GeditMetadataManager *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 static gboolean
91 ev_metadata_manager_init (void)
92 {
93         if (ev_metadata_manager != NULL)
94                 return TRUE;
95
96         ev_metadata_manager = g_new0 (GeditMetadataManager, 1);
97
98         ev_metadata_manager->values_loaded = FALSE;
99         ev_metadata_manager->modified = FALSE;
100
101         ev_metadata_manager->items = 
102                 g_hash_table_new_full (g_str_hash, 
103                                        g_str_equal, 
104                                        g_free,
105                                        item_free);
106
107         ev_metadata_manager->timeout_id = 
108                 g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
109                                     2000, /* 2 sec */
110                                     (GSourceFunc)ev_metadata_manager_save,
111                                     NULL,
112                                     NULL);
113         
114         return TRUE;
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         g_source_remove (ev_metadata_manager->timeout_id);
125
126         ev_metadata_manager_save (NULL);
127
128         if (ev_metadata_manager->items != NULL)
129                 g_hash_table_destroy (ev_metadata_manager->items);
130
131         g_free (ev_metadata_manager);
132         ev_metadata_manager = NULL;
133 }
134
135 static GValue *
136 parse_value (xmlChar *value, xmlChar *type)
137 {
138         GType ret_type;
139         GValue *ret;
140
141         ret_type = g_type_from_name ((char *)type);
142         ret = g_new0 (GValue, 1);
143         g_value_init (ret, ret_type);
144
145         switch (ret_type) {
146                 case G_TYPE_STRING:
147                         g_value_set_string (ret, (char *)value);
148                         break;
149                 case G_TYPE_INT:
150                         g_value_set_int (ret, atoi ((char *)value));
151                         break;
152                 case G_TYPE_DOUBLE:
153                         g_value_set_double (ret, atof ((char *)value));
154                         break;
155                 case G_TYPE_BOOLEAN:
156                         g_value_set_boolean (ret, atoi ((char *)value));
157                         break;
158         }
159
160         return ret;
161 }
162
163 static void
164 parseItem (xmlDocPtr doc, xmlNodePtr cur)
165 {
166         Item *item;
167         
168         xmlChar *uri;
169         xmlChar *atime;
170         
171         if (xmlStrcmp (cur->name, (const xmlChar *)"document") != 0)
172                         return;
173
174         uri = xmlGetProp (cur, (const xmlChar *)"uri");
175         if (uri == NULL)
176                 return;
177         
178         atime = xmlGetProp (cur, (const xmlChar *)"atime");
179         if (atime == NULL)
180         {
181                 xmlFree (uri);
182                 return;
183         }
184
185         item = g_new0 (Item, 1);
186
187         item->atime = atol ((char *)atime);
188         
189         item->values = g_hash_table_new_full (g_str_hash, 
190                                               g_str_equal, 
191                                               g_free, 
192                                               g_free);
193
194         cur = cur->xmlChildrenNode;
195                 
196         while (cur != NULL)
197         {
198                 if (xmlStrcmp (cur->name, (const xmlChar *)"entry") == 0)
199                 {
200                         xmlChar *key;
201                         xmlChar *xml_value;
202                         xmlChar *type;
203                         GValue *value;
204                         
205                         key = xmlGetProp (cur, (const xmlChar *)"key");
206                         xml_value = xmlGetProp (cur, (const xmlChar *)"value");
207                         type = xmlGetProp (cur, (const xmlChar *)"type");
208                         value = parse_value (xml_value, type);
209
210                         if ((key != NULL) && (value != NULL))
211                                 g_hash_table_insert (item->values,
212                                                      xmlStrdup (key), 
213                                                      value);
214
215                         if (key != NULL)
216                                 xmlFree (key);
217                         if (xml_value != NULL)
218                                 xmlFree (xml_value);
219                 }
220                 
221                 cur = cur->next;
222         }
223
224         g_hash_table_insert (ev_metadata_manager->items,
225                              xmlStrdup (uri),
226                              item);
227
228         xmlFree (uri);
229         xmlFree (atime);
230 }
231
232 static gboolean
233 load_values ()
234 {
235         xmlDocPtr doc;
236         xmlNodePtr cur;
237         gchar *file_name;
238
239         g_return_val_if_fail (ev_metadata_manager != NULL, FALSE);
240         g_return_val_if_fail (ev_metadata_manager->values_loaded == FALSE, FALSE);
241
242         ev_metadata_manager->values_loaded = TRUE;
243                 
244         xmlKeepBlanksDefault (0);
245
246         /* FIXME: file locking - Paolo */
247         file_name = g_build_filename (ev_dot_dir (), METADATA_FILE, NULL);
248         if (!g_file_test (file_name, G_FILE_TEST_EXISTS))
249         {
250                 g_free (file_name);
251                 return FALSE;
252         }
253
254         doc = xmlParseFile (file_name);
255         g_free (file_name);
256
257         if (doc == NULL)
258         {
259                 return FALSE;
260         }
261
262         cur = xmlDocGetRootElement (doc);
263         if (cur == NULL) 
264         {
265                 g_message ("The metadata file '%s' is empty", METADATA_FILE);
266                 xmlFreeDoc (doc);
267         
268                 return FALSE;
269         }
270
271         if (xmlStrcmp (cur->name, (const xmlChar *) "metadata")) 
272         {
273                 g_message ("File '%s' is of the wrong type", METADATA_FILE);
274                 xmlFreeDoc (doc);
275                 
276                 return FALSE;
277         }
278
279         cur = xmlDocGetRootElement (doc);
280         cur = cur->xmlChildrenNode;
281         
282         while (cur != NULL)
283         {
284                 parseItem (doc, cur);
285
286                 cur = cur->next;
287         }
288
289         xmlFreeDoc (doc);
290
291         return TRUE;
292 }
293
294 gboolean
295 ev_metadata_manager_get (const gchar *uri,
296                          const gchar *key,
297                          GValue      *value)
298 {
299         Item *item;
300         GValue *ret;
301         
302         g_return_val_if_fail (uri != NULL, FALSE);
303         g_return_val_if_fail (key != NULL, FALSE);
304
305         if (ev_metadata_manager == NULL)
306                 ev_metadata_manager_init ();
307
308         if (!ev_metadata_manager->values_loaded)
309         {
310                 gboolean res;
311
312                 res = load_values ();
313
314                 if (!res)
315                         return FALSE;
316         }
317
318         item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
319                                             uri);
320
321         if (item == NULL)
322                 return FALSE;
323
324         item->atime = time (NULL);
325         
326         if (item->values == NULL)
327                 return FALSE;
328         
329         ret = (GValue *)g_hash_table_lookup (item->values, key);
330
331         if (ret == NULL) {
332                 return FALSE;
333         } else {
334                 g_value_init (value, G_VALUE_TYPE (ret));
335                 g_value_copy (ret, value);
336
337                 return TRUE;
338         }
339 }
340
341 static void
342 value_free (gpointer data)
343 {
344         GValue *value = (GValue *)data;
345
346         g_value_unset (value);
347         g_free (value);
348 }
349
350 void
351 ev_metadata_manager_set (const gchar  *uri,
352                          const gchar  *key,
353                          const GValue *value)
354 {
355         Item *item;
356
357         g_return_if_fail (uri != NULL);
358         g_return_if_fail (key != NULL);
359
360         if (ev_metadata_manager == NULL)
361                 ev_metadata_manager_init ();
362
363         if (!ev_metadata_manager->values_loaded)
364         {
365                 gboolean res;
366
367                 res = load_values ();
368
369                 if (!res)
370                         return;
371         }
372
373         item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
374                                             uri);
375
376         if (item == NULL)
377         {
378                 item = g_new0 (Item, 1);
379
380                 g_hash_table_insert (ev_metadata_manager->items,
381                                      g_strdup (uri),
382                                      item);
383         }
384         
385         if (item->values == NULL)
386                  item->values = g_hash_table_new_full (g_str_hash, 
387                                                        g_str_equal, 
388                                                        g_free, 
389                                                        value_free);
390         if (value != NULL) {
391                 GValue *new;
392
393                 new = g_new0 (GValue, 1);
394                 g_value_init (new, G_VALUE_TYPE (value));
395                 g_value_copy (value, new);
396
397                 g_hash_table_insert (item->values,
398                                      g_strdup (key),
399                                      new);
400         } else {
401                 g_hash_table_remove (item->values,
402                                      key);
403         }
404
405         item->atime = time (NULL);
406
407         ev_metadata_manager->modified = TRUE;
408 }
409
410 static void
411 save_values (const gchar *key, GValue *value, xmlNodePtr parent)
412 {
413         char *string_value;
414         xmlNodePtr xml_node;
415         
416         g_return_if_fail (key != NULL);
417         
418         if (value == NULL)
419                 return;
420                 
421         xml_node = xmlNewChild (parent, NULL, (const xmlChar *)"entry", NULL);
422
423         xmlSetProp (xml_node, (const xmlChar *)"key", (const xmlChar *)key);
424         xmlSetProp (xml_node,
425                     (const xmlChar *)"type",
426                     (const xmlChar *)g_type_name (G_VALUE_TYPE (value)));
427
428         switch (G_VALUE_TYPE (value)) {
429                 case G_TYPE_STRING:
430                         string_value = g_strdup (g_value_get_string (value));
431                         break;
432                 case G_TYPE_INT:
433                         string_value = g_strdup_printf ("%d", g_value_get_int (value));
434                         break;
435                 case G_TYPE_DOUBLE:
436                         string_value = g_strdup_printf ("%f", g_value_get_double (value));
437                         break;
438                 case G_TYPE_BOOLEAN:
439                         string_value = g_strdup_printf ("%d", g_value_get_boolean (value));
440                         break;
441                 default:
442                         string_value = NULL;
443                         g_assert_not_reached ();
444         }
445
446         xmlSetProp (xml_node, (const xmlChar *)"value", (const xmlChar *)string_value);
447
448         g_free (string_value);
449 }
450
451 static void
452 save_item (const gchar *key, const gpointer *data, xmlNodePtr parent)
453 {       
454         xmlNodePtr xml_node;
455         const Item *item = (const Item *)data;
456         gchar *atime;
457
458         g_return_if_fail (key != NULL);
459         
460         if (item == NULL)
461                 return;
462                 
463         xml_node = xmlNewChild (parent, NULL, (const xmlChar *)"document", NULL);
464         
465         xmlSetProp (xml_node, (const xmlChar *)"uri", (const xmlChar *)key);
466
467         /* FIXME: is the cast right? - Paolo */
468         atime = g_strdup_printf ("%d", (int)item->atime);
469         xmlSetProp (xml_node, (const xmlChar *)"atime", (const xmlChar *)atime);        
470
471         g_free (atime);
472
473         g_hash_table_foreach (item->values,
474                               (GHFunc)save_values, xml_node); 
475 }
476
477 static void
478 get_oldest (const gchar *key, const gpointer value, const gchar ** key_to_remove)
479 {
480         const Item *item = (const Item *)value;
481         
482         if (*key_to_remove == NULL)
483         {
484                 *key_to_remove = key;
485         }
486         else
487         {
488                 const Item *item_to_remove = 
489                         g_hash_table_lookup (ev_metadata_manager->items,
490                                              *key_to_remove);
491
492                 g_return_if_fail (item_to_remove != NULL);
493
494                 if (item->atime < item_to_remove->atime)
495                 {
496                         *key_to_remove = key;
497                 }
498         }       
499 }
500
501 static void
502 resize_items ()
503 {
504         while (g_hash_table_size (ev_metadata_manager->items) > MAX_ITEMS)
505         {
506                 gpointer key_to_remove = NULL;
507
508                 g_hash_table_foreach (ev_metadata_manager->items,
509                                       (GHFunc)get_oldest,
510                                       &key_to_remove);
511
512                 g_return_if_fail (key_to_remove != NULL);
513                 
514                 g_hash_table_remove (ev_metadata_manager->items,
515                                      key_to_remove);
516         }
517 }
518
519 static void
520 ev_metadata_manager_save (gpointer data)
521 {       
522         xmlDocPtr  doc;
523         xmlNodePtr root;
524         gchar *file_name;
525
526         if (!ev_metadata_manager->modified)
527                 return;
528
529         resize_items ();
530                 
531         xmlIndentTreeOutput = TRUE;
532
533         doc = xmlNewDoc ((const xmlChar *)"1.0");
534         if (doc == NULL)
535                 return;
536
537         /* Create metadata root */
538         root = xmlNewDocNode (doc, NULL, (const xmlChar *)"metadata", NULL);
539         xmlDocSetRootElement (doc, root);
540
541         g_hash_table_foreach (ev_metadata_manager->items,
542                           (GHFunc)save_item, root);        
543
544         /* FIXME: lock file - Paolo */
545         file_name = g_build_filename (ev_dot_dir (), METADATA_FILE, NULL);
546         xmlSaveFormatFile (file_name, doc, 1);
547         g_free (file_name);
548         
549         xmlFreeDoc (doc); 
550
551         ev_metadata_manager->modified = FALSE;
552 }
553
554 void
555 ev_metadata_manager_set_int (const gchar *uri, const gchar *key, int value)
556 {
557         GValue val = { 0, };
558
559         g_value_init (&val, G_TYPE_INT);
560         g_value_set_int (&val, value);
561
562         ev_metadata_manager_set (uri, key, &val);
563
564         g_value_unset (&val);
565 }
566
567 void
568 ev_metadata_manager_set_double (const gchar *uri, const gchar *key, double value)
569 {
570         GValue val = { 0, };
571
572         g_value_init (&val, G_TYPE_DOUBLE);
573         g_value_set_double (&val, value);
574
575         ev_metadata_manager_set (uri, key, &val);
576
577         g_value_unset (&val);
578 }
579
580 void
581 ev_metadata_manager_set_string (const gchar *uri, const gchar *key, const gchar *value)
582 {
583         GValue val = { 0, };
584
585         g_value_init (&val, G_TYPE_STRING);
586         g_value_set_string (&val, value);
587
588         ev_metadata_manager_set (uri, key, &val);
589
590         g_value_unset (&val);
591 }
592
593 void
594 ev_metadata_manager_set_boolean (const gchar *uri, const gchar *key, gboolean value)
595 {
596         GValue val = { 0, };
597
598         g_value_init (&val, G_TYPE_BOOLEAN);
599         g_value_set_boolean (&val, value);
600
601         ev_metadata_manager_set (uri, key, &val);
602
603         g_value_unset (&val);
604 }