1 diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.c ./libgnome-desktop/egg-recent-item.c
2 --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.c 1969-12-31 19:00:00.000000000 -0500
3 +++ ./libgnome-desktop/egg-recent-item.c 2006-05-08 16:18:44.000000000 -0400
5 +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
7 + * This program is free software; you can redistribute it and/or modify
8 + * it under the terms of the GNU General Public License as
9 + * published by the Free Software Foundation; either version 2 of the
10 + * License, or (at your option) any later version.
12 + * This program is distributed in the hope that it will be useful,
13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 + * GNU General Public License for more details.
17 + * You should have received a copy of the GNU General Public License
18 + * along with this program; if not, write to the Free Software
19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22 + * James Willcox <jwillcox@cs.indiana.edu>
29 +#include <libgnomevfs/gnome-vfs.h>
30 +#include <libgnomevfs/gnome-vfs-mime-utils.h>
31 +#include "egg-recent-item.h"
36 +egg_recent_item_new (void)
38 + EggRecentItem *item;
40 + item = g_new (EggRecentItem, 1);
42 + item->groups = NULL;
43 + item->private_data = FALSE;
45 + item->mime_type = NULL;
46 + item->mime_type_is_explicit = FALSE;
54 +egg_recent_item_free (EggRecentItem *item)
59 + if (item->mime_type)
60 + g_free (item->mime_type);
63 + g_list_foreach (item->groups, (GFunc)g_free, NULL);
64 + g_list_free (item->groups);
65 + item->groups = NULL;
72 +egg_recent_item_ref (EggRecentItem *item)
79 +egg_recent_item_unref (EggRecentItem *item)
83 + if (item->refcount == 0) {
84 + egg_recent_item_free (item);
92 +egg_recent_item_new_from_uri (const gchar *uri)
94 + EggRecentItem *item;
96 + g_return_val_if_fail (uri != NULL, NULL);
98 + item = egg_recent_item_new ();
100 + if (!egg_recent_item_set_uri (item ,uri)) {
101 + egg_recent_item_free (item);
109 +egg_recent_item_update_mime_type (EggRecentItem *item)
111 + if (!item->mime_type_is_explicit) {
112 + g_free (item->mime_type);
113 + item->mime_type = NULL;
116 + item->mime_type = gnome_vfs_get_mime_type (item->uri);
118 + if (!item->mime_type)
119 + item->mime_type = g_strdup (GNOME_VFS_MIME_TYPE_UNKNOWN);
124 +egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri)
128 + /* if G_BROKEN_FILENAMES is not set, this should succede */
129 + if (g_utf8_validate (uri, -1, NULL)) {
130 + item->uri = gnome_vfs_make_uri_from_input (uri);
132 + utf8_uri = g_filename_to_utf8 (uri, -1, NULL, NULL, NULL);
134 + if (utf8_uri == NULL) {
135 + g_warning ("Couldn't convert URI to UTF-8");
139 + if (g_utf8_validate (utf8_uri, -1, NULL)) {
140 + item->uri = gnome_vfs_make_uri_from_input (utf8_uri);
153 +egg_recent_item_get_uri (const EggRecentItem *item)
155 + return g_strdup (item->uri);
158 +G_CONST_RETURN gchar *
159 +egg_recent_item_peek_uri (const EggRecentItem *item)
165 +egg_recent_item_get_uri_utf8 (const EggRecentItem *item)
167 + /* this could fail, but it's not likely, since we've already done it
168 + * once in set_uri()
170 + return g_filename_to_utf8 (item->uri, -1, NULL, NULL, NULL);
174 +egg_recent_item_get_uri_for_display (const EggRecentItem *item)
176 + return gnome_vfs_format_uri_for_display (item->uri);
179 +/* Stolen from gnome_vfs_make_valid_utf8() */
181 +make_valid_utf8 (const char *name)
184 + const char *remainder, *invalid;
185 + int remaining_bytes, valid_bytes;
189 + remaining_bytes = name ? strlen (name) : 0;
191 + while (remaining_bytes != 0) {
192 + if (g_utf8_validate (remainder, remaining_bytes, &invalid))
195 + valid_bytes = invalid - remainder;
197 + if (string == NULL)
198 + string = g_string_sized_new (remaining_bytes);
200 + g_string_append_len (string, remainder, valid_bytes);
201 + g_string_append_c (string, '?');
203 + remaining_bytes -= valid_bytes + 1;
204 + remainder = invalid + 1;
207 + if (string == NULL)
208 + return g_strdup (name);
210 + g_string_append (string, remainder);
211 +/* g_string_append (string, _(" (invalid file name)")); */
212 + g_assert (g_utf8_validate (string->str, -1, NULL));
214 + return g_string_free (string, FALSE);
218 +get_uri_shortname_for_display (GnomeVFSURI *uri)
221 + gboolean validated;
224 + name = gnome_vfs_uri_extract_short_name (uri);
228 + name = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD);
230 + else if (g_ascii_strcasecmp (uri->method_string, "file") == 0)
234 + text_uri = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD);
235 + local_file = gnome_vfs_get_local_path_from_uri (text_uri);
237 + if (local_file != NULL)
240 + name = g_filename_display_basename (local_file);
244 + g_free (local_file);
247 + else if (!gnome_vfs_uri_has_parent (uri))
249 + const gchar *method;
251 + method = uri->method_string;
253 + if (name == NULL ||
254 + strcmp (name, GNOME_VFS_URI_PATH_STR) == 0)
257 + name = g_strdup (method);
264 + name = g_strdup_printf ("%s: %s", method, name);
269 + if (!validated && !g_utf8_validate (name, -1, NULL))
273 + utf8_name = make_valid_utf8 (name);
282 + * egg_recent_item_get_short_name:
283 + * @item: an #EggRecentItem
285 + * Computes a valid UTF-8 string that can be used as the name of the item in a
286 + * menu or list. For example, calling this function on an item that refers to
287 + * "file:///foo/bar.txt" will yield "bar.txt".
289 + * Return value: A newly-allocated string in UTF-8 encoding; free it with
293 +egg_recent_item_get_short_name (const EggRecentItem *item)
298 + g_return_val_if_fail (item != NULL, NULL);
300 + if (item->uri == NULL)
303 + uri = gnome_vfs_uri_new (item->uri);
307 + short_name = get_uri_shortname_for_display (uri);
309 + gnome_vfs_uri_unref (uri);
315 +egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime)
317 + g_free (item->mime_type);
318 + item->mime_type = NULL;
320 + if (mime && mime[0]) {
321 + item->mime_type_is_explicit = TRUE;
322 + item->mime_type = g_strdup (mime);
324 + item->mime_type_is_explicit = FALSE;
329 +egg_recent_item_get_mime_type (EggRecentItem *item)
331 + egg_recent_item_update_mime_type (item);
333 + return g_strdup (item->mime_type);
337 +egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp)
339 + if (timestamp == (time_t) -1)
342 + item->timestamp = timestamp;
346 +egg_recent_item_get_timestamp (const EggRecentItem *item)
348 + return item->timestamp;
351 +G_CONST_RETURN GList *
352 +egg_recent_item_get_groups (const EggRecentItem *item)
354 + return item->groups;
358 +egg_recent_item_in_group (const EggRecentItem *item, const gchar *group_name)
362 + tmp = item->groups;
363 + while (tmp != NULL) {
364 + gchar *val = (gchar *)tmp->data;
366 + if (strcmp (group_name, val) == 0)
376 +egg_recent_item_add_group (EggRecentItem *item, const gchar *group_name)
378 + g_return_if_fail (group_name != NULL);
380 + if (!egg_recent_item_in_group (item, group_name))
381 + item->groups = g_list_append (item->groups, g_strdup (group_name));
385 +egg_recent_item_remove_group (EggRecentItem *item, const gchar *group_name)
389 + g_return_if_fail (group_name != NULL);
391 + tmp = item->groups;
392 + while (tmp != NULL) {
393 + gchar *val = (gchar *)tmp->data;
395 + if (strcmp (group_name, val) == 0) {
396 + item->groups = g_list_remove (item->groups,
407 +egg_recent_item_set_private (EggRecentItem *item, gboolean priv)
409 + item->private_data = priv;
413 +egg_recent_item_get_private (const EggRecentItem *item)
415 + return item->private_data;
419 +egg_recent_item_get_type (void)
421 + static GType boxed_type = 0;
424 + boxed_type = g_boxed_type_register_static ("EggRecentItem",
425 + (GBoxedCopyFunc)egg_recent_item_ref,
426 + (GBoxedFreeFunc)egg_recent_item_unref);
431 diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.h ./libgnome-desktop/egg-recent-item.h
432 --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.h 1969-12-31 19:00:00.000000000 -0500
433 +++ ./libgnome-desktop/egg-recent-item.h 2006-05-08 16:18:44.000000000 -0400
436 +#ifndef __EGG_RECENT_ITEM_H__
437 +#define __EGG_RECENT_ITEM_H__
441 +#include <glib-object.h>
445 +#define EGG_TYPE_RECENT_ITEM (egg_recent_item_get_type ())
447 +#define EGG_RECENT_ITEM_LIST_UNREF(list) \
448 + g_list_foreach (list, (GFunc)egg_recent_item_unref, NULL); \
449 + g_list_free (list);
451 +typedef struct _EggRecentItem EggRecentItem;
453 +struct _EggRecentItem {
454 + /* do not access any of these directly */
459 + gboolean private_data;
465 + guint mime_type_is_explicit : 1;
468 +GType egg_recent_item_get_type (void) G_GNUC_CONST;
471 +EggRecentItem * egg_recent_item_new (void);
473 +EggRecentItem * egg_recent_item_ref (EggRecentItem *item);
474 +EggRecentItem * egg_recent_item_unref (EggRecentItem *item);
476 +/* automatically fetches the mime type, etc */
477 +EggRecentItem * egg_recent_item_new_from_uri (const gchar *uri);
479 +gboolean egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri);
480 +gchar * egg_recent_item_get_uri (const EggRecentItem *item);
481 +gchar * egg_recent_item_get_uri_utf8 (const EggRecentItem *item);
482 +gchar * egg_recent_item_get_uri_for_display (const EggRecentItem *item);
483 +gchar * egg_recent_item_get_short_name (const EggRecentItem *item);
485 +void egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime);
486 +gchar * egg_recent_item_get_mime_type (EggRecentItem *item);
488 +void egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp);
489 +time_t egg_recent_item_get_timestamp (const EggRecentItem *item);
491 +G_CONST_RETURN gchar *egg_recent_item_peek_uri (const EggRecentItem *item);
495 +G_CONST_RETURN GList * egg_recent_item_get_groups (const EggRecentItem *item);
497 +gboolean egg_recent_item_in_group (const EggRecentItem *item,
498 + const gchar *group_name);
500 +void egg_recent_item_add_group (EggRecentItem *item,
501 + const gchar *group_name);
503 +void egg_recent_item_remove_group (EggRecentItem *item,
504 + const gchar *group_name);
506 +void egg_recent_item_set_private (EggRecentItem *item,
509 +gboolean egg_recent_item_get_private (const EggRecentItem *item);
514 +#endif /* __EGG_RECENT_ITEM_H__ */
515 diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.c ./libgnome-desktop/egg-recent-model-multi.c
516 --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.c 1969-12-31 19:00:00.000000000 -0500
517 +++ ./libgnome-desktop/egg-recent-model-multi.c 2006-05-08 17:03:13.000000000 -0400
519 +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
521 + * This program is free software; you can redistribute it and/or modify
522 + * it under the terms of the GNU General Public License as
523 + * published by the Free Software Foundation; either version 2 of the
524 + * License, or (at your option) any later version.
526 + * This program is distributed in the hope that it will be useful,
527 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
528 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
529 + * GNU General Public License for more details.
531 + * You should have received a copy of the GNU General Public License
532 + * along with this program; if not, write to the Free Software
533 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
536 + * James Willcox <jwillcox@cs.indiana.edu>
539 +#ifdef HAVE_CONFIG_H
549 +#include <sys/time.h>
550 +#include <sys/stat.h>
552 +#include <gtk/gtk.h>
553 +#include <libgnomevfs/gnome-vfs.h>
554 +#include <libgnomevfs/gnome-vfs-mime-utils.h>
555 +#include <gconf/gconf-client.h>
556 +#include "egg-recent-model-multi.h"
557 +#include "egg-recent-item.h"
559 +#define EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH ".recently-used"
560 +#define EGG_RECENT_MODEL_MULTI_BUFFER_SIZE 8192
562 +#define EGG_RECENT_MODEL_MULTI_MAX_ITEMS 500
563 +#define EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT 10
564 +#define EGG_RECENT_MODEL_MULTI_TIMEOUT_LENGTH 200
565 +#define EGG_RECENT_MODEL_MULTI_POLL_TIME 3
567 +/* needed for Darwin */
568 +#if !HAVE_DECL_LOCKF
569 +int lockf (int filedes, int function, off_t size);
572 +#define EGG_RECENT_MODEL_MULTI_KEY_DIR "/desktop/gnome/recent_files"
573 +#define EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT_KEY EGG_RECENT_MODEL_MULTI_KEY_DIR "/default_limit"
574 +#define EGG_RECENT_MODEL_MULTI_EXPIRE_KEY EGG_RECENT_MODEL_MULTI_KEY_DIR "/expire"
576 +struct _EggRecentModelMultiPrivate {
577 + GSList *mime_filter_values; /* list of mime types we allow */
578 + GSList *group_filter_values; /* list of groups we allow */
579 + GSList *scheme_filter_values; /* list of URI schemes we allow */
581 + EggRecentModelMultiSort sort_type; /* type of sorting to be done */
583 + int limit; /* soft limit for length of the list */
584 + int expire_days; /* number of days to hold an item */
586 + char *path; /* path to the file we store stuff in */
588 + GHashTable *monitors;
590 + GnomeVFSMonitorHandle *monitor;
592 + GConfClient *client;
593 + gboolean use_default_limit;
595 + guint limit_change_notify_id;
596 + guint expiration_change_notify_id;
598 + guint changed_timeout;
599 + guint poll_timeout;
609 +static GType model_signals[LAST_SIGNAL] = { 0 };
615 + PROP_GROUP_FILTERS,
616 + PROP_SCHEME_FILTERS,
625 + EggRecentItem *current_item;
630 + STATE_RECENT_FILES,
641 + EggRecentModelMulti *model;
645 +#define TAG_RECENT_FILES "RecentFiles"
646 +#define TAG_RECENT_ITEM "RecentItem"
647 +#define TAG_URI "URI"
648 +#define TAG_MIME_TYPE "Mime-Type"
649 +#define TAG_TIMESTAMP "Timestamp"
650 +#define TAG_PRIVATE "Private"
651 +#define TAG_GROUPS "Groups"
652 +#define TAG_GROUP "Group"
654 +static void start_element_handler (GMarkupParseContext *context,
655 + const gchar *element_name,
656 + const gchar **attribute_names,
657 + const gchar **attribute_values,
658 + gpointer user_data,
661 +static void end_element_handler (GMarkupParseContext *context,
662 + const gchar *element_name,
663 + gpointer user_data,
666 +static void text_handler (GMarkupParseContext *context,
669 + gpointer user_data,
672 +static void error_handler (GMarkupParseContext *context,
674 + gpointer user_data);
676 +static GMarkupParser parser = {start_element_handler, end_element_handler,
681 +static GObjectClass *parent_class;
683 +static void egg_recent_model_multi_clear_mime_filter (EggRecentModelMulti *model);
684 +static void egg_recent_model_multi_clear_group_filter (EggRecentModelMulti *model);
685 +static void egg_recent_model_multi_clear_scheme_filter (EggRecentModelMulti *model);
687 +static GObjectClass *parent_class;
690 +egg_recent_model_multi_string_match (const GSList *list, const gchar *str)
694 + if (list == NULL || str == NULL)
700 + if (g_pattern_match_string (tmp->data, str))
710 +egg_recent_model_multi_write_raw (EggRecentModelMulti *model, FILE *file,
711 + const gchar *content)
719 + len = strlen (content);
720 + fd = fileno (file);
722 + if (fstat (fd, &sbuf) < 0)
723 + g_warning ("Couldn't stat XML document.");
725 + if ((off_t)len < sbuf.st_size) {
726 + ftruncate (fd, len);
729 + if (fputs (content, file) == EOF)
741 +egg_recent_model_multi_delete_from_list (GList *list,
752 + EggRecentItem *item = tmp->data;
757 + if (!strcmp (egg_recent_item_peek_uri (item), uri)) {
758 + egg_recent_item_unref (item);
760 + list = g_list_remove_link (list, tmp);
761 + g_list_free_1 (tmp);
771 +egg_recent_model_multi_add_new_groups (EggRecentItem *item,
772 + EggRecentItem *upd_item)
776 + tmp = egg_recent_item_get_groups (upd_item);
779 + char *group = tmp->data;
781 + if (!egg_recent_item_in_group (item, group))
782 + egg_recent_item_add_group (item, group);
789 +egg_recent_model_multi_update_item (GList *items, EggRecentItem *upd_item)
794 + uri = egg_recent_item_peek_uri (upd_item);
799 + EggRecentItem *item = tmp->data;
801 + if (gnome_vfs_uris_match (egg_recent_item_peek_uri (item), uri)) {
802 + egg_recent_item_set_timestamp (item, (time_t) -1);
804 + egg_recent_model_multi_add_new_groups (item, upd_item);
816 +egg_recent_model_multi_read_raw (EggRecentModelMulti *model, FILE *file)
819 + char buf[EGG_RECENT_MODEL_MULTI_BUFFER_SIZE];
823 + string = g_string_new (NULL);
824 + while (fgets (buf, EGG_RECENT_MODEL_MULTI_BUFFER_SIZE, file)) {
825 + string = g_string_append (string, buf);
830 + return g_string_free (string, FALSE);
836 +parse_info_init (void)
840 + retval = g_new0 (ParseInfo, 1);
841 + retval->states = g_slist_prepend (NULL, STATE_START);
842 + retval->items = NULL;
848 +parse_info_free (ParseInfo *info)
850 + g_slist_free (info->states);
855 +push_state (ParseInfo *info,
858 + info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
862 +pop_state (ParseInfo *info)
864 + g_return_if_fail (info->states != NULL);
866 + info->states = g_slist_remove (info->states, info->states->data);
870 +peek_state (ParseInfo *info)
872 + g_return_val_if_fail (info->states != NULL, STATE_START);
874 + return GPOINTER_TO_INT (info->states->data);
877 +#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
880 +valid_element (ParseInfo *info,
881 + int valid_parent_state,
882 + const gchar *element_name,
883 + const gchar *valid_element,
886 + if (peek_state (info) != valid_parent_state) {
887 + g_set_error (error,
889 + G_MARKUP_ERROR_INVALID_CONTENT,
890 + "Unexpected tag '%s', tag '%s' expected",
891 + element_name, valid_element);
899 +start_element_handler (GMarkupParseContext *context,
900 + const gchar *element_name,
901 + const gchar **attribute_names,
902 + const gchar **attribute_values,
903 + gpointer user_data,
906 + ParseInfo *info = (ParseInfo *)user_data;
908 + if (ELEMENT_IS (TAG_RECENT_FILES))
909 + push_state (info, STATE_RECENT_FILES);
910 + else if (ELEMENT_IS (TAG_RECENT_ITEM)) {
911 + if (valid_element (info, STATE_RECENT_FILES,
912 + TAG_RECENT_ITEM, TAG_RECENT_FILES, error)) {
913 + info->current_item = egg_recent_item_new ();
914 + push_state (info, STATE_RECENT_ITEM);
916 + } else if (ELEMENT_IS (TAG_URI)) {
917 + if (valid_element (info, STATE_RECENT_ITEM,
918 + TAG_URI, TAG_RECENT_ITEM, error)) {
919 + push_state (info, STATE_URI);
921 + } else if (ELEMENT_IS (TAG_MIME_TYPE)) {
922 + if (valid_element (info, STATE_RECENT_ITEM,
923 + TAG_MIME_TYPE, TAG_RECENT_ITEM, error)) {
924 + push_state (info, STATE_MIME_TYPE);
926 + } else if (ELEMENT_IS (TAG_TIMESTAMP)) {
927 + if (valid_element (info, STATE_RECENT_ITEM,
928 + TAG_TIMESTAMP, TAG_RECENT_ITEM, error)) {
929 + push_state (info, STATE_TIMESTAMP);
931 + } else if (ELEMENT_IS (TAG_PRIVATE)) {
932 + if (valid_element (info, STATE_RECENT_ITEM,
933 + TAG_PRIVATE, TAG_RECENT_ITEM, error)) {
934 + push_state (info, STATE_PRIVATE);
935 + egg_recent_item_set_private (info->current_item, TRUE);
937 + } else if (ELEMENT_IS (TAG_GROUPS)) {
938 + if (valid_element (info, STATE_RECENT_ITEM,
939 + TAG_GROUPS, TAG_RECENT_ITEM, error)) {
940 + push_state (info, STATE_GROUPS);
942 + } else if (ELEMENT_IS (TAG_GROUP)) {
943 + if (valid_element (info, STATE_GROUPS,
944 + TAG_GROUP, TAG_GROUPS, error)) {
945 + push_state (info, STATE_GROUP);
951 +list_compare_func_mru (gpointer a, gpointer b)
953 + EggRecentItem *item_a = (EggRecentItem *)a;
954 + EggRecentItem *item_b = (EggRecentItem *)b;
956 + return item_a->timestamp < item_b->timestamp;
960 +list_compare_func_lru (gpointer a, gpointer b)
962 + EggRecentItem *item_a = (EggRecentItem *)a;
963 + EggRecentItem *item_b = (EggRecentItem *)b;
965 + return item_a->timestamp > item_b->timestamp;
971 +end_element_handler (GMarkupParseContext *context,
972 + const gchar *element_name,
973 + gpointer user_data,
976 + ParseInfo *info = (ParseInfo *)user_data;
978 + switch (peek_state (info)) {
979 + case STATE_RECENT_ITEM:
980 + if (!info->current_item) {
981 + g_warning ("No recent item found\n");
985 + if (!info->current_item->uri) {
986 + g_warning ("Invalid item found\n");
990 + info->items = g_list_prepend (info->items,
991 + info->current_item);
992 + info->current_item = NULL;
1002 +text_handler (GMarkupParseContext *context,
1003 + const gchar *text,
1005 + gpointer user_data,
1008 + ParseInfo *info = (ParseInfo *)user_data;
1011 + value = g_strndup (text, text_len);
1013 + switch (peek_state (info)) {
1015 + case STATE_RECENT_FILES:
1016 + case STATE_RECENT_ITEM:
1017 + case STATE_PRIVATE:
1018 + case STATE_GROUPS:
1021 + egg_recent_item_set_uri (info->current_item, value);
1023 + case STATE_MIME_TYPE:
1024 + egg_recent_item_set_mime_type (info->current_item, value);
1026 + case STATE_TIMESTAMP:
1027 + egg_recent_item_set_timestamp (info->current_item,
1028 + (time_t)atoi (value));
1031 + egg_recent_item_add_group (info->current_item,
1040 +error_handler (GMarkupParseContext *context,
1042 + gpointer user_data)
1044 + g_warning ("Error in parse: %s", error->message);
1048 +egg_recent_model_multi_enforce_limit (GList *list, int limit)
1053 + /* limit < 0 means unlimited */
1057 + len = g_list_length (list);
1059 + if (len > limit) {
1062 + end = g_list_nth (list, limit-1);
1067 + EGG_RECENT_ITEM_LIST_UNREF (next);
1072 +egg_recent_model_multi_sort (EggRecentModelMulti *model, GList *list)
1074 + switch (model->priv->sort_type) {
1075 + case EGG_RECENT_MODEL_MULTI_SORT_MRU:
1076 + list = g_list_sort (list,
1077 + (GCompareFunc)list_compare_func_mru);
1079 + case EGG_RECENT_MODEL_MULTI_SORT_LRU:
1080 + list = g_list_sort (list,
1081 + (GCompareFunc)list_compare_func_lru);
1083 + case EGG_RECENT_MODEL_MULTI_SORT_NONE:
1091 +egg_recent_model_multi_group_match (EggRecentItem *item, GSList *groups)
1097 + while (tmp != NULL) {
1098 + const gchar * group = (const gchar *)tmp->data;
1100 + if (egg_recent_item_in_group (item, group))
1110 +egg_recent_model_multi_filter (EggRecentModelMulti *model, GList *list)
1112 + GList *newlist = NULL;
1117 + g_return_val_if_fail (list != NULL, NULL);
1119 + for (l = list; l != NULL ; l = l->next) {
1120 + EggRecentItem *item = (EggRecentItem *) l->data;
1121 + gboolean pass_mime_test = FALSE;
1122 + gboolean pass_group_test = FALSE;
1123 + gboolean pass_scheme_test = FALSE;
1125 + g_assert (item != NULL);
1127 + uri = egg_recent_item_get_uri (item);
1129 + /* filter by mime type */
1130 + if (model->priv->mime_filter_values != NULL) {
1131 + mime_type = egg_recent_item_get_mime_type (item);
1133 + if (egg_recent_model_multi_string_match
1134 + (model->priv->mime_filter_values,
1136 + pass_mime_test = TRUE;
1138 + g_free (mime_type);
1140 + pass_mime_test = TRUE;
1142 + /* filter by group */
1143 + if (pass_mime_test && model->priv->group_filter_values != NULL) {
1144 + if (egg_recent_model_multi_group_match
1145 + (item, model->priv->group_filter_values))
1146 + pass_group_test = TRUE;
1147 + } else if (egg_recent_item_get_private (item)) {
1148 + pass_group_test = FALSE;
1150 + pass_group_test = TRUE;
1152 + /* filter by URI scheme */
1153 + if (pass_mime_test && pass_group_test &&
1154 + model->priv->scheme_filter_values != NULL) {
1157 + scheme = gnome_vfs_get_uri_scheme (uri);
1159 + if (egg_recent_model_multi_string_match
1160 + (model->priv->scheme_filter_values, scheme))
1161 + pass_scheme_test = TRUE;
1165 + pass_scheme_test = TRUE;
1167 + if (pass_mime_test && pass_group_test && pass_scheme_test)
1168 + newlist = g_list_prepend (newlist, item);
1170 + egg_recent_item_unref (item);
1175 + g_list_free (list);
1177 + return g_list_reverse (newlist);
1184 +egg_recent_model_multi_monitor_list_cb (GnomeVFSMonitorHandle *handle,
1185 + const gchar *monitor_uri,
1186 + const gchar *info_uri,
1187 + GnomeVFSMonitorEventType event_type,
1188 + gpointer user_data)
1190 + EggRecentModelMulti *model;
1192 + model = EGG_RECENT_MODEL_MULTI (user_data);
1194 + if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
1195 + egg_recent_model_multi_delete (model, monitor_uri);
1196 + g_hash_table_remove (model->priv->monitors, monitor_uri);
1203 +egg_recent_model_multi_monitor_list (EggRecentModelMulti *model, GList *list)
1209 + EggRecentItem *item = (EggRecentItem *)tmp->data;
1210 + GnomeVFSMonitorHandle *handle;
1211 + GnomeVFSResult res;
1216 + uri = egg_recent_item_get_uri (item);
1217 + if (g_hash_table_lookup (model->priv->monitors, uri)) {
1218 + /* already monitoring this one */
1223 + res = gnome_vfs_monitor_add (&handle, uri,
1224 + GNOME_VFS_MONITOR_FILE,
1225 + egg_recent_model_multi_monitor_list_cb,
1228 + if (res == GNOME_VFS_OK)
1229 + g_hash_table_insert (model->priv->monitors, uri, handle);
1238 +egg_recent_model_multi_changed_timeout (EggRecentModelMulti *model)
1240 + model->priv->changed_timeout = 0;
1242 + egg_recent_model_multi_changed (model);
1248 +egg_recent_model_multi_monitor_cb (GnomeVFSMonitorHandle *handle,
1249 + const gchar *monitor_uri,
1250 + const gchar *info_uri,
1251 + GnomeVFSMonitorEventType event_type,
1252 + gpointer user_data)
1254 + EggRecentModelMulti *model;
1256 + g_return_if_fail (user_data != NULL);
1257 + g_return_if_fail (EGG_IS_RECENT_MODEL_MULTI (user_data));
1258 + model = EGG_RECENT_MODEL_MULTI (user_data);
1260 + if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ||
1261 + event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1262 + event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
1263 + if (model->priv->changed_timeout > 0) {
1264 + g_source_remove (model->priv->changed_timeout);
1267 + model->priv->changed_timeout = g_timeout_add (
1268 + EGG_RECENT_MODEL_MULTI_TIMEOUT_LENGTH,
1269 + (GSourceFunc)egg_recent_model_multi_changed_timeout,
1275 +egg_recent_model_multi_poll_timeout (gpointer user_data)
1277 + EggRecentModelMulti *model;
1278 + struct stat stat_buf;
1281 + model = EGG_RECENT_MODEL_MULTI (user_data);
1282 + stat_res = stat (model->priv->path, &stat_buf);
1284 + if (!stat_res && stat_buf.st_mtime &&
1285 + stat_buf.st_mtime != model->priv->last_mtime) {
1286 + model->priv->last_mtime = stat_buf.st_mtime;
1288 + if (model->priv->changed_timeout > 0)
1289 + g_source_remove (model->priv->changed_timeout);
1291 + model->priv->changed_timeout = g_timeout_add (
1292 + EGG_RECENT_MODEL_MULTI_TIMEOUT_LENGTH,
1293 + (GSourceFunc)egg_recent_model_multi_changed_timeout,
1300 +egg_recent_model_multi_monitor (EggRecentModelMulti *model, gboolean should_monitor)
1302 + if (should_monitor && model->priv->monitor == NULL) {
1304 + GnomeVFSResult result;
1306 + uri = gnome_vfs_get_uri_from_local_path (model->priv->path);
1308 + result = gnome_vfs_monitor_add (&model->priv->monitor,
1310 + GNOME_VFS_MONITOR_FILE,
1311 + egg_recent_model_multi_monitor_cb,
1316 + /* if the above fails, don't worry about it.
1317 + * local notifications will still happen
1319 + if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) {
1320 + if (model->priv->poll_timeout > 0)
1321 + g_source_remove (model->priv->poll_timeout);
1323 + model->priv->poll_timeout = g_timeout_add (
1324 + EGG_RECENT_MODEL_MULTI_POLL_TIME * 1000,
1325 + egg_recent_model_multi_poll_timeout,
1329 + } else if (!should_monitor && model->priv->monitor != NULL) {
1330 + gnome_vfs_monitor_cancel (model->priv->monitor);
1331 + model->priv->monitor = NULL;
1336 +egg_recent_model_multi_set_limit_internal (EggRecentModelMulti *model, int limit)
1338 + model->priv->limit = limit;
1341 + egg_recent_model_multi_monitor (model, FALSE);
1343 + egg_recent_model_multi_monitor (model, TRUE);
1344 + egg_recent_model_multi_changed (model);
1349 +egg_recent_model_multi_read (EggRecentModelMulti *model, FILE *file)
1353 + GMarkupParseContext *ctx;
1357 + content = egg_recent_model_multi_read_raw (model, file);
1359 + if (strlen (content) <= 0) {
1364 + info = parse_info_init ();
1366 + ctx = g_markup_parse_context_new (&parser, 0, info, NULL);
1369 + if (!g_markup_parse_context_parse (ctx, content, strlen (content), &error)) {
1370 + g_warning ("Error while parsing the .recently-used file: %s\n",
1373 + g_error_free (error);
1374 + parse_info_free (info);
1380 + if (!g_markup_parse_context_end_parse (ctx, &error)) {
1381 + g_warning ("Unable to complete parsing of the .recently-used file: %s\n",
1384 + g_error_free (error);
1385 + g_markup_parse_context_free (ctx);
1386 + parse_info_free (info);
1391 + list = g_list_reverse (info->items);
1393 + g_markup_parse_context_free (ctx);
1394 + parse_info_free (info);
1402 +egg_recent_model_multi_write (EggRecentModelMulti *model, FILE *file, GList *list)
1406 + EggRecentItem *item;
1407 + const GList *groups;
1411 + string = g_string_new ("<?xml version=\"1.0\"?>\n");
1412 + string = g_string_append (string, "<" TAG_RECENT_FILES ">\n");
1418 + gchar *escaped_uri;
1420 + item = (EggRecentItem *)list->data;
1423 + uri = egg_recent_item_get_uri_utf8 (item);
1424 + escaped_uri = g_markup_escape_text (uri,
1428 + mime_type = egg_recent_item_get_mime_type (item);
1429 + timestamp = egg_recent_item_get_timestamp (item);
1431 + string = g_string_append (string, " <" TAG_RECENT_ITEM ">\n");
1433 + g_string_append_printf (string,
1434 + " <" TAG_URI ">%s</" TAG_URI ">\n", escaped_uri);
1437 + g_string_append_printf (string,
1438 + " <" TAG_MIME_TYPE ">%s</" TAG_MIME_TYPE ">\n", mime_type);
1440 + g_string_append_printf (string,
1441 + " <" TAG_MIME_TYPE "></" TAG_MIME_TYPE ">\n");
1444 + g_string_append_printf (string,
1445 + " <" TAG_TIMESTAMP ">%d</" TAG_TIMESTAMP ">\n", (int)timestamp);
1447 + if (egg_recent_item_get_private (item))
1448 + string = g_string_append (string,
1449 + " <" TAG_PRIVATE "/>\n");
1451 + /* write the groups */
1452 + string = g_string_append (string,
1453 + " <" TAG_GROUPS ">\n");
1454 + groups = egg_recent_item_get_groups (item);
1456 + if (groups == NULL && egg_recent_item_get_private (item))
1457 + g_warning ("Item with URI \"%s\" marked as private, but"
1458 + " does not belong to any groups.\n", uri);
1461 + const gchar *group = (const gchar *)groups->data;
1462 + gchar *escaped_group;
1464 + escaped_group = g_markup_escape_text (group, strlen(group));
1466 + g_string_append_printf (string,
1467 + " <" TAG_GROUP ">%s</" TAG_GROUP ">\n",
1470 + g_free (escaped_group);
1472 + groups = groups->next;
1475 + string = g_string_append (string, " </" TAG_GROUPS ">\n");
1477 + string = g_string_append (string,
1478 + " </" TAG_RECENT_ITEM ">\n");
1480 + g_free (mime_type);
1481 + g_free (escaped_uri);
1483 + list = list->next;
1487 + string = g_string_append (string, "</" TAG_RECENT_FILES ">");
1489 + data = g_string_free (string, FALSE);
1491 + ret = egg_recent_model_multi_write_raw (model, file, data);
1499 +egg_recent_model_multi_open_file (EggRecentModelMulti *model,
1500 + gboolean for_writing)
1503 + mode_t prev_umask;
1505 + file = fopen (model->priv->path, "r+");
1506 + if (file == NULL && for_writing) {
1508 + prev_umask = umask (077);
1510 + file = fopen (model->priv->path, "w+");
1512 + umask (prev_umask);
1514 + g_return_val_if_fail (file != NULL, NULL);
1521 +egg_recent_model_multi_lock_file (FILE *file)
1528 + fd = fileno (file);
1530 + /* Attempt to lock the file 5 times,
1531 + * waiting a random interval (< 1 second)
1532 + * in between attempts.
1533 + * We should really be doing asynchronous
1534 + * locking, but requires substantially larger
1540 + int rand_interval;
1542 + if (lockf (fd, F_TLOCK, 0) == 0)
1545 + rand_interval = 1 + (int) (10.0 * rand()/(RAND_MAX + 1.0));
1547 + g_usleep (100000 * rand_interval);
1555 +#endif /* HAVE_LOCKF */
1559 +egg_recent_model_multi_unlock_file (FILE *file)
1565 + fd = fileno (file);
1567 + return (lockf (fd, F_ULOCK, 0) == 0) ? TRUE : FALSE;
1570 +#endif /* HAVE_LOCKF */
1574 +egg_recent_model_multi_finalize (GObject *object)
1576 + EggRecentModelMulti *model = EGG_RECENT_MODEL_MULTI (object);
1578 + if (model->priv->changed_timeout > 0) {
1579 + g_source_remove (model->priv->changed_timeout);
1582 + egg_recent_model_multi_monitor (model, FALSE);
1585 + g_slist_foreach (model->priv->mime_filter_values,
1586 + (GFunc) g_pattern_spec_free, NULL);
1587 + g_slist_free (model->priv->mime_filter_values);
1588 + model->priv->mime_filter_values = NULL;
1590 + g_slist_foreach (model->priv->scheme_filter_values,
1591 + (GFunc) g_pattern_spec_free, NULL);
1592 + g_slist_free (model->priv->scheme_filter_values);
1593 + model->priv->scheme_filter_values = NULL;
1595 + g_slist_foreach (model->priv->group_filter_values,
1596 + (GFunc) g_free, NULL);
1597 + g_slist_free (model->priv->group_filter_values);
1598 + model->priv->group_filter_values = NULL;
1601 + if (model->priv->limit_change_notify_id)
1602 + gconf_client_notify_remove (model->priv->client,
1603 + model->priv->limit_change_notify_id);
1604 + model->priv->expiration_change_notify_id = 0;
1606 + if (model->priv->expiration_change_notify_id)
1607 + gconf_client_notify_remove (model->priv->client,
1608 + model->priv->expiration_change_notify_id);
1609 + model->priv->expiration_change_notify_id = 0;
1611 + g_object_unref (model->priv->client);
1612 + model->priv->client = NULL;
1615 + g_free (model->priv->path);
1616 + model->priv->path = NULL;
1618 + g_hash_table_destroy (model->priv->monitors);
1619 + model->priv->monitors = NULL;
1621 + if (model->priv->poll_timeout > 0)
1622 + g_source_remove (model->priv->poll_timeout);
1623 + model->priv->poll_timeout =0;
1625 + g_free (model->priv);
1627 + parent_class->finalize (object);
1631 +egg_recent_model_multi_set_property (GObject *object,
1633 + const GValue *value,
1634 + GParamSpec *pspec)
1636 + EggRecentModelMulti *model = EGG_RECENT_MODEL_MULTI (object);
1640 + case PROP_MIME_FILTERS:
1641 + if (model->priv->mime_filter_values != NULL)
1642 + egg_recent_model_multi_clear_mime_filter (model);
1644 + model->priv->mime_filter_values =
1645 + (GSList *)g_value_get_pointer (value);
1648 + case PROP_GROUP_FILTERS:
1649 + if (model->priv->group_filter_values != NULL)
1650 + egg_recent_model_multi_clear_group_filter (model);
1652 + model->priv->group_filter_values =
1653 + (GSList *)g_value_get_pointer (value);
1656 + case PROP_SCHEME_FILTERS:
1657 + if (model->priv->scheme_filter_values != NULL)
1658 + egg_recent_model_multi_clear_scheme_filter (model);
1660 + model->priv->scheme_filter_values =
1661 + (GSList *)g_value_get_pointer (value);
1664 + case PROP_SORT_TYPE:
1665 + model->priv->sort_type = g_value_get_int (value);
1668 + case PROP_FILE_PATH:
1669 + model->priv->path = g_strdup_printf (
1671 + g_get_home_dir (),
1672 + g_value_get_string (value)
1674 + egg_recent_model_multi_monitor (model, TRUE);
1678 + egg_recent_model_multi_set_limit (model,
1679 + g_value_get_int (value));
1683 + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1689 +egg_recent_model_multi_get_property (GObject *object,
1692 + GParamSpec *pspec)
1694 + EggRecentModelMulti *model = EGG_RECENT_MODEL_MULTI (object);
1698 + case PROP_MIME_FILTERS:
1699 + g_value_set_pointer (value, model->priv->mime_filter_values);
1702 + case PROP_GROUP_FILTERS:
1703 + g_value_set_pointer (value, model->priv->group_filter_values);
1706 + case PROP_SCHEME_FILTERS:
1707 + g_value_set_pointer (value, model->priv->scheme_filter_values);
1710 + case PROP_SORT_TYPE:
1711 + g_value_set_int (value, model->priv->sort_type);
1714 + case PROP_FILE_PATH:
1715 + g_value_set_string (value, model->priv->path);
1719 + g_value_set_int (value, model->priv->limit);
1723 + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1729 +egg_recent_model_multi_class_init (EggRecentModelMultiClass * klass)
1731 + GObjectClass *object_class;
1733 + parent_class = g_type_class_peek_parent (klass);
1735 + parent_class = g_type_class_peek_parent (klass);
1737 + object_class = G_OBJECT_CLASS (klass);
1738 + object_class->set_property = egg_recent_model_multi_set_property;
1739 + object_class->get_property = egg_recent_model_multi_get_property;
1740 + object_class->finalize = egg_recent_model_multi_finalize;
1742 + model_signals[CHANGED] = g_signal_new ("changed",
1743 + G_OBJECT_CLASS_TYPE (object_class),
1744 + G_SIGNAL_RUN_LAST,
1745 + G_STRUCT_OFFSET (EggRecentModelMultiClass, changed),
1747 + g_cclosure_marshal_VOID__POINTER,
1752 + g_object_class_install_property (object_class,
1753 + PROP_MIME_FILTERS,
1754 + g_param_spec_pointer ("mime-filters",
1756 + "List of mime types to be allowed.",
1757 + G_PARAM_READWRITE));
1759 + g_object_class_install_property (object_class,
1760 + PROP_GROUP_FILTERS,
1761 + g_param_spec_pointer ("group-filters",
1763 + "List of groups to be allowed.",
1764 + G_PARAM_READWRITE));
1766 + g_object_class_install_property (object_class,
1767 + PROP_SCHEME_FILTERS,
1768 + g_param_spec_pointer ("scheme-filters",
1770 + "List of URI schemes to be allowed.",
1771 + G_PARAM_READWRITE));
1773 + g_object_class_install_property (object_class,
1775 + g_param_spec_int ("sort-type",
1777 + "Type of sorting to be done.",
1778 + 0, EGG_RECENT_MODEL_MULTI_SORT_NONE,
1779 + EGG_RECENT_MODEL_MULTI_SORT_MRU,
1780 + G_PARAM_READWRITE));
1782 + g_object_class_install_property (object_class,
1784 + g_param_spec_string ("file-path",
1786 + "File path relative to $HOME.",
1787 + EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH,
1788 + G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
1790 + g_object_class_install_property (object_class,
1792 + g_param_spec_int ("limit",
1794 + "Max number of items allowed.",
1795 + -1, EGG_RECENT_MODEL_MULTI_MAX_ITEMS,
1796 + EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT,
1797 + G_PARAM_READWRITE));
1799 + klass->changed = NULL;
1805 +egg_recent_model_multi_limit_changed (GConfClient *client, guint cnxn_id,
1806 + GConfEntry *entry, gpointer user_data)
1808 + EggRecentModelMulti *model;
1809 + GConfValue *value;
1811 + model = EGG_RECENT_MODEL_MULTI (user_data);
1813 + g_return_if_fail (model != NULL);
1815 + if (model->priv->use_default_limit == FALSE)
1816 + return; /* ignore this key */
1818 + /* the key was unset, and the schema has apparently failed */
1819 + if (entry == NULL)
1822 + value = gconf_entry_get_value (entry);
1824 + if (value->type != GCONF_VALUE_INT) {
1825 + g_warning ("Expected GConfValue of type integer, "
1826 + "got something else");
1830 + egg_recent_model_multi_set_limit_internal (model, gconf_value_get_int (value));
1834 +egg_recent_model_multi_expiration_changed (GConfClient *client, guint cnxn_id,
1835 + GConfEntry *entry, gpointer user_data)
1841 +egg_recent_model_multi_init (EggRecentModelMulti * model)
1843 + if (!gnome_vfs_init ()) {
1844 + g_warning ("gnome-vfs initialization failed.");
1849 + model->priv = g_new0 (EggRecentModelMultiPrivate, 1);
1851 + model->priv->mime_filter_values = NULL;
1852 + model->priv->group_filter_values = NULL;
1853 + model->priv->scheme_filter_values = NULL;
1855 + model->priv->client = gconf_client_get_default ();
1856 + gconf_client_add_dir (model->priv->client, EGG_RECENT_MODEL_MULTI_KEY_DIR,
1857 + GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
1859 + model->priv->limit_change_notify_id =
1860 + gconf_client_notify_add (model->priv->client,
1861 + EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT_KEY,
1862 + egg_recent_model_multi_limit_changed,
1863 + model, NULL, NULL);
1865 + model->priv->expiration_change_notify_id =
1866 + gconf_client_notify_add (model->priv->client,
1867 + EGG_RECENT_MODEL_MULTI_EXPIRE_KEY,
1868 + egg_recent_model_multi_expiration_changed,
1869 + model, NULL, NULL);
1871 + model->priv->expire_days = gconf_client_get_int (
1872 + model->priv->client,
1873 + EGG_RECENT_MODEL_MULTI_EXPIRE_KEY,
1877 + /* keep this out, for now */
1878 + model->priv->limit = gconf_client_get_int (
1879 + model->priv->client,
1880 + EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT_KEY, NULL);
1881 + model->priv->use_default_limit = TRUE;
1883 + model->priv->limit = EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT;
1884 + model->priv->use_default_limit = FALSE;
1886 + model->priv->monitors = g_hash_table_new_full (
1887 + g_str_hash, g_str_equal,
1888 + (GDestroyNotify) g_free,
1889 + (GDestroyNotify) gnome_vfs_monitor_cancel);
1891 + model->priv->monitor = NULL;
1892 + model->priv->poll_timeout = 0;
1893 + model->priv->last_mtime = 0;
1898 + * egg_recent_model_multi_new:
1899 + * @sort: the type of sorting to use
1900 + * @limit: maximum number of items in the list
1902 + * This creates a new EggRecentModelMulti object.
1904 + * Returns: a EggRecentModelMulti object
1906 +EggRecentModelMulti *
1907 +egg_recent_model_multi_new (EggRecentModelMultiSort sort)
1909 + return egg_recent_model_multi_new_with_file_path (sort, EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH);
1912 +EggRecentModelMulti *
1913 +egg_recent_model_multi_new_with_file_path (EggRecentModelMultiSort sort, const gchar *file_path)
1915 + EggRecentModelMulti *model;
1918 + file_path = EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH;
1920 + model = EGG_RECENT_MODEL_MULTI (g_object_new (egg_recent_model_multi_get_type (),
1921 + "sort-type", sort, "file-path", file_path, NULL));
1923 + g_return_val_if_fail (model, NULL);
1929 + * egg_recent_model_multi_add_full:
1930 + * @model: A EggRecentModelMulti object.
1931 + * @item: A EggRecentItem
1933 + * This function adds an item to the list of recently used URIs.
1935 + * Returns: gboolean
1938 +egg_recent_model_multi_add_full (EggRecentModelMulti * model, EggRecentItem *item)
1941 + GList *list = NULL;
1942 + gboolean ret = FALSE;
1943 + gboolean updated = FALSE;
1947 + g_return_val_if_fail (model != NULL, FALSE);
1948 + g_return_val_if_fail (EGG_IS_RECENT_MODEL_MULTI (model), FALSE);
1950 + uri = egg_recent_item_get_uri (item);
1951 + if (strncmp (uri, "recent-files://", strlen ("recent-files://")) == 0) {
1958 + file = egg_recent_model_multi_open_file (model, TRUE);
1959 + g_return_val_if_fail (file != NULL, FALSE);
1962 + egg_recent_item_set_timestamp (item, t);
1964 + if (egg_recent_model_multi_lock_file (file)) {
1966 + /* read existing stuff */
1967 + list = egg_recent_model_multi_read (model, file);
1969 + /* if it's already there, we just update it */
1970 + updated = egg_recent_model_multi_update_item (list, item);
1973 + list = g_list_prepend (list, item);
1975 + egg_recent_model_multi_enforce_limit (list,
1976 + EGG_RECENT_MODEL_MULTI_MAX_ITEMS);
1979 + /* write new stuff */
1980 + if (!egg_recent_model_multi_write (model, file, list))
1981 + g_warning ("Write failed: %s", strerror (errno));
1984 + list = g_list_remove (list, item);
1986 + EGG_RECENT_ITEM_LIST_UNREF (list);
1989 + g_warning ("Failed to lock: %s", strerror (errno));
1994 + if (!egg_recent_model_multi_unlock_file (file))
1995 + g_warning ("Failed to unlock: %s", strerror (errno));
1999 + if (model->priv->monitor == NULL) {
2000 + /* since monitoring isn't working, at least give a
2001 + * local notification
2003 + egg_recent_model_multi_changed (model);
2010 + * egg_recent_model_multi_add:
2011 + * @model: A EggRecentModelMulti object.
2012 + * @uri: A string URI
2014 + * This function adds an item to the list of recently used URIs.
2016 + * Returns: gboolean
2019 +egg_recent_model_multi_add (EggRecentModelMulti *model, const gchar *uri)
2021 + EggRecentItem *item;
2022 + gboolean ret = FALSE;
2024 + g_return_val_if_fail (model != NULL, FALSE);
2025 + g_return_val_if_fail (uri != NULL, FALSE);
2027 + item = egg_recent_item_new_from_uri (uri);
2029 + g_return_val_if_fail (item != NULL, FALSE);
2031 + ret = egg_recent_model_multi_add_full (model, item);
2033 + egg_recent_item_unref (item);
2041 + * egg_recent_model_multi_delete:
2042 + * @model: A EggRecentModelMulti object.
2043 + * @uri: The URI you want to delete.
2045 + * This function deletes a URI from the file of recently used URIs.
2047 + * Returns: gboolean
2050 +egg_recent_model_multi_delete (EggRecentModelMulti * model, const gchar * uri)
2054 + unsigned int length;
2055 + gboolean ret = FALSE;
2057 + g_return_val_if_fail (model != NULL, FALSE);
2058 + g_return_val_if_fail (EGG_IS_RECENT_MODEL_MULTI (model), FALSE);
2059 + g_return_val_if_fail (uri != NULL, FALSE);
2061 + file = egg_recent_model_multi_open_file (model, TRUE);
2062 + g_return_val_if_fail (file != NULL, FALSE);
2064 + if (egg_recent_model_multi_lock_file (file)) {
2065 + list = egg_recent_model_multi_read (model, file);
2070 + length = g_list_length (list);
2072 + list = egg_recent_model_multi_delete_from_list (list, uri);
2074 + if (length == g_list_length (list)) {
2075 + /* nothing was deleted */
2076 + EGG_RECENT_ITEM_LIST_UNREF (list);
2078 + egg_recent_model_multi_write (model, file, list);
2079 + EGG_RECENT_ITEM_LIST_UNREF (list);
2084 + g_warning ("Failed to lock: %s", strerror (errno));
2090 + if (!egg_recent_model_multi_unlock_file (file))
2091 + g_warning ("Failed to unlock: %s", strerror (errno));
2095 + g_hash_table_remove (model->priv->monitors, uri);
2097 + if (model->priv->monitor == NULL && ret) {
2098 + /* since monitoring isn't working, at least give a
2099 + * local notification
2101 + egg_recent_model_multi_changed (model);
2109 + * egg_recent_model_multi_get_list:
2110 + * @model: A EggRecentModelMulti object.
2112 + * This function gets the current contents of the file
2114 + * Returns: a GList
2117 +egg_recent_model_multi_get_list (EggRecentModelMulti *model)
2120 + GList *list = NULL;
2122 + file = egg_recent_model_multi_open_file (model, FALSE);
2126 + if (egg_recent_model_multi_lock_file (file))
2127 + list = egg_recent_model_multi_read (model, file);
2129 + g_warning ("Failed to lock: %s", strerror (errno));
2134 + if (!egg_recent_model_multi_unlock_file (file))
2135 + g_warning ("Failed to unlock: %s", strerror (errno));
2137 + if (list != NULL) {
2138 + list = egg_recent_model_multi_filter (model, list);
2139 + list = egg_recent_model_multi_sort (model, list);
2141 + egg_recent_model_multi_enforce_limit (list, model->priv->limit);
2152 + * egg_recent_model_multi_set_limit:
2153 + * @model: A EggRecentModelMulti object.
2154 + * @limit: The maximum length of the list
2156 + * This function sets the maximum length of the list. Note: This only affects
2157 + * the length of the list emitted in the "changed" signal, not the list stored
2163 +egg_recent_model_multi_set_limit (EggRecentModelMulti *model, int limit)
2165 + model->priv->use_default_limit = FALSE;
2167 + egg_recent_model_multi_set_limit_internal (model, limit);
2171 + * egg_recent_model_multi_get_limit:
2172 + * @model: A EggRecentModelMulti object.
2174 + * This function gets the maximum length of the list.
2179 +egg_recent_model_multi_get_limit (EggRecentModelMulti *model)
2181 + return model->priv->limit;
2186 + * egg_recent_model_multi_clear:
2187 + * @model: A EggRecentModelMulti object.
2189 + * This function clears the contents of the file
2194 +egg_recent_model_multi_clear (EggRecentModelMulti *model)
2199 + file = egg_recent_model_multi_open_file (model, TRUE);
2200 + g_return_if_fail (file != NULL);
2202 + fd = fileno (file);
2204 + if (egg_recent_model_multi_lock_file (file)) {
2205 + ftruncate (fd, 0);
2207 + g_warning ("Failed to lock: %s", strerror (errno));
2211 + if (!egg_recent_model_multi_unlock_file (file))
2212 + g_warning ("Failed to unlock: %s", strerror (errno));
2216 + if (model->priv->monitor == NULL) {
2217 + /* since monitoring isn't working, at least give a
2218 + * local notification
2220 + egg_recent_model_multi_changed (model);
2225 +egg_recent_model_multi_clear_mime_filter (EggRecentModelMulti *model)
2227 + g_return_if_fail (model != NULL);
2229 + if (model->priv->mime_filter_values != NULL) {
2230 + g_slist_foreach (model->priv->mime_filter_values,
2231 + (GFunc) g_pattern_spec_free, NULL);
2232 + g_slist_free (model->priv->mime_filter_values);
2233 + model->priv->mime_filter_values = NULL;
2238 + * egg_recent_model_multi_set_filter_mime_types:
2239 + * @model: A EggRecentModelMulti object.
2241 + * Sets which mime types are allowed in the list.
2246 +egg_recent_model_multi_set_filter_mime_types (EggRecentModelMulti *model,
2250 + GSList *list = NULL;
2253 + g_return_if_fail (model != NULL);
2255 + egg_recent_model_multi_clear_mime_filter (model);
2257 + va_start (valist, model);
2259 + str = va_arg (valist, gchar*);
2261 + while (str != NULL) {
2262 + list = g_slist_prepend (list, g_pattern_spec_new (str));
2264 + str = va_arg (valist, gchar*);
2269 + model->priv->mime_filter_values = list;
2273 +egg_recent_model_multi_clear_group_filter (EggRecentModelMulti *model)
2275 + g_return_if_fail (model != NULL);
2277 + if (model->priv->group_filter_values != NULL) {
2278 + g_slist_foreach (model->priv->group_filter_values, (GFunc)g_free, NULL);
2279 + g_slist_free (model->priv->group_filter_values);
2280 + model->priv->group_filter_values = NULL;
2285 + * egg_recent_model_multi_set_filter_groups:
2286 + * @model: A EggRecentModelMulti object.
2288 + * Sets which groups are allowed in the list.
2293 +egg_recent_model_multi_set_filter_groups (EggRecentModelMulti *model,
2297 + GSList *list = NULL;
2300 + g_return_if_fail (model != NULL);
2302 + egg_recent_model_multi_clear_group_filter (model);
2304 + va_start (valist, model);
2306 + str = va_arg (valist, gchar*);
2308 + while (str != NULL) {
2309 + list = g_slist_prepend (list, g_strdup (str));
2311 + str = va_arg (valist, gchar*);
2316 + model->priv->group_filter_values = list;
2320 +egg_recent_model_multi_clear_scheme_filter (EggRecentModelMulti *model)
2322 + g_return_if_fail (model != NULL);
2324 + if (model->priv->scheme_filter_values != NULL) {
2325 + g_slist_foreach (model->priv->scheme_filter_values,
2326 + (GFunc) g_pattern_spec_free, NULL);
2327 + g_slist_free (model->priv->scheme_filter_values);
2328 + model->priv->scheme_filter_values = NULL;
2333 + * egg_recent_model_multi_set_filter_uri_schemes:
2334 + * @model: A EggRecentModelMulti object.
2336 + * Sets which URI schemes (file, http, ftp, etc) are allowed in the list.
2341 +egg_recent_model_multi_set_filter_uri_schemes (EggRecentModelMulti *model, ...)
2344 + GSList *list = NULL;
2347 + g_return_if_fail (model != NULL);
2349 + egg_recent_model_multi_clear_scheme_filter (model);
2351 + va_start (valist, model);
2353 + str = va_arg (valist, gchar*);
2355 + while (str != NULL) {
2356 + list = g_slist_prepend (list, g_pattern_spec_new (str));
2358 + str = va_arg (valist, gchar*);
2363 + model->priv->scheme_filter_values = list;
2367 + * egg_recent_model_multi_set_sort:
2368 + * @model: A EggRecentModelMulti object.
2369 + * @sort: A EggRecentModelMultiSort type
2371 + * Sets the type of sorting to be used.
2376 +egg_recent_model_multi_set_sort (EggRecentModelMulti *model,
2377 + EggRecentModelMultiSort sort)
2379 + g_return_if_fail (model != NULL);
2381 + model->priv->sort_type = sort;
2385 + * egg_recent_model_multi_changed:
2386 + * @model: A EggRecentModelMulti object.
2388 + * This function causes a "changed" signal to be emitted.
2393 +egg_recent_model_multi_changed (EggRecentModelMulti *model)
2395 + GList *list = NULL;
2397 + if (model->priv->limit > 0) {
2398 + list = egg_recent_model_multi_get_list (model);
2399 + /* egg_recent_model_multi_monitor_list (model, list); */
2401 + g_signal_emit (G_OBJECT (model), model_signals[CHANGED], 0,
2406 + EGG_RECENT_ITEM_LIST_UNREF (list);
2410 +egg_recent_model_multi_remove_expired_list (EggRecentModelMulti *model, GList *list)
2412 + time_t current_time;
2413 + time_t day_seconds;
2415 + time (¤t_time);
2416 + day_seconds = model->priv->expire_days*24*60*60;
2418 + while (list != NULL) {
2419 + EggRecentItem *item = list->data;
2422 + timestamp = egg_recent_item_get_timestamp (item);
2424 + if ((timestamp+day_seconds) < current_time) {
2425 + gchar *uri = egg_recent_item_get_uri (item);
2426 + egg_recent_model_multi_delete (model, uri);
2431 + list = list->next;
2437 + * egg_recent_model_multi_remove_expired:
2438 + * @model: A EggRecentModelMulti object.
2440 + * Goes through the entire list, and removes any items that are older than
2441 + * the user-specified expiration period.
2446 +egg_recent_model_multi_remove_expired (EggRecentModelMulti *model)
2451 + g_return_if_fail (model != NULL);
2453 + file = egg_recent_model_multi_open_file (model, FALSE);
2457 + if (egg_recent_model_multi_lock_file (file)) {
2458 + list = egg_recent_model_multi_read (model, file);
2461 + g_warning ("Failed to lock: %s", strerror (errno));
2465 + if (!egg_recent_model_multi_unlock_file (file))
2466 + g_warning ("Failed to unlock: %s", strerror (errno));
2468 + if (list != NULL) {
2469 + egg_recent_model_multi_remove_expired_list (model, list);
2470 + EGG_RECENT_ITEM_LIST_UNREF (list);
2477 + * egg_recent_model_multi_get_type:
2479 + * This returns a GType representing a EggRecentModelMulti object.
2481 + * Returns: a GType
2484 +egg_recent_model_multi_get_type (void)
2486 + static GType egg_recent_model_multi_type = 0;
2488 + if(!egg_recent_model_multi_type) {
2489 + static const GTypeInfo egg_recent_model_multi_info = {
2490 + sizeof (EggRecentModelMultiClass),
2491 + NULL, /* base init */
2492 + NULL, /* base finalize */
2493 + (GClassInitFunc)egg_recent_model_multi_class_init, /* class init */
2494 + NULL, /* class finalize */
2495 + NULL, /* class data */
2496 + sizeof (EggRecentModelMulti),
2498 + (GInstanceInitFunc) egg_recent_model_multi_init
2501 + egg_recent_model_multi_type = g_type_register_static (G_TYPE_OBJECT,
2502 + "EggRecentModelMulti",
2503 + &egg_recent_model_multi_info, 0);
2506 + return egg_recent_model_multi_type;
2509 diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.h ./libgnome-desktop/egg-recent-model-multi.h
2510 --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.h 1969-12-31 19:00:00.000000000 -0500
2511 +++ ./libgnome-desktop/egg-recent-model-multi.h 2006-05-08 16:48:27.000000000 -0400
2513 +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2514 +#ifndef __EGG_RECENT_MODEL_MULTI_H__
2515 +#define __EGG_RECENT_MODEL_MULTI_H__
2517 +#include "egg-recent-item.h"
2521 +#define EGG_TYPE_RECENT_MODEL_MULTI (egg_recent_model_multi_get_type ())
2522 +#define EGG_RECENT_MODEL_MULTI(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, EGG_TYPE_RECENT_MODEL_MULTI, EggRecentModelMulti)
2523 +#define EGG_RECENT_MODEL_MULTI_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, EGG_TYPE_RECENT_MODEL_MULTI, EggRecentModelMultiClass)
2524 +#define EGG_IS_RECENT_MODEL_MULTI(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_model_multi_get_type ())
2526 +typedef struct _EggRecentModelMulti EggRecentModelMulti;
2527 +typedef struct _EggRecentModelMultiPrivate EggRecentModelMultiPrivate;
2528 +typedef struct _EggRecentModelMultiClass EggRecentModelMultiClass;
2530 +struct _EggRecentModelMulti {
2531 + GObject parent_instance;
2533 + EggRecentModelMultiPrivate *priv;
2536 +struct _EggRecentModelMultiClass {
2537 + GObjectClass parent_class;
2539 + void (*changed) (EggRecentModelMulti *model, GList *list);
2543 + EGG_RECENT_MODEL_MULTI_SORT_MRU,
2544 + EGG_RECENT_MODEL_MULTI_SORT_LRU,
2545 + EGG_RECENT_MODEL_MULTI_SORT_NONE
2546 +} EggRecentModelMultiSort;
2549 +/* Standard group names */
2550 +#define EGG_RECENT_GROUP_LAUNCHERS "Launchers"
2553 +GType egg_recent_model_multi_get_type (void);
2556 +EggRecentModelMulti * egg_recent_model_multi_new (EggRecentModelMultiSort sort);
2557 +EggRecentModelMulti * egg_recent_model_multi_new_with_file_path (EggRecentModelMultiSort sort, const gchar *file_path);
2559 +/* public methods */
2560 +void egg_recent_model_multi_set_filter_mime_types (EggRecentModelMulti *model,
2563 +void egg_recent_model_multi_set_filter_groups (EggRecentModelMulti *model, ...);
2565 +void egg_recent_model_multi_set_filter_uri_schemes (EggRecentModelMulti *model,
2568 +void egg_recent_model_multi_set_sort (EggRecentModelMulti *model,
2569 + EggRecentModelMultiSort sort);
2571 +gboolean egg_recent_model_multi_add_full (EggRecentModelMulti *model,
2572 + EggRecentItem *item);
2574 +gboolean egg_recent_model_multi_add (EggRecentModelMulti *model,
2575 + const gchar *uri);
2577 +gboolean egg_recent_model_multi_delete (EggRecentModelMulti *model,
2578 + const gchar *uri);
2580 +void egg_recent_model_multi_clear (EggRecentModelMulti *model);
2582 +GList * egg_recent_model_multi_get_list (EggRecentModelMulti *model);
2584 +void egg_recent_model_multi_changed (EggRecentModelMulti *model);
2586 +void egg_recent_model_multi_set_limit (EggRecentModelMulti *model, int limit);
2587 +int egg_recent_model_multi_get_limit (EggRecentModelMulti *model);
2589 +void egg_recent_model_multi_remove_expired (EggRecentModelMulti *model);
2593 +#endif /* __EGG_RECENT_MODEL_MULTI_H__ */
2594 diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/gnome-desktop-item.c ./libgnome-desktop/gnome-desktop-item.c
2595 --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/gnome-desktop-item.c 2005-11-04 03:43:42.000000000 -0500
2596 +++ ./libgnome-desktop/gnome-desktop-item.c 2006-05-08 17:07:23.000000000 -0400
2598 #include <gtk/gtk.h>
2601 +#include "egg-recent-item.h"
2602 +#include "egg-recent-model-multi.h"
2604 #define sure_string(s) ((s)!=NULL?(s):"")
2606 struct _GnomeDesktopItem {
2611 +static void update_recently_used_apps (const GnomeDesktopItem *item);
2614 readbuf_getc (ReadBuf *rb)
2616 @@ -2091,6 +2096,8 @@
2617 (flags & GNOME_DESKTOP_ITEM_LAUNCH_APPEND_PATHS),
2620 + update_recently_used_apps (item);
2625 @@ -4095,3 +4102,22 @@
2631 +update_recently_used_apps (const GnomeDesktopItem *item)
2633 + EggRecentModelMulti *model;
2639 + model = egg_recent_model_multi_new_with_file_path (EGG_RECENT_MODEL_MULTI_SORT_MRU, ".recently-used-apps");
2644 + egg_recent_model_multi_add (model, gnome_desktop_item_get_location (item));
2646 + g_object_unref (G_OBJECT (model));
2648 Files ../gnome-desktop-2.12.2-pristine/libgnome-desktop/.gnome-desktop-item.c.swp and ./libgnome-desktop/.gnome-desktop-item.c.swp differ
2649 diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/Makefile.am ./libgnome-desktop/Makefile.am
2650 --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/Makefile.am 2004-11-18 12:42:53.000000000 -0500
2651 +++ ./libgnome-desktop/Makefile.am 2006-05-08 16:55:25.000000000 -0400
2654 noinst_PROGRAMS = test-ditem test-hint test-ditem-edit
2656 -libgnome_desktop_2_la_SOURCES = \
2657 - gnome-desktop-item.c \
2658 - gnome-ditem-edit.c \
2659 +libgnome_desktop_2_la_SOURCES = \
2660 + gnome-desktop-item.c \
2661 + gnome-ditem-edit.c \
2662 + egg-recent-item.h \
2663 + egg-recent-item.c \
2664 + egg-recent-model-multi.h \
2665 + egg-recent-model-multi.c \
2668 libgnome_desktop_2_la_LIBADD = \