Updated Spanish translation
[anjuta-git-plugin.git] / libegg / egg-recent-model.c
blob7a6e1ead8b7e6fb6df573c0c57a3db78f029f376
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of the
6 * License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 * Authors:
18 * James Willcox <jwillcox@cs.indiana.edu>
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/time.h>
32 #include <sys/stat.h>
33 #include <time.h>
34 #include <gtk/gtk.h>
35 #include <libgnomevfs/gnome-vfs.h>
36 #include <libgnomevfs/gnome-vfs-mime-utils.h>
37 #include <gconf/gconf-client.h>
38 #include "egg-recent-model.h"
39 #include "egg-recent-item.h"
41 #define EGG_RECENT_MODEL_FILE_PATH "/.recently-used"
42 #define EGG_RECENT_MODEL_BUFFER_SIZE 8192
44 #define EGG_RECENT_MODEL_MAX_ITEMS 500
45 #define EGG_RECENT_MODEL_DEFAULT_LIMIT 10
46 #define EGG_RECENT_MODEL_TIMEOUT_LENGTH 200
47 #define EGG_RECENT_MODEL_POLL_TIME 3
49 /* needed for Darwin */
50 #if !HAVE_DECL_LOCKF
51 int lockf (int filedes, int function, off_t size);
52 #endif
54 #define EGG_RECENT_MODEL_KEY_DIR "/desktop/gnome/recent_files"
55 #define EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY EGG_RECENT_MODEL_KEY_DIR "/default_limit"
56 #define EGG_RECENT_MODEL_EXPIRE_KEY EGG_RECENT_MODEL_KEY_DIR "/expire"
58 struct _EggRecentModelPrivate {
59 GSList *mime_filter_values; /* list of mime types we allow */
60 GSList *group_filter_values; /* list of groups we allow */
61 GSList *scheme_filter_values; /* list of URI schemes we allow */
63 EggRecentModelSort sort_type; /* type of sorting to be done */
65 int limit; /* soft limit for length of the list */
66 int expire_days; /* number of days to hold an item */
68 char *path; /* path to the file we store stuff in */
70 GHashTable *monitors;
72 GnomeVFSMonitorHandle *monitor;
74 GConfClient *client;
75 gboolean use_default_limit;
77 guint limit_change_notify_id;
78 guint expiration_change_notify_id;
80 guint changed_timeout;
81 guint poll_timeout;
82 time_t last_mtime;
85 /* signals */
86 enum {
87 CHANGED,
88 LAST_SIGNAL
91 static GType model_signals[LAST_SIGNAL] = { 0 };
93 /* properties */
94 enum {
95 PROP_BOGUS,
96 PROP_MIME_FILTERS,
97 PROP_GROUP_FILTERS,
98 PROP_SCHEME_FILTERS,
99 PROP_SORT_TYPE,
100 PROP_LIMIT
103 typedef struct {
104 GSList *states;
105 GList *items;
106 EggRecentItem *current_item;
107 } ParseInfo;
109 typedef enum {
110 STATE_START,
111 STATE_RECENT_FILES,
112 STATE_RECENT_ITEM,
113 STATE_URI,
114 STATE_MIME_TYPE,
115 STATE_TIMESTAMP,
116 STATE_PRIVATE,
117 STATE_GROUPS,
118 STATE_GROUP
119 } ParseState;
121 typedef struct {
122 EggRecentModel *model;
123 GList *list;
124 } ChangedData;
126 #define TAG_RECENT_FILES "RecentFiles"
127 #define TAG_RECENT_ITEM "RecentItem"
128 #define TAG_URI "URI"
129 #define TAG_MIME_TYPE "Mime-Type"
130 #define TAG_TIMESTAMP "Timestamp"
131 #define TAG_PRIVATE "Private"
132 #define TAG_GROUPS "Groups"
133 #define TAG_GROUP "Group"
135 static void start_element_handler (GMarkupParseContext *context,
136 const gchar *element_name,
137 const gchar **attribute_names,
138 const gchar **attribute_values,
139 gpointer user_data,
140 GError **error);
142 static void end_element_handler (GMarkupParseContext *context,
143 const gchar *element_name,
144 gpointer user_data,
145 GError **error);
147 static void text_handler (GMarkupParseContext *context,
148 const gchar *text,
149 gsize text_len,
150 gpointer user_data,
151 GError **error);
153 static void error_handler (GMarkupParseContext *context,
154 GError *error,
155 gpointer user_data);
157 static GMarkupParser parser = {start_element_handler, end_element_handler,
158 text_handler,
159 NULL,
160 error_handler};
162 static GObjectClass *parent_class;
164 static void egg_recent_model_clear_mime_filter (EggRecentModel *model);
165 static void egg_recent_model_clear_group_filter (EggRecentModel *model);
166 static void egg_recent_model_clear_scheme_filter (EggRecentModel *model);
168 static GObjectClass *parent_class;
170 static gboolean
171 egg_recent_model_string_match (const GSList *list, const gchar *str)
173 const GSList *tmp;
175 if (list == NULL || str == NULL)
176 return TRUE;
178 tmp = list;
180 while (tmp) {
181 if (g_pattern_match_string (tmp->data, str))
182 return TRUE;
184 tmp = tmp->next;
187 return FALSE;
190 static gboolean
191 egg_recent_model_write_raw (EggRecentModel *model, FILE *file,
192 const gchar *content)
194 int len;
195 int fd;
196 struct stat sbuf;
198 rewind (file);
200 len = strlen (content);
201 fd = fileno (file);
203 if (fstat (fd, &sbuf) < 0)
204 g_warning ("Couldn't stat XML document.");
206 if ((off_t)len < sbuf.st_size) {
207 ftruncate (fd, len);
210 if (fputs (content, file) == EOF)
211 return FALSE;
213 #ifndef G_OS_WIN32
214 fsync (fd);
215 #endif
216 rewind (file);
218 return TRUE;
221 static GList *
222 egg_recent_model_delete_from_list (GList *list,
223 const gchar *uri)
225 GList *tmp;
227 if (!uri)
228 return list;
230 tmp = list;
232 while (tmp) {
233 EggRecentItem *item = tmp->data;
234 GList *next;
236 next = tmp->next;
238 if (!strcmp (egg_recent_item_peek_uri (item), uri)) {
239 egg_recent_item_unref (item);
241 list = g_list_remove_link (list, tmp);
242 g_list_free_1 (tmp);
245 tmp = next;
248 return list;
251 static void
252 egg_recent_model_add_new_groups (EggRecentItem *item,
253 EggRecentItem *upd_item)
255 const GList *tmp;
257 tmp = egg_recent_item_get_groups (upd_item);
259 while (tmp) {
260 char *group = tmp->data;
262 if (!egg_recent_item_in_group (item, group))
263 egg_recent_item_add_group (item, group);
265 tmp = tmp->next;
269 static gboolean
270 egg_recent_model_update_item (GList *items, EggRecentItem *upd_item)
272 GList *tmp;
273 const char *uri;
275 uri = egg_recent_item_peek_uri (upd_item);
277 tmp = items;
279 while (tmp) {
280 EggRecentItem *item = tmp->data;
282 if (gnome_vfs_uris_match (egg_recent_item_peek_uri (item), uri)) {
283 egg_recent_item_set_timestamp (item, (time_t) -1);
285 egg_recent_model_add_new_groups (item, upd_item);
287 return TRUE;
290 tmp = tmp->next;
293 return FALSE;
296 static gchar *
297 egg_recent_model_read_raw (EggRecentModel *model, FILE *file)
299 GString *string;
300 char buf[EGG_RECENT_MODEL_BUFFER_SIZE];
302 rewind (file);
304 string = g_string_new (NULL);
305 while (fgets (buf, EGG_RECENT_MODEL_BUFFER_SIZE, file)) {
306 string = g_string_append (string, buf);
309 rewind (file);
311 return g_string_free (string, FALSE);
316 static ParseInfo *
317 parse_info_init (void)
319 ParseInfo *retval;
321 retval = g_new0 (ParseInfo, 1);
322 retval->states = g_slist_prepend (NULL, STATE_START);
323 retval->items = NULL;
325 return retval;
328 static void
329 parse_info_free (ParseInfo *info)
331 g_slist_free (info->states);
332 g_free (info);
335 static void
336 push_state (ParseInfo *info,
337 ParseState state)
339 info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
342 static void
343 pop_state (ParseInfo *info)
345 g_return_if_fail (info->states != NULL);
347 info->states = g_slist_remove (info->states, info->states->data);
350 static ParseState
351 peek_state (ParseInfo *info)
353 g_return_val_if_fail (info->states != NULL, STATE_START);
355 return GPOINTER_TO_INT (info->states->data);
358 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
360 static gboolean
361 valid_element (ParseInfo *info,
362 int valid_parent_state,
363 const gchar *element_name,
364 const gchar *valid_element,
365 GError **error)
367 if (peek_state (info) != valid_parent_state) {
368 g_set_error (error,
369 G_MARKUP_ERROR,
370 G_MARKUP_ERROR_INVALID_CONTENT,
371 "Unexpected tag '%s', tag '%s' expected",
372 element_name, valid_element);
373 return FALSE;
376 return TRUE;
379 static void
380 start_element_handler (GMarkupParseContext *context,
381 const gchar *element_name,
382 const gchar **attribute_names,
383 const gchar **attribute_values,
384 gpointer user_data,
385 GError **error)
387 ParseInfo *info = (ParseInfo *)user_data;
389 if (ELEMENT_IS (TAG_RECENT_FILES))
390 push_state (info, STATE_RECENT_FILES);
391 else if (ELEMENT_IS (TAG_RECENT_ITEM)) {
392 if (valid_element (info, STATE_RECENT_FILES,
393 TAG_RECENT_ITEM, TAG_RECENT_FILES, error)) {
394 info->current_item = egg_recent_item_new ();
395 push_state (info, STATE_RECENT_ITEM);
397 } else if (ELEMENT_IS (TAG_URI)) {
398 if (valid_element (info, STATE_RECENT_ITEM,
399 TAG_URI, TAG_RECENT_ITEM, error)) {
400 push_state (info, STATE_URI);
402 } else if (ELEMENT_IS (TAG_MIME_TYPE)) {
403 if (valid_element (info, STATE_RECENT_ITEM,
404 TAG_MIME_TYPE, TAG_RECENT_ITEM, error)) {
405 push_state (info, STATE_MIME_TYPE);
407 } else if (ELEMENT_IS (TAG_TIMESTAMP)) {
408 if (valid_element (info, STATE_RECENT_ITEM,
409 TAG_TIMESTAMP, TAG_RECENT_ITEM, error)) {
410 push_state (info, STATE_TIMESTAMP);
412 } else if (ELEMENT_IS (TAG_PRIVATE)) {
413 if (valid_element (info, STATE_RECENT_ITEM,
414 TAG_PRIVATE, TAG_RECENT_ITEM, error)) {
415 push_state (info, STATE_PRIVATE);
416 egg_recent_item_set_private (info->current_item, TRUE);
418 } else if (ELEMENT_IS (TAG_GROUPS)) {
419 if (valid_element (info, STATE_RECENT_ITEM,
420 TAG_GROUPS, TAG_RECENT_ITEM, error)) {
421 push_state (info, STATE_GROUPS);
423 } else if (ELEMENT_IS (TAG_GROUP)) {
424 if (valid_element (info, STATE_GROUPS,
425 TAG_GROUP, TAG_GROUPS, error)) {
426 push_state (info, STATE_GROUP);
431 static gint
432 list_compare_func_mru (gpointer a, gpointer b)
434 EggRecentItem *item_a = (EggRecentItem *)a;
435 EggRecentItem *item_b = (EggRecentItem *)b;
437 return item_a->timestamp < item_b->timestamp;
440 static gint
441 list_compare_func_lru (gpointer a, gpointer b)
443 EggRecentItem *item_a = (EggRecentItem *)a;
444 EggRecentItem *item_b = (EggRecentItem *)b;
446 return item_a->timestamp > item_b->timestamp;
451 static void
452 end_element_handler (GMarkupParseContext *context,
453 const gchar *element_name,
454 gpointer user_data,
455 GError **error)
457 ParseInfo *info = (ParseInfo *)user_data;
459 switch (peek_state (info)) {
460 case STATE_RECENT_ITEM:
461 if (!info->current_item) {
462 g_warning ("No recent item found\n");
463 break;
466 if (!info->current_item->uri) {
467 g_warning ("Invalid item found\n");
468 break;
471 info->items = g_list_prepend (info->items,
472 info->current_item);
473 info->current_item = NULL;
474 break;
475 default:
476 break;
479 pop_state (info);
482 static void
483 text_handler (GMarkupParseContext *context,
484 const gchar *text,
485 gsize text_len,
486 gpointer user_data,
487 GError **error)
489 ParseInfo *info = (ParseInfo *)user_data;
490 gchar *value;
492 value = g_strndup (text, text_len);
494 switch (peek_state (info)) {
495 case STATE_START:
496 case STATE_RECENT_FILES:
497 case STATE_RECENT_ITEM:
498 case STATE_PRIVATE:
499 case STATE_GROUPS:
500 break;
501 case STATE_URI:
502 egg_recent_item_set_uri (info->current_item, value);
503 break;
504 case STATE_MIME_TYPE:
505 egg_recent_item_set_mime_type (info->current_item, value);
506 break;
507 case STATE_TIMESTAMP:
508 egg_recent_item_set_timestamp (info->current_item,
509 (time_t)atoi (value));
510 break;
511 case STATE_GROUP:
512 egg_recent_item_add_group (info->current_item,
513 text);
514 break;
517 g_free (value);
520 static void
521 error_handler (GMarkupParseContext *context,
522 GError *error,
523 gpointer user_data)
525 g_warning ("Error in parse: %s", error->message);
528 static void
529 egg_recent_model_enforce_limit (GList *list, int limit)
531 int len;
532 GList *end;
534 /* limit < 0 means unlimited */
535 if (limit <= 0)
536 return;
538 len = g_list_length (list);
540 if (len > limit) {
541 GList *next;
543 end = g_list_nth (list, limit-1);
544 next = end->next;
546 end->next = NULL;
548 EGG_RECENT_ITEM_LIST_UNREF (next);
552 static GList *
553 egg_recent_model_sort (EggRecentModel *model, GList *list)
555 switch (model->priv->sort_type) {
556 case EGG_RECENT_MODEL_SORT_MRU:
557 list = g_list_sort (list,
558 (GCompareFunc)list_compare_func_mru);
559 break;
560 case EGG_RECENT_MODEL_SORT_LRU:
561 list = g_list_sort (list,
562 (GCompareFunc)list_compare_func_lru);
563 break;
564 case EGG_RECENT_MODEL_SORT_NONE:
565 break;
568 return list;
571 static gboolean
572 egg_recent_model_group_match (EggRecentItem *item, GSList *groups)
574 GSList *tmp;
576 tmp = groups;
578 while (tmp != NULL) {
579 const gchar * group = (const gchar *)tmp->data;
581 if (egg_recent_item_in_group (item, group))
582 return TRUE;
584 tmp = tmp->next;
587 return FALSE;
590 static GList *
591 egg_recent_model_filter (EggRecentModel *model, GList *list)
593 GList *newlist = NULL;
594 GList *l;
595 gchar *mime_type;
596 gchar *uri;
598 g_return_val_if_fail (list != NULL, NULL);
600 for (l = list; l != NULL ; l = l->next) {
601 EggRecentItem *item = (EggRecentItem *) l->data;
602 gboolean pass_mime_test = FALSE;
603 gboolean pass_group_test = FALSE;
604 gboolean pass_scheme_test = FALSE;
606 g_assert (item != NULL);
608 uri = egg_recent_item_get_uri (item);
610 /* filter by mime type */
611 if (model->priv->mime_filter_values != NULL) {
612 mime_type = egg_recent_item_get_mime_type (item);
614 if (egg_recent_model_string_match
615 (model->priv->mime_filter_values,
616 mime_type))
617 pass_mime_test = TRUE;
619 g_free (mime_type);
620 } else
621 pass_mime_test = TRUE;
623 /* filter by group */
624 if (pass_mime_test && model->priv->group_filter_values != NULL) {
625 if (egg_recent_model_group_match
626 (item, model->priv->group_filter_values))
627 pass_group_test = TRUE;
628 } else if (egg_recent_item_get_private (item)) {
629 pass_group_test = FALSE;
630 } else
631 pass_group_test = TRUE;
633 /* filter by URI scheme */
634 if (pass_mime_test && pass_group_test &&
635 model->priv->scheme_filter_values != NULL) {
636 gchar *scheme;
638 scheme = gnome_vfs_get_uri_scheme (uri);
640 if (egg_recent_model_string_match
641 (model->priv->scheme_filter_values, scheme))
642 pass_scheme_test = TRUE;
644 g_free (scheme);
645 } else
646 pass_scheme_test = TRUE;
648 if (pass_mime_test && pass_group_test && pass_scheme_test)
649 newlist = g_list_prepend (newlist, item);
650 else
651 egg_recent_item_unref (item);
653 g_free (uri);
656 g_list_free (list);
658 return g_list_reverse (newlist);
663 #if 0
664 static void
665 egg_recent_model_monitor_list_cb (GnomeVFSMonitorHandle *handle,
666 const gchar *monitor_uri,
667 const gchar *info_uri,
668 GnomeVFSMonitorEventType event_type,
669 gpointer user_data)
671 EggRecentModel *model;
673 model = EGG_RECENT_MODEL (user_data);
675 if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
676 egg_recent_model_delete (model, monitor_uri);
677 g_hash_table_remove (model->priv->monitors, monitor_uri);
683 static void
684 egg_recent_model_monitor_list (EggRecentModel *model, GList *list)
686 GList *tmp;
688 tmp = list;
689 while (tmp) {
690 EggRecentItem *item = (EggRecentItem *)tmp->data;
691 GnomeVFSMonitorHandle *handle;
692 GnomeVFSResult res;
693 gchar *uri;
695 tmp = tmp->next;
697 uri = egg_recent_item_get_uri (item);
698 if (g_hash_table_lookup (model->priv->monitors, uri)) {
699 /* already monitoring this one */
700 g_free (uri);
701 continue;
704 res = gnome_vfs_monitor_add (&handle, uri,
705 GNOME_VFS_MONITOR_FILE,
706 egg_recent_model_monitor_list_cb,
707 model);
709 if (res == GNOME_VFS_OK)
710 g_hash_table_insert (model->priv->monitors, uri, handle);
711 else
712 g_free (uri);
715 #endif
718 static gboolean
719 egg_recent_model_changed_timeout (EggRecentModel *model)
721 model->priv->changed_timeout = 0;
723 egg_recent_model_changed (model);
725 return FALSE;
728 static void
729 egg_recent_model_monitor_cb (GnomeVFSMonitorHandle *handle,
730 const gchar *monitor_uri,
731 const gchar *info_uri,
732 GnomeVFSMonitorEventType event_type,
733 gpointer user_data)
735 EggRecentModel *model;
737 g_return_if_fail (user_data != NULL);
738 g_return_if_fail (EGG_IS_RECENT_MODEL (user_data));
739 model = EGG_RECENT_MODEL (user_data);
741 if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ||
742 event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
743 event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
744 if (model->priv->changed_timeout > 0) {
745 g_source_remove (model->priv->changed_timeout);
748 model->priv->changed_timeout = g_timeout_add (
749 EGG_RECENT_MODEL_TIMEOUT_LENGTH,
750 (GSourceFunc)egg_recent_model_changed_timeout,
751 model);
755 static gboolean
756 egg_recent_model_poll_timeout (gpointer user_data)
758 EggRecentModel *model;
759 struct stat stat_buf;
760 int stat_res;
762 model = EGG_RECENT_MODEL (user_data);
763 stat_res = stat (model->priv->path, &stat_buf);
765 if (!stat_res && stat_buf.st_mtime &&
766 stat_buf.st_mtime != model->priv->last_mtime) {
767 model->priv->last_mtime = stat_buf.st_mtime;
769 if (model->priv->changed_timeout > 0)
770 g_source_remove (model->priv->changed_timeout);
772 model->priv->changed_timeout = g_timeout_add (
773 EGG_RECENT_MODEL_TIMEOUT_LENGTH,
774 (GSourceFunc)egg_recent_model_changed_timeout,
775 model);
777 return TRUE;
780 static void
781 egg_recent_model_monitor (EggRecentModel *model, gboolean should_monitor)
783 if (should_monitor && model->priv->monitor == NULL) {
784 char *uri;
785 GnomeVFSResult result;
787 uri = gnome_vfs_get_uri_from_local_path (model->priv->path);
789 result = gnome_vfs_monitor_add (&model->priv->monitor,
790 uri,
791 GNOME_VFS_MONITOR_FILE,
792 egg_recent_model_monitor_cb,
793 model);
795 g_free (uri);
797 /* if the above fails, don't worry about it.
798 * local notifications will still happen
800 if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) {
801 if (model->priv->poll_timeout > 0)
802 g_source_remove (model->priv->poll_timeout);
804 model->priv->poll_timeout = g_timeout_add (
805 EGG_RECENT_MODEL_POLL_TIME * 1000,
806 egg_recent_model_poll_timeout,
807 model);
810 } else if (!should_monitor && model->priv->monitor != NULL) {
811 gnome_vfs_monitor_cancel (model->priv->monitor);
812 model->priv->monitor = NULL;
816 static void
817 egg_recent_model_set_limit_internal (EggRecentModel *model, int limit)
819 model->priv->limit = limit;
821 if (limit <= 0)
822 egg_recent_model_monitor (model, FALSE);
823 else {
824 egg_recent_model_monitor (model, TRUE);
825 egg_recent_model_changed (model);
829 static GList *
830 egg_recent_model_read (EggRecentModel *model, FILE *file)
832 GList *list=NULL;
833 gchar *content;
834 GMarkupParseContext *ctx;
835 ParseInfo *info;
836 GError *error;
838 content = egg_recent_model_read_raw (model, file);
840 if (strlen (content) <= 0) {
841 g_free (content);
842 return NULL;
845 info = parse_info_init ();
847 ctx = g_markup_parse_context_new (&parser, 0, info, NULL);
849 error = NULL;
850 if (!g_markup_parse_context_parse (ctx, content, strlen (content), &error)) {
851 g_warning ("Error while parsing the .recently-used file: %s\n",
852 error->message);
854 g_error_free (error);
855 parse_info_free (info);
857 return NULL;
860 error = NULL;
861 if (!g_markup_parse_context_end_parse (ctx, &error)) {
862 g_warning ("Unable to complete parsing of the .recently-used file: %s\n",
863 error->message);
865 g_error_free (error);
866 g_markup_parse_context_free (ctx);
867 parse_info_free (info);
869 return NULL;
872 list = g_list_reverse (info->items);
874 g_markup_parse_context_free (ctx);
875 parse_info_free (info);
876 g_free (content);
878 return list;
882 static gboolean
883 egg_recent_model_write (EggRecentModel *model, FILE *file, GList *list)
885 GString *string;
886 gchar *data;
887 EggRecentItem *item;
888 const GList *groups;
889 int i;
890 int ret;
892 string = g_string_new ("<?xml version=\"1.0\"?>\n");
893 string = g_string_append (string, "<" TAG_RECENT_FILES ">\n");
895 i=0;
896 while (list) {
897 gchar *uri;
898 gchar *mime_type;
899 gchar *escaped_uri;
900 time_t timestamp;
901 item = (EggRecentItem *)list->data;
904 uri = egg_recent_item_get_uri_utf8 (item);
905 escaped_uri = g_markup_escape_text (uri,
906 strlen (uri));
907 g_free (uri);
909 mime_type = egg_recent_item_get_mime_type (item);
910 timestamp = egg_recent_item_get_timestamp (item);
912 string = g_string_append (string, " <" TAG_RECENT_ITEM ">\n");
914 g_string_append_printf (string,
915 " <" TAG_URI ">%s</" TAG_URI ">\n", escaped_uri);
917 if (mime_type)
918 g_string_append_printf (string,
919 " <" TAG_MIME_TYPE ">%s</" TAG_MIME_TYPE ">\n", mime_type);
920 else
921 g_string_append_printf (string,
922 " <" TAG_MIME_TYPE "></" TAG_MIME_TYPE ">\n");
925 g_string_append_printf (string,
926 " <" TAG_TIMESTAMP ">%d</" TAG_TIMESTAMP ">\n", (int)timestamp);
928 if (egg_recent_item_get_private (item))
929 string = g_string_append (string,
930 " <" TAG_PRIVATE "/>\n");
932 /* write the groups */
933 string = g_string_append (string,
934 " <" TAG_GROUPS ">\n");
935 groups = egg_recent_item_get_groups (item);
937 if (groups == NULL && egg_recent_item_get_private (item))
938 g_warning ("Item with URI \"%s\" marked as private, but"
939 " does not belong to any groups.\n", uri);
941 while (groups) {
942 const gchar *group = (const gchar *)groups->data;
943 gchar *escaped_group;
945 escaped_group = g_markup_escape_text (group, strlen(group));
947 g_string_append_printf (string,
948 " <" TAG_GROUP ">%s</" TAG_GROUP ">\n",
949 escaped_group);
951 g_free (escaped_group);
953 groups = groups->next;
956 string = g_string_append (string, " </" TAG_GROUPS ">\n");
958 string = g_string_append (string,
959 " </" TAG_RECENT_ITEM ">\n");
961 g_free (mime_type);
962 g_free (escaped_uri);
964 list = list->next;
965 i++;
968 string = g_string_append (string, "</" TAG_RECENT_FILES ">");
970 data = g_string_free (string, FALSE);
972 ret = egg_recent_model_write_raw (model, file, data);
974 g_free (data);
976 return ret;
979 static FILE *
980 egg_recent_model_open_file (EggRecentModel *model,
981 gboolean for_writing)
983 FILE *file;
984 mode_t prev_umask;
986 file = fopen (model->priv->path, "r+");
987 if (file == NULL && for_writing) {
988 /* be paranoid */
989 prev_umask = umask (077);
991 file = fopen (model->priv->path, "w+");
993 umask (prev_umask);
995 g_return_val_if_fail (file != NULL, NULL);
998 return file;
1001 static gboolean
1002 egg_recent_model_lock_file (FILE *file)
1004 #ifdef HAVE_LOCKF
1005 int fd;
1006 gint try = 5;
1008 rewind (file);
1009 fd = fileno (file);
1011 /* Attempt to lock the file 5 times,
1012 * waiting a random interval (< 1 second)
1013 * in between attempts.
1014 * We should really be doing asynchronous
1015 * locking, but requires substantially larger
1016 * changes.
1019 while (try > 0)
1021 int rand_interval;
1023 if (lockf (fd, F_TLOCK, 0) == 0)
1024 return TRUE;
1026 rand_interval = 1 + (int) (10.0 * rand()/(RAND_MAX + 1.0));
1028 g_usleep (100000 * rand_interval);
1030 --try;
1033 return FALSE;
1034 #else
1035 return TRUE;
1036 #endif /* HAVE_LOCKF */
1039 static gboolean
1040 egg_recent_model_unlock_file (FILE *file)
1042 #ifdef HAVE_LOCKF
1043 int fd;
1045 rewind (file);
1046 fd = fileno (file);
1048 return (lockf (fd, F_ULOCK, 0) == 0) ? TRUE : FALSE;
1049 #else
1050 return TRUE;
1051 #endif /* HAVE_LOCKF */
1054 static void
1055 egg_recent_model_finalize (GObject *object)
1057 EggRecentModel *model = EGG_RECENT_MODEL (object);
1059 if (model->priv->changed_timeout > 0) {
1060 g_source_remove (model->priv->changed_timeout);
1063 egg_recent_model_monitor (model, FALSE);
1066 g_slist_foreach (model->priv->mime_filter_values,
1067 (GFunc) g_pattern_spec_free, NULL);
1068 g_slist_free (model->priv->mime_filter_values);
1069 model->priv->mime_filter_values = NULL;
1071 g_slist_foreach (model->priv->scheme_filter_values,
1072 (GFunc) g_pattern_spec_free, NULL);
1073 g_slist_free (model->priv->scheme_filter_values);
1074 model->priv->scheme_filter_values = NULL;
1076 g_slist_foreach (model->priv->group_filter_values,
1077 (GFunc) g_free, NULL);
1078 g_slist_free (model->priv->group_filter_values);
1079 model->priv->group_filter_values = NULL;
1082 if (model->priv->limit_change_notify_id)
1083 gconf_client_notify_remove (model->priv->client,
1084 model->priv->limit_change_notify_id);
1085 model->priv->expiration_change_notify_id = 0;
1087 if (model->priv->expiration_change_notify_id)
1088 gconf_client_notify_remove (model->priv->client,
1089 model->priv->expiration_change_notify_id);
1090 model->priv->expiration_change_notify_id = 0;
1092 g_object_unref (model->priv->client);
1093 model->priv->client = NULL;
1096 g_free (model->priv->path);
1097 model->priv->path = NULL;
1099 g_hash_table_destroy (model->priv->monitors);
1100 model->priv->monitors = NULL;
1102 if (model->priv->poll_timeout > 0)
1103 g_source_remove (model->priv->poll_timeout);
1104 model->priv->poll_timeout =0;
1106 g_free (model->priv);
1108 parent_class->finalize (object);
1111 static void
1112 egg_recent_model_set_property (GObject *object,
1113 guint prop_id,
1114 const GValue *value,
1115 GParamSpec *pspec)
1117 EggRecentModel *model = EGG_RECENT_MODEL (object);
1119 switch (prop_id)
1121 case PROP_MIME_FILTERS:
1122 if (model->priv->mime_filter_values != NULL)
1123 egg_recent_model_clear_mime_filter (model);
1125 model->priv->mime_filter_values =
1126 (GSList *)g_value_get_pointer (value);
1127 break;
1129 case PROP_GROUP_FILTERS:
1130 if (model->priv->group_filter_values != NULL)
1131 egg_recent_model_clear_group_filter (model);
1133 model->priv->group_filter_values =
1134 (GSList *)g_value_get_pointer (value);
1135 break;
1137 case PROP_SCHEME_FILTERS:
1138 if (model->priv->scheme_filter_values != NULL)
1139 egg_recent_model_clear_scheme_filter (model);
1141 model->priv->scheme_filter_values =
1142 (GSList *)g_value_get_pointer (value);
1143 break;
1145 case PROP_SORT_TYPE:
1146 model->priv->sort_type = g_value_get_int (value);
1147 break;
1149 case PROP_LIMIT:
1150 egg_recent_model_set_limit (model,
1151 g_value_get_int (value));
1152 break;
1154 default:
1155 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1156 break;
1160 static void
1161 egg_recent_model_get_property (GObject *object,
1162 guint prop_id,
1163 GValue *value,
1164 GParamSpec *pspec)
1166 EggRecentModel *model = EGG_RECENT_MODEL (object);
1168 switch (prop_id)
1170 case PROP_MIME_FILTERS:
1171 g_value_set_pointer (value, model->priv->mime_filter_values);
1172 break;
1174 case PROP_GROUP_FILTERS:
1175 g_value_set_pointer (value, model->priv->group_filter_values);
1176 break;
1178 case PROP_SCHEME_FILTERS:
1179 g_value_set_pointer (value, model->priv->scheme_filter_values);
1180 break;
1182 case PROP_SORT_TYPE:
1183 g_value_set_int (value, model->priv->sort_type);
1184 break;
1186 case PROP_LIMIT:
1187 g_value_set_int (value, model->priv->limit);
1188 break;
1190 default:
1191 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1192 break;
1196 static void
1197 egg_recent_model_class_init (EggRecentModelClass * klass)
1199 GObjectClass *object_class;
1201 parent_class = g_type_class_peek_parent (klass);
1203 parent_class = g_type_class_peek_parent (klass);
1205 object_class = G_OBJECT_CLASS (klass);
1206 object_class->set_property = egg_recent_model_set_property;
1207 object_class->get_property = egg_recent_model_get_property;
1208 object_class->finalize = egg_recent_model_finalize;
1210 model_signals[CHANGED] = g_signal_new ("changed",
1211 G_OBJECT_CLASS_TYPE (object_class),
1212 G_SIGNAL_RUN_LAST,
1213 G_STRUCT_OFFSET (EggRecentModelClass, changed),
1214 NULL, NULL,
1215 g_cclosure_marshal_VOID__POINTER,
1216 G_TYPE_NONE, 1,
1217 G_TYPE_POINTER);
1220 g_object_class_install_property (object_class,
1221 PROP_MIME_FILTERS,
1222 g_param_spec_pointer ("mime-filters",
1223 "Mime Filters",
1224 "List of mime types to be allowed.",
1225 G_PARAM_READWRITE));
1227 g_object_class_install_property (object_class,
1228 PROP_GROUP_FILTERS,
1229 g_param_spec_pointer ("group-filters",
1230 "Group Filters",
1231 "List of groups to be allowed.",
1232 G_PARAM_READWRITE));
1234 g_object_class_install_property (object_class,
1235 PROP_SCHEME_FILTERS,
1236 g_param_spec_pointer ("scheme-filters",
1237 "Scheme Filters",
1238 "List of URI schemes to be allowed.",
1239 G_PARAM_READWRITE));
1241 g_object_class_install_property (object_class,
1242 PROP_SORT_TYPE,
1243 g_param_spec_int ("sort-type",
1244 "Sort Type",
1245 "Type of sorting to be done.",
1246 0, EGG_RECENT_MODEL_SORT_NONE,
1247 EGG_RECENT_MODEL_SORT_MRU,
1248 G_PARAM_READWRITE));
1250 g_object_class_install_property (object_class,
1251 PROP_LIMIT,
1252 g_param_spec_int ("limit",
1253 "Limit",
1254 "Max number of items allowed.",
1255 -1, EGG_RECENT_MODEL_MAX_ITEMS,
1256 EGG_RECENT_MODEL_DEFAULT_LIMIT,
1257 G_PARAM_READWRITE));
1259 klass->changed = NULL;
1264 static void
1265 egg_recent_model_limit_changed (GConfClient *client, guint cnxn_id,
1266 GConfEntry *entry, gpointer user_data)
1268 EggRecentModel *model;
1269 GConfValue *value;
1271 model = EGG_RECENT_MODEL (user_data);
1273 g_return_if_fail (model != NULL);
1275 if (model->priv->use_default_limit == FALSE)
1276 return; /* ignore this key */
1278 /* the key was unset, and the schema has apparently failed */
1279 if (entry == NULL)
1280 return;
1282 value = gconf_entry_get_value (entry);
1284 if (value->type != GCONF_VALUE_INT) {
1285 g_warning ("Expected GConfValue of type integer, "
1286 "got something else");
1290 egg_recent_model_set_limit_internal (model, gconf_value_get_int (value));
1293 static void
1294 egg_recent_model_expiration_changed (GConfClient *client, guint cnxn_id,
1295 GConfEntry *entry, gpointer user_data)
1300 static void
1301 egg_recent_model_init (EggRecentModel * model)
1303 if (!gnome_vfs_init ()) {
1304 g_warning ("gnome-vfs initialization failed.");
1305 return;
1309 model->priv = g_new0 (EggRecentModelPrivate, 1);
1311 model->priv->path = g_strdup_printf ("%s" EGG_RECENT_MODEL_FILE_PATH,
1312 g_get_home_dir ());
1314 model->priv->mime_filter_values = NULL;
1315 model->priv->group_filter_values = NULL;
1316 model->priv->scheme_filter_values = NULL;
1318 model->priv->client = gconf_client_get_default ();
1319 gconf_client_add_dir (model->priv->client, EGG_RECENT_MODEL_KEY_DIR,
1320 GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
1322 model->priv->limit_change_notify_id =
1323 gconf_client_notify_add (model->priv->client,
1324 EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY,
1325 egg_recent_model_limit_changed,
1326 model, NULL, NULL);
1328 model->priv->expiration_change_notify_id =
1329 gconf_client_notify_add (model->priv->client,
1330 EGG_RECENT_MODEL_EXPIRE_KEY,
1331 egg_recent_model_expiration_changed,
1332 model, NULL, NULL);
1334 model->priv->expire_days = gconf_client_get_int (
1335 model->priv->client,
1336 EGG_RECENT_MODEL_EXPIRE_KEY,
1337 NULL);
1339 #if 0
1340 /* keep this out, for now */
1341 model->priv->limit = gconf_client_get_int (
1342 model->priv->client,
1343 EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY, NULL);
1344 model->priv->use_default_limit = TRUE;
1345 #endif
1346 model->priv->limit = EGG_RECENT_MODEL_DEFAULT_LIMIT;
1347 model->priv->use_default_limit = FALSE;
1349 model->priv->monitors = g_hash_table_new_full (
1350 g_str_hash, g_str_equal,
1351 (GDestroyNotify) g_free,
1352 (GDestroyNotify) gnome_vfs_monitor_cancel);
1354 model->priv->monitor = NULL;
1355 model->priv->poll_timeout = 0;
1356 model->priv->last_mtime = 0;
1357 egg_recent_model_monitor (model, TRUE);
1362 * egg_recent_model_new:
1363 * @sort: the type of sorting to use
1364 * @limit: maximum number of items in the list
1366 * This creates a new EggRecentModel object.
1368 * Returns: a EggRecentModel object
1370 EggRecentModel *
1371 egg_recent_model_new (EggRecentModelSort sort)
1373 EggRecentModel *model;
1375 model = EGG_RECENT_MODEL (g_object_new (egg_recent_model_get_type (),
1376 "sort-type", sort, NULL));
1378 g_return_val_if_fail (model, NULL);
1380 return model;
1384 * egg_recent_model_add_full:
1385 * @model: A EggRecentModel object.
1386 * @item: A EggRecentItem
1388 * This function adds an item to the list of recently used URIs.
1390 * Returns: gboolean
1392 gboolean
1393 egg_recent_model_add_full (EggRecentModel * model, EggRecentItem *item)
1395 FILE *file;
1396 GList *list = NULL;
1397 gboolean ret = FALSE;
1398 gboolean updated = FALSE;
1399 char *uri;
1400 time_t t;
1402 g_return_val_if_fail (model != NULL, FALSE);
1403 g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE);
1405 uri = egg_recent_item_get_uri (item);
1406 if (strncmp (uri, "recent-files://", strlen ("recent-files://")) == 0) {
1407 g_free (uri);
1408 return FALSE;
1409 } else {
1410 g_free (uri);
1413 file = egg_recent_model_open_file (model, TRUE);
1414 g_return_val_if_fail (file != NULL, FALSE);
1416 time (&t);
1417 egg_recent_item_set_timestamp (item, t);
1419 if (egg_recent_model_lock_file (file)) {
1421 /* read existing stuff */
1422 list = egg_recent_model_read (model, file);
1424 /* if it's already there, we just update it */
1425 updated = egg_recent_model_update_item (list, item);
1427 if (!updated) {
1428 list = g_list_prepend (list, item);
1430 egg_recent_model_enforce_limit (list,
1431 EGG_RECENT_MODEL_MAX_ITEMS);
1434 /* write new stuff */
1435 if (!egg_recent_model_write (model, file, list))
1436 g_warning ("Write failed: %s", strerror (errno));
1438 if (!updated)
1439 list = g_list_remove (list, item);
1441 EGG_RECENT_ITEM_LIST_UNREF (list);
1442 ret = TRUE;
1443 } else {
1444 g_warning ("Failed to lock: %s", strerror (errno));
1445 fclose (file);
1446 return FALSE;
1449 if (!egg_recent_model_unlock_file (file))
1450 g_warning ("Failed to unlock: %s", strerror (errno));
1452 fclose (file);
1454 if (model->priv->monitor == NULL) {
1455 /* since monitoring isn't working, at least give a
1456 * local notification
1458 egg_recent_model_changed (model);
1461 return ret;
1465 * egg_recent_model_add:
1466 * @model: A EggRecentModel object.
1467 * @uri: A string URI
1469 * This function adds an item to the list of recently used URIs.
1471 * Returns: gboolean
1473 gboolean
1474 egg_recent_model_add (EggRecentModel *model, const gchar *uri)
1476 EggRecentItem *item;
1477 gboolean ret = FALSE;
1479 g_return_val_if_fail (model != NULL, FALSE);
1480 g_return_val_if_fail (uri != NULL, FALSE);
1482 item = egg_recent_item_new_from_uri (uri);
1484 g_return_val_if_fail (item != NULL, FALSE);
1486 ret = egg_recent_model_add_full (model, item);
1488 egg_recent_item_unref (item);
1490 return ret;
1496 * egg_recent_model_delete:
1497 * @model: A EggRecentModel object.
1498 * @uri: The URI you want to delete.
1500 * This function deletes a URI from the file of recently used URIs.
1502 * Returns: gboolean
1504 gboolean
1505 egg_recent_model_delete (EggRecentModel * model, const gchar * uri)
1507 FILE *file;
1508 GList *list;
1509 unsigned int length;
1510 gboolean ret = FALSE;
1512 g_return_val_if_fail (model != NULL, FALSE);
1513 g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE);
1514 g_return_val_if_fail (uri != NULL, FALSE);
1516 file = egg_recent_model_open_file (model, TRUE);
1517 g_return_val_if_fail (file != NULL, FALSE);
1519 if (egg_recent_model_lock_file (file)) {
1520 list = egg_recent_model_read (model, file);
1522 if (list == NULL)
1523 goto out;
1525 length = g_list_length (list);
1527 list = egg_recent_model_delete_from_list (list, uri);
1529 if (length == g_list_length (list)) {
1530 /* nothing was deleted */
1531 EGG_RECENT_ITEM_LIST_UNREF (list);
1532 } else {
1533 egg_recent_model_write (model, file, list);
1534 EGG_RECENT_ITEM_LIST_UNREF (list);
1535 ret = TRUE;
1538 } else {
1539 g_warning ("Failed to lock: %s", strerror (errno));
1540 return FALSE;
1543 out:
1545 if (!egg_recent_model_unlock_file (file))
1546 g_warning ("Failed to unlock: %s", strerror (errno));
1548 fclose (file);
1550 g_hash_table_remove (model->priv->monitors, uri);
1552 if (model->priv->monitor == NULL && ret) {
1553 /* since monitoring isn't working, at least give a
1554 * local notification
1556 egg_recent_model_changed (model);
1559 return ret;
1564 * egg_recent_model_get_list:
1565 * @model: A EggRecentModel object.
1567 * This function gets the current contents of the file
1569 * Returns: a GList
1571 GList *
1572 egg_recent_model_get_list (EggRecentModel *model)
1574 FILE *file;
1575 GList *list = NULL;
1577 file = egg_recent_model_open_file (model, FALSE);
1578 if (file == NULL)
1579 return NULL;
1581 if (egg_recent_model_lock_file (file))
1582 list = egg_recent_model_read (model, file);
1583 else {
1584 g_warning ("Failed to lock: %s", strerror (errno));
1585 fclose (file);
1586 return NULL;
1589 if (!egg_recent_model_unlock_file (file))
1590 g_warning ("Failed to unlock: %s", strerror (errno));
1592 if (list != NULL) {
1593 list = egg_recent_model_filter (model, list);
1594 list = egg_recent_model_sort (model, list);
1596 egg_recent_model_enforce_limit (list, model->priv->limit);
1599 fclose (file);
1601 return list;
1607 * egg_recent_model_set_limit:
1608 * @model: A EggRecentModel object.
1609 * @limit: The maximum length of the list
1611 * This function sets the maximum length of the list. Note: This only affects
1612 * the length of the list emitted in the "changed" signal, not the list stored
1613 * on disk.
1615 * Returns: void
1617 void
1618 egg_recent_model_set_limit (EggRecentModel *model, int limit)
1620 model->priv->use_default_limit = FALSE;
1622 egg_recent_model_set_limit_internal (model, limit);
1626 * egg_recent_model_get_limit:
1627 * @model: A EggRecentModel object.
1629 * This function gets the maximum length of the list.
1631 * Returns: int
1634 egg_recent_model_get_limit (EggRecentModel *model)
1636 return model->priv->limit;
1641 * egg_recent_model_clear:
1642 * @model: A EggRecentModel object.
1644 * This function clears the contents of the file
1646 * Returns: void
1648 void
1649 egg_recent_model_clear (EggRecentModel *model)
1651 FILE *file;
1652 int fd;
1654 file = egg_recent_model_open_file (model, TRUE);
1655 g_return_if_fail (file != NULL);
1657 fd = fileno (file);
1659 if (egg_recent_model_lock_file (file)) {
1660 ftruncate (fd, 0);
1661 } else {
1662 g_warning ("Failed to lock: %s", strerror (errno));
1663 return;
1666 if (!egg_recent_model_unlock_file (file))
1667 g_warning ("Failed to unlock: %s", strerror (errno));
1669 fclose (file);
1671 if (model->priv->monitor == NULL) {
1672 /* since monitoring isn't working, at least give a
1673 * local notification
1675 egg_recent_model_changed (model);
1679 static void
1680 egg_recent_model_clear_mime_filter (EggRecentModel *model)
1682 g_return_if_fail (model != NULL);
1684 if (model->priv->mime_filter_values != NULL) {
1685 g_slist_foreach (model->priv->mime_filter_values,
1686 (GFunc) g_pattern_spec_free, NULL);
1687 g_slist_free (model->priv->mime_filter_values);
1688 model->priv->mime_filter_values = NULL;
1693 * egg_recent_model_set_filter_mime_types:
1694 * @model: A EggRecentModel object.
1696 * Sets which mime types are allowed in the list.
1698 * Returns: void
1700 void
1701 egg_recent_model_set_filter_mime_types (EggRecentModel *model,
1702 ...)
1704 va_list valist;
1705 GSList *list = NULL;
1706 gchar *str;
1708 g_return_if_fail (model != NULL);
1710 egg_recent_model_clear_mime_filter (model);
1712 va_start (valist, model);
1714 str = va_arg (valist, gchar*);
1716 while (str != NULL) {
1717 list = g_slist_prepend (list, g_pattern_spec_new (str));
1719 str = va_arg (valist, gchar*);
1722 va_end (valist);
1724 model->priv->mime_filter_values = list;
1727 static void
1728 egg_recent_model_clear_group_filter (EggRecentModel *model)
1730 g_return_if_fail (model != NULL);
1732 if (model->priv->group_filter_values != NULL) {
1733 g_slist_foreach (model->priv->group_filter_values, (GFunc)g_free, NULL);
1734 g_slist_free (model->priv->group_filter_values);
1735 model->priv->group_filter_values = NULL;
1740 * egg_recent_model_set_filter_groups:
1741 * @model: A EggRecentModel object.
1743 * Sets which groups are allowed in the list.
1745 * Returns: void
1747 void
1748 egg_recent_model_set_filter_groups (EggRecentModel *model,
1749 ...)
1751 va_list valist;
1752 GSList *list = NULL;
1753 gchar *str;
1755 g_return_if_fail (model != NULL);
1757 egg_recent_model_clear_group_filter (model);
1759 va_start (valist, model);
1761 str = va_arg (valist, gchar*);
1763 while (str != NULL) {
1764 list = g_slist_prepend (list, g_strdup (str));
1766 str = va_arg (valist, gchar*);
1769 va_end (valist);
1771 model->priv->group_filter_values = list;
1774 static void
1775 egg_recent_model_clear_scheme_filter (EggRecentModel *model)
1777 g_return_if_fail (model != NULL);
1779 if (model->priv->scheme_filter_values != NULL) {
1780 g_slist_foreach (model->priv->scheme_filter_values,
1781 (GFunc) g_pattern_spec_free, NULL);
1782 g_slist_free (model->priv->scheme_filter_values);
1783 model->priv->scheme_filter_values = NULL;
1788 * egg_recent_model_set_filter_uri_schemes:
1789 * @model: A EggRecentModel object.
1791 * Sets which URI schemes (file, http, ftp, etc) are allowed in the list.
1793 * Returns: void
1795 void
1796 egg_recent_model_set_filter_uri_schemes (EggRecentModel *model, ...)
1798 va_list valist;
1799 GSList *list = NULL;
1800 gchar *str;
1802 g_return_if_fail (model != NULL);
1804 egg_recent_model_clear_scheme_filter (model);
1806 va_start (valist, model);
1808 str = va_arg (valist, gchar*);
1810 while (str != NULL) {
1811 list = g_slist_prepend (list, g_pattern_spec_new (str));
1813 str = va_arg (valist, gchar*);
1816 va_end (valist);
1818 model->priv->scheme_filter_values = list;
1822 * egg_recent_model_set_sort:
1823 * @model: A EggRecentModel object.
1824 * @sort: A EggRecentModelSort type
1826 * Sets the type of sorting to be used.
1828 * Returns: void
1830 void
1831 egg_recent_model_set_sort (EggRecentModel *model,
1832 EggRecentModelSort sort)
1834 g_return_if_fail (model != NULL);
1836 model->priv->sort_type = sort;
1840 * egg_recent_model_changed:
1841 * @model: A EggRecentModel object.
1843 * This function causes a "changed" signal to be emitted.
1845 * Returns: void
1847 void
1848 egg_recent_model_changed (EggRecentModel *model)
1850 GList *list = NULL;
1852 if (model->priv->limit > 0) {
1853 list = egg_recent_model_get_list (model);
1854 /* egg_recent_model_monitor_list (model, list); */
1856 g_signal_emit (G_OBJECT (model), model_signals[CHANGED], 0,
1857 list);
1860 if (list)
1861 EGG_RECENT_ITEM_LIST_UNREF (list);
1864 static void
1865 egg_recent_model_remove_expired_list (EggRecentModel *model, GList *list)
1867 time_t current_time;
1868 time_t day_seconds;
1870 time (&current_time);
1871 day_seconds = model->priv->expire_days*24*60*60;
1873 while (list != NULL) {
1874 EggRecentItem *item = list->data;
1875 time_t timestamp;
1877 timestamp = egg_recent_item_get_timestamp (item);
1879 if ((timestamp+day_seconds) < current_time) {
1880 gchar *uri = egg_recent_item_get_uri (item);
1881 egg_recent_model_delete (model, uri);
1883 g_strdup (uri);
1886 list = list->next;
1892 * egg_recent_model_remove_expired:
1893 * @model: A EggRecentModel object.
1895 * Goes through the entire list, and removes any items that are older than
1896 * the user-specified expiration period.
1898 * Returns: void
1900 void
1901 egg_recent_model_remove_expired (EggRecentModel *model)
1903 FILE *file;
1904 GList *list=NULL;
1906 g_return_if_fail (model != NULL);
1908 file = egg_recent_model_open_file (model, FALSE);
1909 if (file == NULL)
1910 return;
1912 if (egg_recent_model_lock_file (file)) {
1913 list = egg_recent_model_read (model, file);
1915 } else {
1916 g_warning ("Failed to lock: %s", strerror (errno));
1917 return;
1920 if (!egg_recent_model_unlock_file (file))
1921 g_warning ("Failed to unlock: %s", strerror (errno));
1923 if (list != NULL) {
1924 egg_recent_model_remove_expired_list (model, list);
1925 EGG_RECENT_ITEM_LIST_UNREF (list);
1928 fclose (file);
1932 * egg_recent_model_get_type:
1934 * This returns a GType representing a EggRecentModel object.
1936 * Returns: a GType
1938 GType
1939 egg_recent_model_get_type (void)
1941 static GType egg_recent_model_type = 0;
1943 if(!egg_recent_model_type) {
1944 static const GTypeInfo egg_recent_model_info = {
1945 sizeof (EggRecentModelClass),
1946 NULL, /* base init */
1947 NULL, /* base finalize */
1948 (GClassInitFunc)egg_recent_model_class_init, /* class init */
1949 NULL, /* class finalize */
1950 NULL, /* class data */
1951 sizeof (EggRecentModel),
1953 (GInstanceInitFunc) egg_recent_model_init
1956 egg_recent_model_type = g_type_register_static (G_TYPE_OBJECT,
1957 "EggRecentModel",
1958 &egg_recent_model_info, 0);
1961 return egg_recent_model_type;