1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * ev-metadata-manager.c
4 * This file is part of ev
6 * Copyright (C) 2003 Paolo Maggi
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.
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.
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.
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.
37 #include <libxml/xmlreader.h>
39 #include "ev-metadata-manager.h"
40 #include "ev-application.h"
41 #include "ev-file-helpers.h"
43 #define METADATA_FILE "ev-metadata.xml"
47 typedef struct _EvMetadataManager EvMetadataManager;
49 typedef struct _Item Item;
53 time_t atime; /* time of last access */
58 struct _EvMetadataManager
60 gboolean values_loaded; /* It is true if the file
68 static gboolean ev_metadata_manager_save (gpointer data);
71 static EvMetadataManager *ev_metadata_manager = NULL;
75 * @data: a pointer to a #Item data
77 * It does free the values on the #GHashTable where data points.
80 item_free (gpointer data)
82 Item *item = (Item *) data;
84 if (item->values != NULL)
85 g_hash_table_destroy (item->values);
87 g_slice_free (Item, item);
91 * ev_metadata_arm_timeout
93 * Setup a timeout for saving the metadata to disk.
96 ev_metadata_arm_timeout(void)
98 if (ev_metadata_manager->timeout_id)
101 ev_metadata_manager->timeout_id =
102 g_timeout_add_seconds_full (G_PRIORITY_DEFAULT_IDLE,
104 (GSourceFunc)ev_metadata_manager_save,
110 * ev_metadata_manager_init:
112 * Creates an EvMetadataManager with default values.
114 * values_loaded -> %FALSE.
115 * timeout_id -> the id of the event source.
116 * items -> a new full empty #GHashTable.
119 ev_metadata_manager_init (void)
121 ev_metadata_manager = g_slice_new0 (EvMetadataManager);
123 ev_metadata_manager->values_loaded = FALSE;
125 ev_metadata_manager->items =
126 g_hash_table_new_full (g_str_hash,
132 /* This function must be called before exiting ev */
134 ev_metadata_manager_shutdown (void)
136 if (ev_metadata_manager == NULL)
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);
145 if (ev_metadata_manager->items != NULL)
146 g_hash_table_destroy (ev_metadata_manager->items);
148 g_slice_free (EvMetadataManager, ev_metadata_manager);
149 ev_metadata_manager = NULL;
153 value_free (gpointer data)
155 GValue *value = (GValue *)data;
157 g_value_unset (value);
158 g_slice_free (GValue, value);
162 parse_value (xmlChar *value, xmlChar *type)
167 ret_type = g_type_from_name ((char *)type);
168 ret = g_slice_new0 (GValue);
169 g_value_init (ret, ret_type);
173 g_value_set_string (ret, (char *)value);
176 g_value_set_int (ret, g_ascii_strtoull ((char *)value, NULL, 0));
179 g_value_set_double (ret, g_ascii_strtod ((char *)value, NULL));
182 g_value_set_boolean (ret, g_ascii_strtoull ((char *)value, NULL, 0));
190 parseItem (xmlDocPtr doc, xmlNodePtr cur)
197 if (xmlStrcmp (cur->name, (const xmlChar *)"document") != 0)
200 uri = xmlGetProp (cur, (const xmlChar *)"uri");
204 atime = xmlGetProp (cur, (const xmlChar *)"atime");
211 item = g_slice_new0 (Item);
213 item->atime = g_ascii_strtoull((char*)atime, NULL, 0);
215 item->values = g_hash_table_new_full (g_str_hash,
220 cur = cur->xmlChildrenNode;
224 if (xmlStrcmp (cur->name, (const xmlChar *)"entry") == 0)
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);
236 if ((key != NULL) && (value != NULL))
237 g_hash_table_insert (item->values,
245 if (xml_value != NULL)
252 g_hash_table_insert (ev_metadata_manager->items,
267 g_return_val_if_fail (ev_metadata_manager != NULL, FALSE);
268 g_return_val_if_fail (ev_metadata_manager->values_loaded == FALSE, FALSE);
270 ev_metadata_manager->values_loaded = TRUE;
272 xmlKeepBlanksDefault (0);
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))
282 doc = xmlParseFile (file_name);
290 cur = xmlDocGetRootElement (doc);
293 g_message ("The metadata file “%s” is empty", METADATA_FILE);
299 if (xmlStrcmp (cur->name, (const xmlChar *) "metadata"))
301 g_message ("File “%s” is of the wrong type", METADATA_FILE);
307 cur = xmlDocGetRootElement (doc);
308 cur = cur->xmlChildrenNode;
312 parseItem (doc, cur);
322 #define LAST_URI "last-used-value"
325 ev_metadata_manager_get_last (const gchar *key,
332 g_assert (ev_metadata_manager->values_loaded);
337 item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
343 item->atime = time (NULL);
345 if (item->values == NULL)
348 ret = (GValue *)g_hash_table_lookup (item->values, key);
351 g_value_init (value, G_VALUE_TYPE (ret));
352 g_value_copy (ret, value);
360 ev_metadata_manager_set_last (const gchar *key,
365 g_assert (ev_metadata_manager->values_loaded);
367 item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
372 item = g_slice_new0 (Item);
374 g_hash_table_insert (ev_metadata_manager->items,
379 if (item->values == NULL)
380 item->values = g_hash_table_new_full (g_str_hash,
387 new = g_slice_new0 (GValue);
388 g_value_init (new, G_VALUE_TYPE (value));
389 g_value_copy (value, new);
391 g_hash_table_insert (item->values,
395 g_hash_table_remove (item->values,
399 item->atime = time (NULL);
400 ev_metadata_arm_timeout ();
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
411 * Retrieve value for uri in metadata database
413 * Returns: @TRUE if value was taken.
416 ev_metadata_manager_get (const gchar *uri,
419 gboolean ignore_last)
424 g_return_val_if_fail (key != NULL, FALSE);
426 if (ev_metadata_manager == NULL)
429 if (!ev_metadata_manager->values_loaded)
433 res = load_values ();
436 return ev_metadata_manager_get_last (key, value, ignore_last);
440 return ev_metadata_manager_get_last (key, value, ignore_last);
442 item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
446 return ev_metadata_manager_get_last (key, value, ignore_last);
448 item->atime = time (NULL);
450 if (item->values == NULL)
451 return ev_metadata_manager_get_last (key, value, ignore_last);
453 ret = (GValue *)g_hash_table_lookup (item->values, key);
456 g_value_init (value, G_VALUE_TYPE (ret));
457 g_value_copy (ret, value);
461 return ev_metadata_manager_get_last (key, value, ignore_last);
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
470 * Set value for key in metadata database
473 ev_metadata_manager_set (const gchar *uri,
479 g_return_if_fail (key != NULL);
481 if (ev_metadata_manager == NULL)
484 if (!ev_metadata_manager->values_loaded)
488 res = load_values ();
496 ev_metadata_manager_set_last (key, value);
500 item = (Item *)g_hash_table_lookup (ev_metadata_manager->items,
505 item = g_slice_new0 (Item);
507 g_hash_table_insert (ev_metadata_manager->items,
512 if (item->values == NULL)
513 item->values = g_hash_table_new_full (g_str_hash,
520 new = g_slice_new0 (GValue);
521 g_value_init (new, G_VALUE_TYPE (value));
522 g_value_copy (value, new);
524 g_hash_table_insert (item->values,
527 ev_metadata_manager_set_last (key, value);
529 g_hash_table_remove (item->values,
533 item->atime = time (NULL);
535 ev_metadata_arm_timeout ();
539 save_values (const gchar *key, GValue *value, xmlNodePtr parent)
544 g_return_if_fail (key != NULL);
549 xml_node = xmlNewChild (parent, NULL, (const xmlChar *)"entry", NULL);
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)));
556 switch (G_VALUE_TYPE (value)) {
558 string_value = g_value_dup_string (value);
561 string_value = g_strdup_printf ("%d", g_value_get_int (value));
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);
571 string_value = g_strdup_printf ("%d", g_value_get_boolean (value));
575 g_assert_not_reached ();
578 xmlSetProp (xml_node, (const xmlChar *)"value", (const xmlChar *)string_value);
580 g_free (string_value);
584 save_item (const gchar *key, const gpointer *data, xmlNodePtr parent)
587 const Item *item = (const Item *)data;
590 g_return_if_fail (key != NULL);
595 xml_node = xmlNewChild (parent, NULL, (const xmlChar *)"document", NULL);
597 xmlSetProp (xml_node, (const xmlChar *)"uri", (const xmlChar *)key);
599 atime = g_strdup_printf ("%ld", item->atime);
600 xmlSetProp (xml_node, (const xmlChar *)"atime", (const xmlChar *)atime);
603 g_hash_table_foreach (item->values,
604 (GHFunc)save_values, xml_node);
608 get_oldest (const gchar *key, const gpointer value, const gchar ** key_to_remove)
610 const Item *item = (const Item *)value;
612 if (*key_to_remove == NULL)
614 *key_to_remove = key;
618 const Item *item_to_remove =
619 g_hash_table_lookup (ev_metadata_manager->items,
622 g_return_if_fail (item_to_remove != NULL);
624 if (item->atime < item_to_remove->atime)
626 *key_to_remove = key;
634 while (g_hash_table_size (ev_metadata_manager->items) > MAX_ITEMS)
636 gpointer key_to_remove = NULL;
638 g_hash_table_foreach (ev_metadata_manager->items,
642 g_return_if_fail (key_to_remove != NULL);
644 g_hash_table_remove (ev_metadata_manager->items,
650 ev_metadata_manager_save (gpointer data)
656 ev_metadata_manager->timeout_id = 0;
660 xmlIndentTreeOutput = TRUE;
662 doc = xmlNewDoc ((const xmlChar *)"1.0");
666 /* Create metadata root */
667 root = xmlNewDocNode (doc, NULL, (const xmlChar *)"metadata", NULL);
668 xmlDocSetRootElement (doc, root);
670 g_hash_table_foreach (ev_metadata_manager->items,
671 (GHFunc)save_item, root);
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);
684 ev_metadata_manager_set_int (const gchar *uri, const gchar *key, int value)
688 g_value_init (&val, G_TYPE_INT);
689 g_value_set_int (&val, value);
691 ev_metadata_manager_set (uri, key, &val);
693 g_value_unset (&val);
697 ev_metadata_manager_set_double (const gchar *uri, const gchar *key, double value)
701 g_value_init (&val, G_TYPE_DOUBLE);
702 g_value_set_double (&val, value);
704 ev_metadata_manager_set (uri, key, &val);
706 g_value_unset (&val);
710 ev_metadata_manager_set_string (const gchar *uri, const gchar *key, const gchar *value)
714 g_value_init (&val, G_TYPE_STRING);
715 g_value_set_static_string (&val, value);
717 ev_metadata_manager_set (uri, key, &val);
719 g_value_unset (&val);
723 ev_metadata_manager_set_boolean (const gchar *uri, const gchar *key, gboolean value)
727 g_value_init (&val, G_TYPE_BOOLEAN);
728 g_value_set_boolean (&val, value);
730 ev_metadata_manager_set (uri, key, &val);
732 g_value_unset (&val);