2702 lines
63 KiB
Diff
2702 lines
63 KiB
Diff
diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/Makefile.am gnome-desktop-2.16.1/libgnome-desktop/Makefile.am
|
|
--- gnome-desktop-2.16.1-pristine/libgnome-desktop/Makefile.am 2006-06-04 07:53:09.000000000 -0400
|
|
+++ gnome-desktop-2.16.1/libgnome-desktop/Makefile.am 2006-10-23 16:19:45.000000000 -0400
|
|
@@ -17,9 +17,13 @@ lib_LTLIBRARIES = libgnome-desktop-2.la
|
|
|
|
noinst_PROGRAMS = test-ditem test-hint test-ditem-edit
|
|
|
|
-libgnome_desktop_2_la_SOURCES = \
|
|
- gnome-desktop-item.c \
|
|
- gnome-ditem-edit.c \
|
|
+libgnome_desktop_2_la_SOURCES = \
|
|
+ gnome-desktop-item.c \
|
|
+ gnome-ditem-edit.c \
|
|
+ egg-recent-item.h \
|
|
+ egg-recent-item.c \
|
|
+ egg-recent-model.h \
|
|
+ egg-recent-model.c \
|
|
gnome-hint.c
|
|
|
|
libgnome_desktop_2_la_LIBADD = \
|
|
diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-item.c gnome-desktop-2.16.1/libgnome-desktop/egg-recent-item.c
|
|
--- gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-item.c 1969-12-31 19:00:00.000000000 -0500
|
|
+++ gnome-desktop-2.16.1/libgnome-desktop/egg-recent-item.c 2006-10-23 16:19:45.000000000 -0400
|
|
@@ -0,0 +1,426 @@
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
+/*
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation; either version 2 of the
|
|
+ * License, or (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ *
|
|
+ * Authors:
|
|
+ * James Willcox <jwillcox@cs.indiana.edu>
|
|
+ */
|
|
+
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <glib.h>
|
|
+#include <libgnomevfs/gnome-vfs.h>
|
|
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
|
|
+#include "egg-recent-item.h"
|
|
+
|
|
+
|
|
+
|
|
+EggRecentItem *
|
|
+egg_recent_item_new (void)
|
|
+{
|
|
+ EggRecentItem *item;
|
|
+
|
|
+ item = g_new (EggRecentItem, 1);
|
|
+
|
|
+ item->groups = NULL;
|
|
+ item->private_data = FALSE;
|
|
+ item->uri = NULL;
|
|
+ item->mime_type = NULL;
|
|
+ item->mime_type_is_explicit = FALSE;
|
|
+
|
|
+ item->refcount = 1;
|
|
+
|
|
+ return item;
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_item_free (EggRecentItem *item)
|
|
+{
|
|
+ if (item->uri)
|
|
+ g_free (item->uri);
|
|
+
|
|
+ if (item->mime_type)
|
|
+ g_free (item->mime_type);
|
|
+
|
|
+ if (item->groups) {
|
|
+ g_list_foreach (item->groups, (GFunc)g_free, NULL);
|
|
+ g_list_free (item->groups);
|
|
+ item->groups = NULL;
|
|
+ }
|
|
+
|
|
+ g_free (item);
|
|
+}
|
|
+
|
|
+EggRecentItem *
|
|
+egg_recent_item_ref (EggRecentItem *item)
|
|
+{
|
|
+ item->refcount++;
|
|
+ return item;
|
|
+}
|
|
+
|
|
+EggRecentItem *
|
|
+egg_recent_item_unref (EggRecentItem *item)
|
|
+{
|
|
+ item->refcount--;
|
|
+
|
|
+ if (item->refcount == 0) {
|
|
+ egg_recent_item_free (item);
|
|
+ }
|
|
+
|
|
+ return item;
|
|
+}
|
|
+
|
|
+
|
|
+EggRecentItem *
|
|
+egg_recent_item_new_from_uri (const gchar *uri)
|
|
+{
|
|
+ EggRecentItem *item;
|
|
+
|
|
+ g_return_val_if_fail (uri != NULL, NULL);
|
|
+
|
|
+ item = egg_recent_item_new ();
|
|
+
|
|
+ if (!egg_recent_item_set_uri (item ,uri)) {
|
|
+ egg_recent_item_free (item);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return item;
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_item_update_mime_type (EggRecentItem *item)
|
|
+{
|
|
+ if (!item->mime_type_is_explicit) {
|
|
+ g_free (item->mime_type);
|
|
+ item->mime_type = NULL;
|
|
+
|
|
+ if (item->uri)
|
|
+ item->mime_type = gnome_vfs_get_mime_type (item->uri);
|
|
+
|
|
+ if (!item->mime_type)
|
|
+ item->mime_type = g_strdup (GNOME_VFS_MIME_TYPE_UNKNOWN);
|
|
+ }
|
|
+}
|
|
+
|
|
+gboolean
|
|
+egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri)
|
|
+{
|
|
+ gchar *utf8_uri;
|
|
+
|
|
+ /* if G_BROKEN_FILENAMES is not set, this should succede */
|
|
+ if (g_utf8_validate (uri, -1, NULL)) {
|
|
+ item->uri = gnome_vfs_make_uri_from_input (uri);
|
|
+ } else {
|
|
+ utf8_uri = g_filename_to_utf8 (uri, -1, NULL, NULL, NULL);
|
|
+
|
|
+ if (utf8_uri == NULL) {
|
|
+ g_warning ("Couldn't convert URI to UTF-8");
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ if (g_utf8_validate (utf8_uri, -1, NULL)) {
|
|
+ item->uri = gnome_vfs_make_uri_from_input (utf8_uri);
|
|
+ } else {
|
|
+ g_free (utf8_uri);
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ g_free (utf8_uri);
|
|
+ }
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+gchar *
|
|
+egg_recent_item_get_uri (const EggRecentItem *item)
|
|
+{
|
|
+ return g_strdup (item->uri);
|
|
+}
|
|
+
|
|
+G_CONST_RETURN gchar *
|
|
+egg_recent_item_peek_uri (const EggRecentItem *item)
|
|
+{
|
|
+ return item->uri;
|
|
+}
|
|
+
|
|
+gchar *
|
|
+egg_recent_item_get_uri_utf8 (const EggRecentItem *item)
|
|
+{
|
|
+ /* this could fail, but it's not likely, since we've already done it
|
|
+ * once in set_uri()
|
|
+ */
|
|
+ return g_filename_to_utf8 (item->uri, -1, NULL, NULL, NULL);
|
|
+}
|
|
+
|
|
+gchar *
|
|
+egg_recent_item_get_uri_for_display (const EggRecentItem *item)
|
|
+{
|
|
+ return gnome_vfs_format_uri_for_display (item->uri);
|
|
+}
|
|
+
|
|
+/* Stolen from gnome_vfs_make_valid_utf8() */
|
|
+static char *
|
|
+make_valid_utf8 (const char *name)
|
|
+{
|
|
+ GString *string;
|
|
+ const char *remainder, *invalid;
|
|
+ int remaining_bytes, valid_bytes;
|
|
+
|
|
+ string = NULL;
|
|
+ remainder = name;
|
|
+ remaining_bytes = name ? strlen (name) : 0;
|
|
+
|
|
+ while (remaining_bytes != 0) {
|
|
+ if (g_utf8_validate (remainder, remaining_bytes, &invalid))
|
|
+ break;
|
|
+
|
|
+ valid_bytes = invalid - remainder;
|
|
+
|
|
+ if (string == NULL)
|
|
+ string = g_string_sized_new (remaining_bytes);
|
|
+
|
|
+ g_string_append_len (string, remainder, valid_bytes);
|
|
+ g_string_append_c (string, '?');
|
|
+
|
|
+ remaining_bytes -= valid_bytes + 1;
|
|
+ remainder = invalid + 1;
|
|
+ }
|
|
+
|
|
+ if (string == NULL)
|
|
+ return g_strdup (name);
|
|
+
|
|
+ g_string_append (string, remainder);
|
|
+/* g_string_append (string, _(" (invalid file name)")); */
|
|
+ g_assert (g_utf8_validate (string->str, -1, NULL));
|
|
+
|
|
+ return g_string_free (string, FALSE);
|
|
+}
|
|
+
|
|
+static gchar *
|
|
+get_uri_shortname_for_display (GnomeVFSURI *uri)
|
|
+{
|
|
+ gchar *name;
|
|
+ gboolean validated;
|
|
+
|
|
+ validated = FALSE;
|
|
+ name = gnome_vfs_uri_extract_short_name (uri);
|
|
+
|
|
+ if (name == NULL)
|
|
+ {
|
|
+ name = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD);
|
|
+ }
|
|
+ else if (g_ascii_strcasecmp (uri->method_string, "file") == 0)
|
|
+ {
|
|
+ gchar *text_uri;
|
|
+ gchar *local_file;
|
|
+ text_uri = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD);
|
|
+ local_file = gnome_vfs_get_local_path_from_uri (text_uri);
|
|
+
|
|
+ if (local_file != NULL)
|
|
+ {
|
|
+ g_free (name);
|
|
+ name = g_filename_display_basename (local_file);
|
|
+ validated = TRUE;
|
|
+ }
|
|
+
|
|
+ g_free (local_file);
|
|
+ g_free (text_uri);
|
|
+ }
|
|
+ else if (!gnome_vfs_uri_has_parent (uri))
|
|
+ {
|
|
+ const gchar *method;
|
|
+
|
|
+ method = uri->method_string;
|
|
+
|
|
+ if (name == NULL ||
|
|
+ strcmp (name, GNOME_VFS_URI_PATH_STR) == 0)
|
|
+ {
|
|
+ g_free (name);
|
|
+ name = g_strdup (method);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ gchar *tmp;
|
|
+
|
|
+ tmp = name;
|
|
+ name = g_strdup_printf ("%s: %s", method, name);
|
|
+ g_free (tmp);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!validated && !g_utf8_validate (name, -1, NULL))
|
|
+ {
|
|
+ gchar *utf8_name;
|
|
+
|
|
+ utf8_name = make_valid_utf8 (name);
|
|
+ g_free (name);
|
|
+ name = utf8_name;
|
|
+ }
|
|
+
|
|
+ return name;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * egg_recent_item_get_short_name:
|
|
+ * @item: an #EggRecentItem
|
|
+ *
|
|
+ * Computes a valid UTF-8 string that can be used as the name of the item in a
|
|
+ * menu or list. For example, calling this function on an item that refers to
|
|
+ * "file:///foo/bar.txt" will yield "bar.txt".
|
|
+ *
|
|
+ * Return value: A newly-allocated string in UTF-8 encoding; free it with
|
|
+ * g_free().
|
|
+ **/
|
|
+gchar *
|
|
+egg_recent_item_get_short_name (const EggRecentItem *item)
|
|
+{
|
|
+ GnomeVFSURI *uri;
|
|
+ gchar *short_name;
|
|
+
|
|
+ g_return_val_if_fail (item != NULL, NULL);
|
|
+
|
|
+ if (item->uri == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ uri = gnome_vfs_uri_new (item->uri);
|
|
+ if (uri == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ short_name = get_uri_shortname_for_display (uri);
|
|
+
|
|
+ gnome_vfs_uri_unref (uri);
|
|
+
|
|
+ return short_name;
|
|
+}
|
|
+
|
|
+void
|
|
+egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime)
|
|
+{
|
|
+ g_free (item->mime_type);
|
|
+ item->mime_type = NULL;
|
|
+
|
|
+ if (mime && mime[0]) {
|
|
+ item->mime_type_is_explicit = TRUE;
|
|
+ item->mime_type = g_strdup (mime);
|
|
+ } else {
|
|
+ item->mime_type_is_explicit = FALSE;
|
|
+ }
|
|
+}
|
|
+
|
|
+gchar *
|
|
+egg_recent_item_get_mime_type (EggRecentItem *item)
|
|
+{
|
|
+ egg_recent_item_update_mime_type (item);
|
|
+
|
|
+ return g_strdup (item->mime_type);
|
|
+}
|
|
+
|
|
+void
|
|
+egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp)
|
|
+{
|
|
+ if (timestamp == (time_t) -1)
|
|
+ time (×tamp);
|
|
+
|
|
+ item->timestamp = timestamp;
|
|
+}
|
|
+
|
|
+time_t
|
|
+egg_recent_item_get_timestamp (const EggRecentItem *item)
|
|
+{
|
|
+ return item->timestamp;
|
|
+}
|
|
+
|
|
+G_CONST_RETURN GList *
|
|
+egg_recent_item_get_groups (const EggRecentItem *item)
|
|
+{
|
|
+ return item->groups;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+egg_recent_item_in_group (const EggRecentItem *item, const gchar *group_name)
|
|
+{
|
|
+ GList *tmp;
|
|
+
|
|
+ tmp = item->groups;
|
|
+ while (tmp != NULL) {
|
|
+ gchar *val = (gchar *)tmp->data;
|
|
+
|
|
+ if (strcmp (group_name, val) == 0)
|
|
+ return TRUE;
|
|
+
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+void
|
|
+egg_recent_item_add_group (EggRecentItem *item, const gchar *group_name)
|
|
+{
|
|
+ g_return_if_fail (group_name != NULL);
|
|
+
|
|
+ if (!egg_recent_item_in_group (item, group_name))
|
|
+ item->groups = g_list_append (item->groups, g_strdup (group_name));
|
|
+}
|
|
+
|
|
+void
|
|
+egg_recent_item_remove_group (EggRecentItem *item, const gchar *group_name)
|
|
+{
|
|
+ GList *tmp;
|
|
+
|
|
+ g_return_if_fail (group_name != NULL);
|
|
+
|
|
+ tmp = item->groups;
|
|
+ while (tmp != NULL) {
|
|
+ gchar *val = (gchar *)tmp->data;
|
|
+
|
|
+ if (strcmp (group_name, val) == 0) {
|
|
+ item->groups = g_list_remove (item->groups,
|
|
+ val);
|
|
+ g_free (val);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+egg_recent_item_set_private (EggRecentItem *item, gboolean priv)
|
|
+{
|
|
+ item->private_data = priv;
|
|
+}
|
|
+
|
|
+gboolean
|
|
+egg_recent_item_get_private (const EggRecentItem *item)
|
|
+{
|
|
+ return item->private_data;
|
|
+}
|
|
+
|
|
+GType
|
|
+egg_recent_item_get_type (void)
|
|
+{
|
|
+ static GType boxed_type = 0;
|
|
+
|
|
+ if (!boxed_type) {
|
|
+ boxed_type = g_boxed_type_register_static ("EggRecentItem",
|
|
+ (GBoxedCopyFunc)egg_recent_item_ref,
|
|
+ (GBoxedFreeFunc)egg_recent_item_unref);
|
|
+ }
|
|
+
|
|
+ return boxed_type;
|
|
+}
|
|
diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-item.h gnome-desktop-2.16.1/libgnome-desktop/egg-recent-item.h
|
|
--- gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-item.h 1969-12-31 19:00:00.000000000 -0500
|
|
+++ gnome-desktop-2.16.1/libgnome-desktop/egg-recent-item.h 2006-10-23 16:19:45.000000000 -0400
|
|
@@ -0,0 +1,80 @@
|
|
+
|
|
+#ifndef __EGG_RECENT_ITEM_H__
|
|
+#define __EGG_RECENT_ITEM_H__
|
|
+
|
|
+#include <time.h>
|
|
+#include <glib.h>
|
|
+#include <glib-object.h>
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+#define EGG_TYPE_RECENT_ITEM (egg_recent_item_get_type ())
|
|
+
|
|
+#define EGG_RECENT_ITEM_LIST_UNREF(list) \
|
|
+ g_list_foreach (list, (GFunc)egg_recent_item_unref, NULL); \
|
|
+ g_list_free (list);
|
|
+
|
|
+typedef struct _EggRecentItem EggRecentItem;
|
|
+
|
|
+struct _EggRecentItem {
|
|
+ /* do not access any of these directly */
|
|
+ gchar *uri;
|
|
+ gchar *mime_type;
|
|
+ time_t timestamp;
|
|
+
|
|
+ gboolean private_data;
|
|
+
|
|
+ GList *groups;
|
|
+
|
|
+ int refcount;
|
|
+
|
|
+ guint mime_type_is_explicit : 1;
|
|
+};
|
|
+
|
|
+GType egg_recent_item_get_type (void) G_GNUC_CONST;
|
|
+
|
|
+/* constructors */
|
|
+EggRecentItem * egg_recent_item_new (void);
|
|
+
|
|
+EggRecentItem * egg_recent_item_ref (EggRecentItem *item);
|
|
+EggRecentItem * egg_recent_item_unref (EggRecentItem *item);
|
|
+
|
|
+/* automatically fetches the mime type, etc */
|
|
+EggRecentItem * egg_recent_item_new_from_uri (const gchar *uri);
|
|
+
|
|
+gboolean egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri);
|
|
+gchar * egg_recent_item_get_uri (const EggRecentItem *item);
|
|
+gchar * egg_recent_item_get_uri_utf8 (const EggRecentItem *item);
|
|
+gchar * egg_recent_item_get_uri_for_display (const EggRecentItem *item);
|
|
+gchar * egg_recent_item_get_short_name (const EggRecentItem *item);
|
|
+
|
|
+void egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime);
|
|
+gchar * egg_recent_item_get_mime_type (EggRecentItem *item);
|
|
+
|
|
+void egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp);
|
|
+time_t egg_recent_item_get_timestamp (const EggRecentItem *item);
|
|
+
|
|
+G_CONST_RETURN gchar *egg_recent_item_peek_uri (const EggRecentItem *item);
|
|
+
|
|
+
|
|
+/* groups */
|
|
+G_CONST_RETURN GList * egg_recent_item_get_groups (const EggRecentItem *item);
|
|
+
|
|
+gboolean egg_recent_item_in_group (const EggRecentItem *item,
|
|
+ const gchar *group_name);
|
|
+
|
|
+void egg_recent_item_add_group (EggRecentItem *item,
|
|
+ const gchar *group_name);
|
|
+
|
|
+void egg_recent_item_remove_group (EggRecentItem *item,
|
|
+ const gchar *group_name);
|
|
+
|
|
+void egg_recent_item_set_private (EggRecentItem *item,
|
|
+ gboolean priv);
|
|
+
|
|
+gboolean egg_recent_item_get_private (const EggRecentItem *item);
|
|
+
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __EGG_RECENT_ITEM_H__ */
|
|
diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-model.c gnome-desktop-2.16.1/libgnome-desktop/egg-recent-model.c
|
|
--- gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-model.c 1969-12-31 19:00:00.000000000 -0500
|
|
+++ gnome-desktop-2.16.1/libgnome-desktop/egg-recent-model.c 2006-10-23 16:19:45.000000000 -0400
|
|
@@ -0,0 +1,1965 @@
|
|
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
+/*
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as
|
|
+ * published by the Free Software Foundation; either version 2 of the
|
|
+ * License, or (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
+ *
|
|
+ * Authors:
|
|
+ * James Willcox <jwillcox@cs.indiana.edu>
|
|
+ */
|
|
+
|
|
+#ifdef HAVE_CONFIG_H
|
|
+#include <config.h>
|
|
+#endif
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <errno.h>
|
|
+#include <stdlib.h>
|
|
+#include <unistd.h>
|
|
+#include <fcntl.h>
|
|
+#include <sys/time.h>
|
|
+#include <sys/stat.h>
|
|
+#include <time.h>
|
|
+#include <gtk/gtk.h>
|
|
+#include <libgnomevfs/gnome-vfs.h>
|
|
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
|
|
+#include <gconf/gconf-client.h>
|
|
+
|
|
+#define EGG_ENABLE_RECENT_FILES
|
|
+#include "egg-recent-model.h"
|
|
+#include "egg-recent-item.h"
|
|
+
|
|
+#define EGG_RECENT_MODEL_FILE_PATH "/.recently-used"
|
|
+#define EGG_RECENT_MODEL_BUFFER_SIZE 8192
|
|
+
|
|
+#define EGG_RECENT_MODEL_MAX_ITEMS 500
|
|
+#define EGG_RECENT_MODEL_DEFAULT_LIMIT 10
|
|
+#define EGG_RECENT_MODEL_TIMEOUT_LENGTH 200
|
|
+#define EGG_RECENT_MODEL_POLL_TIME 3
|
|
+
|
|
+/* needed for Darwin */
|
|
+#if !HAVE_DECL_LOCKF
|
|
+int lockf (int filedes, int function, off_t size);
|
|
+#endif
|
|
+
|
|
+#define EGG_RECENT_MODEL_KEY_DIR "/desktop/gnome/recent_files"
|
|
+#define EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY EGG_RECENT_MODEL_KEY_DIR "/default_limit"
|
|
+#define EGG_RECENT_MODEL_EXPIRE_KEY EGG_RECENT_MODEL_KEY_DIR "/expire"
|
|
+
|
|
+struct _EggRecentModelPrivate {
|
|
+ GSList *mime_filter_values; /* list of mime types we allow */
|
|
+ GSList *group_filter_values; /* list of groups we allow */
|
|
+ GSList *scheme_filter_values; /* list of URI schemes we allow */
|
|
+
|
|
+ EggRecentModelSort sort_type; /* type of sorting to be done */
|
|
+
|
|
+ int limit; /* soft limit for length of the list */
|
|
+ int expire_days; /* number of days to hold an item */
|
|
+
|
|
+ char *path; /* path to the file we store stuff in */
|
|
+
|
|
+ GHashTable *monitors;
|
|
+
|
|
+ GnomeVFSMonitorHandle *monitor;
|
|
+
|
|
+ GConfClient *client;
|
|
+ gboolean use_default_limit;
|
|
+
|
|
+ guint limit_change_notify_id;
|
|
+ guint expiration_change_notify_id;
|
|
+
|
|
+ guint changed_timeout;
|
|
+ guint poll_timeout;
|
|
+ time_t last_mtime;
|
|
+};
|
|
+
|
|
+/* signals */
|
|
+enum {
|
|
+ CHANGED,
|
|
+ LAST_SIGNAL
|
|
+};
|
|
+
|
|
+static GType model_signals[LAST_SIGNAL] = { 0 };
|
|
+
|
|
+/* properties */
|
|
+enum {
|
|
+ PROP_BOGUS,
|
|
+ PROP_MIME_FILTERS,
|
|
+ PROP_GROUP_FILTERS,
|
|
+ PROP_SCHEME_FILTERS,
|
|
+ PROP_SORT_TYPE,
|
|
+ PROP_LIMIT
|
|
+};
|
|
+
|
|
+typedef struct {
|
|
+ GSList *states;
|
|
+ GList *items;
|
|
+ EggRecentItem *current_item;
|
|
+} ParseInfo;
|
|
+
|
|
+typedef enum {
|
|
+ STATE_START,
|
|
+ STATE_RECENT_FILES,
|
|
+ STATE_RECENT_ITEM,
|
|
+ STATE_URI,
|
|
+ STATE_MIME_TYPE,
|
|
+ STATE_TIMESTAMP,
|
|
+ STATE_PRIVATE,
|
|
+ STATE_GROUPS,
|
|
+ STATE_GROUP
|
|
+} ParseState;
|
|
+
|
|
+typedef struct {
|
|
+ EggRecentModel *model;
|
|
+ GList *list;
|
|
+} ChangedData;
|
|
+
|
|
+#define TAG_RECENT_FILES "RecentFiles"
|
|
+#define TAG_RECENT_ITEM "RecentItem"
|
|
+#define TAG_URI "URI"
|
|
+#define TAG_MIME_TYPE "Mime-Type"
|
|
+#define TAG_TIMESTAMP "Timestamp"
|
|
+#define TAG_PRIVATE "Private"
|
|
+#define TAG_GROUPS "Groups"
|
|
+#define TAG_GROUP "Group"
|
|
+
|
|
+static void start_element_handler (GMarkupParseContext *context,
|
|
+ const gchar *element_name,
|
|
+ const gchar **attribute_names,
|
|
+ const gchar **attribute_values,
|
|
+ gpointer user_data,
|
|
+ GError **error);
|
|
+
|
|
+static void end_element_handler (GMarkupParseContext *context,
|
|
+ const gchar *element_name,
|
|
+ gpointer user_data,
|
|
+ GError **error);
|
|
+
|
|
+static void text_handler (GMarkupParseContext *context,
|
|
+ const gchar *text,
|
|
+ gsize text_len,
|
|
+ gpointer user_data,
|
|
+ GError **error);
|
|
+
|
|
+static void error_handler (GMarkupParseContext *context,
|
|
+ GError *error,
|
|
+ gpointer user_data);
|
|
+
|
|
+static GMarkupParser parser = {start_element_handler, end_element_handler,
|
|
+ text_handler,
|
|
+ NULL,
|
|
+ error_handler};
|
|
+
|
|
+static GObjectClass *parent_class;
|
|
+
|
|
+static void egg_recent_model_clear_mime_filter (EggRecentModel *model);
|
|
+static void egg_recent_model_clear_group_filter (EggRecentModel *model);
|
|
+static void egg_recent_model_clear_scheme_filter (EggRecentModel *model);
|
|
+
|
|
+static GObjectClass *parent_class;
|
|
+
|
|
+static gboolean
|
|
+egg_recent_model_string_match (const GSList *list, const gchar *str)
|
|
+{
|
|
+ const GSList *tmp;
|
|
+
|
|
+ if (list == NULL || str == NULL)
|
|
+ return TRUE;
|
|
+
|
|
+ tmp = list;
|
|
+
|
|
+ while (tmp) {
|
|
+ if (g_pattern_match_string (tmp->data, str))
|
|
+ return TRUE;
|
|
+
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+egg_recent_model_write_raw (EggRecentModel *model, FILE *file,
|
|
+ const gchar *content)
|
|
+{
|
|
+ int len;
|
|
+ int fd;
|
|
+ struct stat sbuf;
|
|
+
|
|
+ rewind (file);
|
|
+
|
|
+ len = strlen (content);
|
|
+ fd = fileno (file);
|
|
+
|
|
+ if (fstat (fd, &sbuf) < 0)
|
|
+ g_warning ("Couldn't stat XML document.");
|
|
+
|
|
+ if ((off_t)len < sbuf.st_size) {
|
|
+ ftruncate (fd, len);
|
|
+ }
|
|
+
|
|
+ if (fputs (content, file) == EOF)
|
|
+ return FALSE;
|
|
+
|
|
+#ifndef G_OS_WIN32
|
|
+ fsync (fd);
|
|
+#endif
|
|
+ rewind (file);
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static GList *
|
|
+egg_recent_model_delete_from_list (GList *list,
|
|
+ const gchar *uri)
|
|
+{
|
|
+ GList *tmp;
|
|
+
|
|
+ if (!uri)
|
|
+ return list;
|
|
+
|
|
+ tmp = list;
|
|
+
|
|
+ while (tmp) {
|
|
+ EggRecentItem *item = tmp->data;
|
|
+ GList *next;
|
|
+
|
|
+ next = tmp->next;
|
|
+
|
|
+ if (!strcmp (egg_recent_item_peek_uri (item), uri)) {
|
|
+ egg_recent_item_unref (item);
|
|
+
|
|
+ list = g_list_remove_link (list, tmp);
|
|
+ g_list_free_1 (tmp);
|
|
+ }
|
|
+
|
|
+ tmp = next;
|
|
+ }
|
|
+
|
|
+ return list;
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_add_new_groups (EggRecentItem *item,
|
|
+ EggRecentItem *upd_item)
|
|
+{
|
|
+ const GList *tmp;
|
|
+
|
|
+ tmp = egg_recent_item_get_groups (upd_item);
|
|
+
|
|
+ while (tmp) {
|
|
+ char *group = tmp->data;
|
|
+
|
|
+ if (!egg_recent_item_in_group (item, group))
|
|
+ egg_recent_item_add_group (item, group);
|
|
+
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+egg_recent_model_update_item (GList *items, EggRecentItem *upd_item)
|
|
+{
|
|
+ GList *tmp;
|
|
+ const char *uri;
|
|
+
|
|
+ uri = egg_recent_item_peek_uri (upd_item);
|
|
+
|
|
+ tmp = items;
|
|
+
|
|
+ while (tmp) {
|
|
+ EggRecentItem *item = tmp->data;
|
|
+
|
|
+ if (gnome_vfs_uris_match (egg_recent_item_peek_uri (item), uri)) {
|
|
+ egg_recent_item_set_timestamp (item, (time_t) -1);
|
|
+
|
|
+ egg_recent_model_add_new_groups (item, upd_item);
|
|
+
|
|
+ return TRUE;
|
|
+ }
|
|
+
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static gchar *
|
|
+egg_recent_model_read_raw (EggRecentModel *model, FILE *file)
|
|
+{
|
|
+ GString *string;
|
|
+ char buf[EGG_RECENT_MODEL_BUFFER_SIZE];
|
|
+
|
|
+ rewind (file);
|
|
+
|
|
+ string = g_string_new (NULL);
|
|
+ while (fgets (buf, EGG_RECENT_MODEL_BUFFER_SIZE, file)) {
|
|
+ string = g_string_append (string, buf);
|
|
+ }
|
|
+
|
|
+ rewind (file);
|
|
+
|
|
+ return g_string_free (string, FALSE);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static ParseInfo *
|
|
+parse_info_init (void)
|
|
+{
|
|
+ ParseInfo *retval;
|
|
+
|
|
+ retval = g_new0 (ParseInfo, 1);
|
|
+ retval->states = g_slist_prepend (NULL, STATE_START);
|
|
+ retval->items = NULL;
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static void
|
|
+parse_info_free (ParseInfo *info)
|
|
+{
|
|
+ g_slist_free (info->states);
|
|
+ g_free (info);
|
|
+}
|
|
+
|
|
+static void
|
|
+push_state (ParseInfo *info,
|
|
+ ParseState state)
|
|
+{
|
|
+ info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
|
|
+}
|
|
+
|
|
+static void
|
|
+pop_state (ParseInfo *info)
|
|
+{
|
|
+ g_return_if_fail (info->states != NULL);
|
|
+
|
|
+ info->states = g_slist_remove (info->states, info->states->data);
|
|
+}
|
|
+
|
|
+static ParseState
|
|
+peek_state (ParseInfo *info)
|
|
+{
|
|
+ g_return_val_if_fail (info->states != NULL, STATE_START);
|
|
+
|
|
+ return GPOINTER_TO_INT (info->states->data);
|
|
+}
|
|
+
|
|
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
|
|
+
|
|
+static gboolean
|
|
+valid_element (ParseInfo *info,
|
|
+ int valid_parent_state,
|
|
+ const gchar *element_name,
|
|
+ const gchar *valid_element,
|
|
+ GError **error)
|
|
+{
|
|
+ if (peek_state (info) != valid_parent_state) {
|
|
+ g_set_error (error,
|
|
+ G_MARKUP_ERROR,
|
|
+ G_MARKUP_ERROR_INVALID_CONTENT,
|
|
+ "Unexpected tag '%s', tag '%s' expected",
|
|
+ element_name, valid_element);
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static void
|
|
+start_element_handler (GMarkupParseContext *context,
|
|
+ const gchar *element_name,
|
|
+ const gchar **attribute_names,
|
|
+ const gchar **attribute_values,
|
|
+ gpointer user_data,
|
|
+ GError **error)
|
|
+{
|
|
+ ParseInfo *info = (ParseInfo *)user_data;
|
|
+
|
|
+ if (ELEMENT_IS (TAG_RECENT_FILES))
|
|
+ push_state (info, STATE_RECENT_FILES);
|
|
+ else if (ELEMENT_IS (TAG_RECENT_ITEM)) {
|
|
+ if (valid_element (info, STATE_RECENT_FILES,
|
|
+ TAG_RECENT_ITEM, TAG_RECENT_FILES, error)) {
|
|
+ info->current_item = egg_recent_item_new ();
|
|
+ push_state (info, STATE_RECENT_ITEM);
|
|
+ }
|
|
+ } else if (ELEMENT_IS (TAG_URI)) {
|
|
+ if (valid_element (info, STATE_RECENT_ITEM,
|
|
+ TAG_URI, TAG_RECENT_ITEM, error)) {
|
|
+ push_state (info, STATE_URI);
|
|
+ }
|
|
+ } else if (ELEMENT_IS (TAG_MIME_TYPE)) {
|
|
+ if (valid_element (info, STATE_RECENT_ITEM,
|
|
+ TAG_MIME_TYPE, TAG_RECENT_ITEM, error)) {
|
|
+ push_state (info, STATE_MIME_TYPE);
|
|
+ }
|
|
+ } else if (ELEMENT_IS (TAG_TIMESTAMP)) {
|
|
+ if (valid_element (info, STATE_RECENT_ITEM,
|
|
+ TAG_TIMESTAMP, TAG_RECENT_ITEM, error)) {
|
|
+ push_state (info, STATE_TIMESTAMP);
|
|
+ }
|
|
+ } else if (ELEMENT_IS (TAG_PRIVATE)) {
|
|
+ if (valid_element (info, STATE_RECENT_ITEM,
|
|
+ TAG_PRIVATE, TAG_RECENT_ITEM, error)) {
|
|
+ push_state (info, STATE_PRIVATE);
|
|
+ egg_recent_item_set_private (info->current_item, TRUE);
|
|
+ }
|
|
+ } else if (ELEMENT_IS (TAG_GROUPS)) {
|
|
+ if (valid_element (info, STATE_RECENT_ITEM,
|
|
+ TAG_GROUPS, TAG_RECENT_ITEM, error)) {
|
|
+ push_state (info, STATE_GROUPS);
|
|
+ }
|
|
+ } else if (ELEMENT_IS (TAG_GROUP)) {
|
|
+ if (valid_element (info, STATE_GROUPS,
|
|
+ TAG_GROUP, TAG_GROUPS, error)) {
|
|
+ push_state (info, STATE_GROUP);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static gint
|
|
+list_compare_func_mru (gpointer a, gpointer b)
|
|
+{
|
|
+ EggRecentItem *item_a = (EggRecentItem *)a;
|
|
+ EggRecentItem *item_b = (EggRecentItem *)b;
|
|
+
|
|
+ return item_a->timestamp < item_b->timestamp;
|
|
+}
|
|
+
|
|
+static gint
|
|
+list_compare_func_lru (gpointer a, gpointer b)
|
|
+{
|
|
+ EggRecentItem *item_a = (EggRecentItem *)a;
|
|
+ EggRecentItem *item_b = (EggRecentItem *)b;
|
|
+
|
|
+ return item_a->timestamp > item_b->timestamp;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static void
|
|
+end_element_handler (GMarkupParseContext *context,
|
|
+ const gchar *element_name,
|
|
+ gpointer user_data,
|
|
+ GError **error)
|
|
+{
|
|
+ ParseInfo *info = (ParseInfo *)user_data;
|
|
+
|
|
+ switch (peek_state (info)) {
|
|
+ case STATE_RECENT_ITEM:
|
|
+ if (!info->current_item) {
|
|
+ g_warning ("No recent item found\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!info->current_item->uri) {
|
|
+ g_warning ("Invalid item found\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ info->items = g_list_prepend (info->items,
|
|
+ info->current_item);
|
|
+ info->current_item = NULL;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ pop_state (info);
|
|
+}
|
|
+
|
|
+static void
|
|
+text_handler (GMarkupParseContext *context,
|
|
+ const gchar *text,
|
|
+ gsize text_len,
|
|
+ gpointer user_data,
|
|
+ GError **error)
|
|
+{
|
|
+ ParseInfo *info = (ParseInfo *)user_data;
|
|
+ gchar *value;
|
|
+
|
|
+ value = g_strndup (text, text_len);
|
|
+
|
|
+ switch (peek_state (info)) {
|
|
+ case STATE_START:
|
|
+ case STATE_RECENT_FILES:
|
|
+ case STATE_RECENT_ITEM:
|
|
+ case STATE_PRIVATE:
|
|
+ case STATE_GROUPS:
|
|
+ break;
|
|
+ case STATE_URI:
|
|
+ egg_recent_item_set_uri (info->current_item, value);
|
|
+ break;
|
|
+ case STATE_MIME_TYPE:
|
|
+ egg_recent_item_set_mime_type (info->current_item, value);
|
|
+ break;
|
|
+ case STATE_TIMESTAMP:
|
|
+ egg_recent_item_set_timestamp (info->current_item,
|
|
+ (time_t)atoi (value));
|
|
+ break;
|
|
+ case STATE_GROUP:
|
|
+ egg_recent_item_add_group (info->current_item,
|
|
+ text);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ g_free (value);
|
|
+}
|
|
+
|
|
+static void
|
|
+error_handler (GMarkupParseContext *context,
|
|
+ GError *error,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ g_warning ("Error in parse: %s", error->message);
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_enforce_limit (GList *list, int limit)
|
|
+{
|
|
+ int len;
|
|
+ GList *end;
|
|
+
|
|
+ /* limit < 0 means unlimited */
|
|
+ if (limit <= 0)
|
|
+ return;
|
|
+
|
|
+ len = g_list_length (list);
|
|
+
|
|
+ if (len > limit) {
|
|
+ GList *next;
|
|
+
|
|
+ end = g_list_nth (list, limit-1);
|
|
+ next = end->next;
|
|
+
|
|
+ end->next = NULL;
|
|
+
|
|
+ EGG_RECENT_ITEM_LIST_UNREF (next);
|
|
+ }
|
|
+}
|
|
+
|
|
+static GList *
|
|
+egg_recent_model_sort (EggRecentModel *model, GList *list)
|
|
+{
|
|
+ switch (model->priv->sort_type) {
|
|
+ case EGG_RECENT_MODEL_SORT_MRU:
|
|
+ list = g_list_sort (list,
|
|
+ (GCompareFunc)list_compare_func_mru);
|
|
+ break;
|
|
+ case EGG_RECENT_MODEL_SORT_LRU:
|
|
+ list = g_list_sort (list,
|
|
+ (GCompareFunc)list_compare_func_lru);
|
|
+ break;
|
|
+ case EGG_RECENT_MODEL_SORT_NONE:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return list;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+egg_recent_model_group_match (EggRecentItem *item, GSList *groups)
|
|
+{
|
|
+ GSList *tmp;
|
|
+
|
|
+ tmp = groups;
|
|
+
|
|
+ while (tmp != NULL) {
|
|
+ const gchar * group = (const gchar *)tmp->data;
|
|
+
|
|
+ if (egg_recent_item_in_group (item, group))
|
|
+ return TRUE;
|
|
+
|
|
+ tmp = tmp->next;
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static GList *
|
|
+egg_recent_model_filter (EggRecentModel *model, GList *list)
|
|
+{
|
|
+ GList *newlist = NULL;
|
|
+ GList *l;
|
|
+ gchar *mime_type;
|
|
+ gchar *uri;
|
|
+
|
|
+ g_return_val_if_fail (list != NULL, NULL);
|
|
+
|
|
+ for (l = list; l != NULL ; l = l->next) {
|
|
+ EggRecentItem *item = (EggRecentItem *) l->data;
|
|
+ gboolean pass_mime_test = FALSE;
|
|
+ gboolean pass_group_test = FALSE;
|
|
+ gboolean pass_scheme_test = FALSE;
|
|
+
|
|
+ g_assert (item != NULL);
|
|
+
|
|
+ uri = egg_recent_item_get_uri (item);
|
|
+
|
|
+ /* filter by mime type */
|
|
+ if (model->priv->mime_filter_values != NULL) {
|
|
+ mime_type = egg_recent_item_get_mime_type (item);
|
|
+
|
|
+ if (egg_recent_model_string_match
|
|
+ (model->priv->mime_filter_values,
|
|
+ mime_type))
|
|
+ pass_mime_test = TRUE;
|
|
+
|
|
+ g_free (mime_type);
|
|
+ } else
|
|
+ pass_mime_test = TRUE;
|
|
+
|
|
+ /* filter by group */
|
|
+ if (pass_mime_test && model->priv->group_filter_values != NULL) {
|
|
+ if (egg_recent_model_group_match
|
|
+ (item, model->priv->group_filter_values))
|
|
+ pass_group_test = TRUE;
|
|
+ } else if (egg_recent_item_get_private (item)) {
|
|
+ pass_group_test = FALSE;
|
|
+ } else
|
|
+ pass_group_test = TRUE;
|
|
+
|
|
+ /* filter by URI scheme */
|
|
+ if (pass_mime_test && pass_group_test &&
|
|
+ model->priv->scheme_filter_values != NULL) {
|
|
+ gchar *scheme;
|
|
+
|
|
+ scheme = gnome_vfs_get_uri_scheme (uri);
|
|
+
|
|
+ if (egg_recent_model_string_match
|
|
+ (model->priv->scheme_filter_values, scheme))
|
|
+ pass_scheme_test = TRUE;
|
|
+
|
|
+ g_free (scheme);
|
|
+ } else
|
|
+ pass_scheme_test = TRUE;
|
|
+
|
|
+ if (pass_mime_test && pass_group_test && pass_scheme_test)
|
|
+ newlist = g_list_prepend (newlist, item);
|
|
+ else
|
|
+ egg_recent_item_unref (item);
|
|
+
|
|
+ g_free (uri);
|
|
+ }
|
|
+
|
|
+ g_list_free (list);
|
|
+
|
|
+ return g_list_reverse (newlist);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+#if 0
|
|
+static void
|
|
+egg_recent_model_monitor_list_cb (GnomeVFSMonitorHandle *handle,
|
|
+ const gchar *monitor_uri,
|
|
+ const gchar *info_uri,
|
|
+ GnomeVFSMonitorEventType event_type,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ EggRecentModel *model;
|
|
+
|
|
+ model = EGG_RECENT_MODEL (user_data);
|
|
+
|
|
+ if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
|
|
+ egg_recent_model_delete (model, monitor_uri);
|
|
+ g_hash_table_remove (model->priv->monitors, monitor_uri);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static void
|
|
+egg_recent_model_monitor_list (EggRecentModel *model, GList *list)
|
|
+{
|
|
+ GList *tmp;
|
|
+
|
|
+ tmp = list;
|
|
+ while (tmp) {
|
|
+ EggRecentItem *item = (EggRecentItem *)tmp->data;
|
|
+ GnomeVFSMonitorHandle *handle;
|
|
+ GnomeVFSResult res;
|
|
+ gchar *uri;
|
|
+
|
|
+ tmp = tmp->next;
|
|
+
|
|
+ uri = egg_recent_item_get_uri (item);
|
|
+ if (g_hash_table_lookup (model->priv->monitors, uri)) {
|
|
+ /* already monitoring this one */
|
|
+ g_free (uri);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ res = gnome_vfs_monitor_add (&handle, uri,
|
|
+ GNOME_VFS_MONITOR_FILE,
|
|
+ egg_recent_model_monitor_list_cb,
|
|
+ model);
|
|
+
|
|
+ if (res == GNOME_VFS_OK)
|
|
+ g_hash_table_insert (model->priv->monitors, uri, handle);
|
|
+ else
|
|
+ g_free (uri);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+static gboolean
|
|
+egg_recent_model_changed_timeout (EggRecentModel *model)
|
|
+{
|
|
+ model->priv->changed_timeout = 0;
|
|
+
|
|
+ egg_recent_model_changed (model);
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_monitor_cb (GnomeVFSMonitorHandle *handle,
|
|
+ const gchar *monitor_uri,
|
|
+ const gchar *info_uri,
|
|
+ GnomeVFSMonitorEventType event_type,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ EggRecentModel *model;
|
|
+
|
|
+ g_return_if_fail (user_data != NULL);
|
|
+ g_return_if_fail (EGG_IS_RECENT_MODEL (user_data));
|
|
+ model = EGG_RECENT_MODEL (user_data);
|
|
+
|
|
+ if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ||
|
|
+ event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
|
|
+ event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
|
|
+ if (model->priv->changed_timeout > 0) {
|
|
+ g_source_remove (model->priv->changed_timeout);
|
|
+ }
|
|
+
|
|
+ model->priv->changed_timeout = g_timeout_add (
|
|
+ EGG_RECENT_MODEL_TIMEOUT_LENGTH,
|
|
+ (GSourceFunc)egg_recent_model_changed_timeout,
|
|
+ model);
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+egg_recent_model_poll_timeout (gpointer user_data)
|
|
+{
|
|
+ EggRecentModel *model;
|
|
+ struct stat stat_buf;
|
|
+ int stat_res;
|
|
+
|
|
+ model = EGG_RECENT_MODEL (user_data);
|
|
+ stat_res = stat (model->priv->path, &stat_buf);
|
|
+
|
|
+ if (!stat_res && stat_buf.st_mtime &&
|
|
+ stat_buf.st_mtime != model->priv->last_mtime) {
|
|
+ model->priv->last_mtime = stat_buf.st_mtime;
|
|
+
|
|
+ if (model->priv->changed_timeout > 0)
|
|
+ g_source_remove (model->priv->changed_timeout);
|
|
+
|
|
+ model->priv->changed_timeout = g_timeout_add (
|
|
+ EGG_RECENT_MODEL_TIMEOUT_LENGTH,
|
|
+ (GSourceFunc)egg_recent_model_changed_timeout,
|
|
+ model);
|
|
+ }
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_monitor (EggRecentModel *model, gboolean should_monitor)
|
|
+{
|
|
+ if (should_monitor && model->priv->monitor == NULL) {
|
|
+ char *uri;
|
|
+ GnomeVFSResult result;
|
|
+
|
|
+ uri = gnome_vfs_get_uri_from_local_path (model->priv->path);
|
|
+
|
|
+ result = gnome_vfs_monitor_add (&model->priv->monitor,
|
|
+ uri,
|
|
+ GNOME_VFS_MONITOR_FILE,
|
|
+ egg_recent_model_monitor_cb,
|
|
+ model);
|
|
+
|
|
+ g_free (uri);
|
|
+
|
|
+ /* if the above fails, don't worry about it.
|
|
+ * local notifications will still happen
|
|
+ */
|
|
+ if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) {
|
|
+ if (model->priv->poll_timeout > 0)
|
|
+ g_source_remove (model->priv->poll_timeout);
|
|
+
|
|
+ model->priv->poll_timeout = g_timeout_add (
|
|
+ EGG_RECENT_MODEL_POLL_TIME * 1000,
|
|
+ egg_recent_model_poll_timeout,
|
|
+ model);
|
|
+ }
|
|
+
|
|
+ } else if (!should_monitor && model->priv->monitor != NULL) {
|
|
+ gnome_vfs_monitor_cancel (model->priv->monitor);
|
|
+ model->priv->monitor = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_set_limit_internal (EggRecentModel *model, int limit)
|
|
+{
|
|
+ model->priv->limit = limit;
|
|
+
|
|
+ if (limit <= 0)
|
|
+ egg_recent_model_monitor (model, FALSE);
|
|
+ else {
|
|
+ egg_recent_model_monitor (model, TRUE);
|
|
+ egg_recent_model_changed (model);
|
|
+ }
|
|
+}
|
|
+
|
|
+static GList *
|
|
+egg_recent_model_read (EggRecentModel *model, FILE *file)
|
|
+{
|
|
+ GList *list=NULL;
|
|
+ gchar *content;
|
|
+ GMarkupParseContext *ctx;
|
|
+ ParseInfo *info;
|
|
+ GError *error;
|
|
+
|
|
+ content = egg_recent_model_read_raw (model, file);
|
|
+
|
|
+ if (strlen (content) <= 0) {
|
|
+ g_free (content);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ info = parse_info_init ();
|
|
+
|
|
+ ctx = g_markup_parse_context_new (&parser, 0, info, NULL);
|
|
+
|
|
+ error = NULL;
|
|
+ if (!g_markup_parse_context_parse (ctx, content, strlen (content), &error)) {
|
|
+ g_warning ("Error while parsing the .recently-used file: %s\n",
|
|
+ error->message);
|
|
+
|
|
+ g_error_free (error);
|
|
+ parse_info_free (info);
|
|
+
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ error = NULL;
|
|
+ if (!g_markup_parse_context_end_parse (ctx, &error)) {
|
|
+ g_warning ("Unable to complete parsing of the .recently-used file: %s\n",
|
|
+ error->message);
|
|
+
|
|
+ g_error_free (error);
|
|
+ g_markup_parse_context_free (ctx);
|
|
+ parse_info_free (info);
|
|
+
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ list = g_list_reverse (info->items);
|
|
+
|
|
+ g_markup_parse_context_free (ctx);
|
|
+ parse_info_free (info);
|
|
+ g_free (content);
|
|
+
|
|
+ return list;
|
|
+}
|
|
+
|
|
+
|
|
+static gboolean
|
|
+egg_recent_model_write (EggRecentModel *model, FILE *file, GList *list)
|
|
+{
|
|
+ GString *string;
|
|
+ gchar *data;
|
|
+ EggRecentItem *item;
|
|
+ const GList *groups;
|
|
+ int i;
|
|
+ int ret;
|
|
+
|
|
+ string = g_string_new ("<?xml version=\"1.0\"?>\n");
|
|
+ string = g_string_append (string, "<" TAG_RECENT_FILES ">\n");
|
|
+
|
|
+ i=0;
|
|
+ while (list) {
|
|
+ gchar *uri;
|
|
+ gchar *mime_type;
|
|
+ gchar *escaped_uri;
|
|
+ time_t timestamp;
|
|
+ item = (EggRecentItem *)list->data;
|
|
+
|
|
+
|
|
+ uri = egg_recent_item_get_uri_utf8 (item);
|
|
+ escaped_uri = g_markup_escape_text (uri,
|
|
+ strlen (uri));
|
|
+ g_free (uri);
|
|
+
|
|
+ mime_type = egg_recent_item_get_mime_type (item);
|
|
+ timestamp = egg_recent_item_get_timestamp (item);
|
|
+
|
|
+ string = g_string_append (string, " <" TAG_RECENT_ITEM ">\n");
|
|
+
|
|
+ g_string_append_printf (string,
|
|
+ " <" TAG_URI ">%s</" TAG_URI ">\n", escaped_uri);
|
|
+
|
|
+ if (mime_type)
|
|
+ g_string_append_printf (string,
|
|
+ " <" TAG_MIME_TYPE ">%s</" TAG_MIME_TYPE ">\n", mime_type);
|
|
+ else
|
|
+ g_string_append_printf (string,
|
|
+ " <" TAG_MIME_TYPE "></" TAG_MIME_TYPE ">\n");
|
|
+
|
|
+
|
|
+ g_string_append_printf (string,
|
|
+ " <" TAG_TIMESTAMP ">%d</" TAG_TIMESTAMP ">\n", (int)timestamp);
|
|
+
|
|
+ if (egg_recent_item_get_private (item))
|
|
+ string = g_string_append (string,
|
|
+ " <" TAG_PRIVATE "/>\n");
|
|
+
|
|
+ /* write the groups */
|
|
+ string = g_string_append (string,
|
|
+ " <" TAG_GROUPS ">\n");
|
|
+ groups = egg_recent_item_get_groups (item);
|
|
+
|
|
+ if (groups == NULL && egg_recent_item_get_private (item))
|
|
+ g_warning ("Item with URI \"%s\" marked as private, but"
|
|
+ " does not belong to any groups.\n", uri);
|
|
+
|
|
+ while (groups) {
|
|
+ const gchar *group = (const gchar *)groups->data;
|
|
+ gchar *escaped_group;
|
|
+
|
|
+ escaped_group = g_markup_escape_text (group, strlen(group));
|
|
+
|
|
+ g_string_append_printf (string,
|
|
+ " <" TAG_GROUP ">%s</" TAG_GROUP ">\n",
|
|
+ escaped_group);
|
|
+
|
|
+ g_free (escaped_group);
|
|
+
|
|
+ groups = groups->next;
|
|
+ }
|
|
+
|
|
+ string = g_string_append (string, " </" TAG_GROUPS ">\n");
|
|
+
|
|
+ string = g_string_append (string,
|
|
+ " </" TAG_RECENT_ITEM ">\n");
|
|
+
|
|
+ g_free (mime_type);
|
|
+ g_free (escaped_uri);
|
|
+
|
|
+ list = list->next;
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ string = g_string_append (string, "</" TAG_RECENT_FILES ">");
|
|
+
|
|
+ data = g_string_free (string, FALSE);
|
|
+
|
|
+ ret = egg_recent_model_write_raw (model, file, data);
|
|
+
|
|
+ g_free (data);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static FILE *
|
|
+egg_recent_model_open_file (EggRecentModel *model,
|
|
+ gboolean for_writing)
|
|
+{
|
|
+ FILE *file;
|
|
+ mode_t prev_umask;
|
|
+
|
|
+ file = fopen (model->priv->path, "r+");
|
|
+ if (file == NULL && for_writing) {
|
|
+ /* be paranoid */
|
|
+ prev_umask = umask (077);
|
|
+
|
|
+ file = fopen (model->priv->path, "w+");
|
|
+
|
|
+ umask (prev_umask);
|
|
+
|
|
+ g_return_val_if_fail (file != NULL, NULL);
|
|
+ }
|
|
+
|
|
+ return file;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+egg_recent_model_lock_file (FILE *file)
|
|
+{
|
|
+#ifdef HAVE_LOCKF
|
|
+ int fd;
|
|
+ gint try = 5;
|
|
+
|
|
+ rewind (file);
|
|
+ fd = fileno (file);
|
|
+
|
|
+ /* Attempt to lock the file 5 times,
|
|
+ * waiting a random interval (< 1 second)
|
|
+ * in between attempts.
|
|
+ * We should really be doing asynchronous
|
|
+ * locking, but requires substantially larger
|
|
+ * changes.
|
|
+ */
|
|
+
|
|
+ while (try > 0)
|
|
+ {
|
|
+ int rand_interval;
|
|
+
|
|
+ if (lockf (fd, F_TLOCK, 0) == 0)
|
|
+ return TRUE;
|
|
+
|
|
+ rand_interval = 1 + (int) (10.0 * rand()/(RAND_MAX + 1.0));
|
|
+
|
|
+ g_usleep (100000 * rand_interval);
|
|
+
|
|
+ --try;
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+#else
|
|
+ return TRUE;
|
|
+#endif /* HAVE_LOCKF */
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+egg_recent_model_unlock_file (FILE *file)
|
|
+{
|
|
+#ifdef HAVE_LOCKF
|
|
+ int fd;
|
|
+
|
|
+ rewind (file);
|
|
+ fd = fileno (file);
|
|
+
|
|
+ return (lockf (fd, F_ULOCK, 0) == 0) ? TRUE : FALSE;
|
|
+#else
|
|
+ return TRUE;
|
|
+#endif /* HAVE_LOCKF */
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_finalize (GObject *object)
|
|
+{
|
|
+ EggRecentModel *model = EGG_RECENT_MODEL (object);
|
|
+
|
|
+ if (model->priv->changed_timeout > 0) {
|
|
+ g_source_remove (model->priv->changed_timeout);
|
|
+ }
|
|
+
|
|
+ egg_recent_model_monitor (model, FALSE);
|
|
+
|
|
+
|
|
+ g_slist_foreach (model->priv->mime_filter_values,
|
|
+ (GFunc) g_pattern_spec_free, NULL);
|
|
+ g_slist_free (model->priv->mime_filter_values);
|
|
+ model->priv->mime_filter_values = NULL;
|
|
+
|
|
+ g_slist_foreach (model->priv->scheme_filter_values,
|
|
+ (GFunc) g_pattern_spec_free, NULL);
|
|
+ g_slist_free (model->priv->scheme_filter_values);
|
|
+ model->priv->scheme_filter_values = NULL;
|
|
+
|
|
+ g_slist_foreach (model->priv->group_filter_values,
|
|
+ (GFunc) g_free, NULL);
|
|
+ g_slist_free (model->priv->group_filter_values);
|
|
+ model->priv->group_filter_values = NULL;
|
|
+
|
|
+
|
|
+ if (model->priv->limit_change_notify_id)
|
|
+ gconf_client_notify_remove (model->priv->client,
|
|
+ model->priv->limit_change_notify_id);
|
|
+ model->priv->expiration_change_notify_id = 0;
|
|
+
|
|
+ if (model->priv->expiration_change_notify_id)
|
|
+ gconf_client_notify_remove (model->priv->client,
|
|
+ model->priv->expiration_change_notify_id);
|
|
+ model->priv->expiration_change_notify_id = 0;
|
|
+
|
|
+ g_object_unref (model->priv->client);
|
|
+ model->priv->client = NULL;
|
|
+
|
|
+
|
|
+ g_free (model->priv->path);
|
|
+ model->priv->path = NULL;
|
|
+
|
|
+ g_hash_table_destroy (model->priv->monitors);
|
|
+ model->priv->monitors = NULL;
|
|
+
|
|
+ if (model->priv->poll_timeout > 0)
|
|
+ g_source_remove (model->priv->poll_timeout);
|
|
+ model->priv->poll_timeout =0;
|
|
+
|
|
+ g_free (model->priv);
|
|
+
|
|
+ parent_class->finalize (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_set_property (GObject *object,
|
|
+ guint prop_id,
|
|
+ const GValue *value,
|
|
+ GParamSpec *pspec)
|
|
+{
|
|
+ EggRecentModel *model = EGG_RECENT_MODEL (object);
|
|
+
|
|
+ switch (prop_id)
|
|
+ {
|
|
+ case PROP_MIME_FILTERS:
|
|
+ if (model->priv->mime_filter_values != NULL)
|
|
+ egg_recent_model_clear_mime_filter (model);
|
|
+
|
|
+ model->priv->mime_filter_values =
|
|
+ (GSList *)g_value_get_pointer (value);
|
|
+ break;
|
|
+
|
|
+ case PROP_GROUP_FILTERS:
|
|
+ if (model->priv->group_filter_values != NULL)
|
|
+ egg_recent_model_clear_group_filter (model);
|
|
+
|
|
+ model->priv->group_filter_values =
|
|
+ (GSList *)g_value_get_pointer (value);
|
|
+ break;
|
|
+
|
|
+ case PROP_SCHEME_FILTERS:
|
|
+ if (model->priv->scheme_filter_values != NULL)
|
|
+ egg_recent_model_clear_scheme_filter (model);
|
|
+
|
|
+ model->priv->scheme_filter_values =
|
|
+ (GSList *)g_value_get_pointer (value);
|
|
+ break;
|
|
+
|
|
+ case PROP_SORT_TYPE:
|
|
+ model->priv->sort_type = g_value_get_int (value);
|
|
+ break;
|
|
+
|
|
+ case PROP_LIMIT:
|
|
+ egg_recent_model_set_limit (model,
|
|
+ g_value_get_int (value));
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_get_property (GObject *object,
|
|
+ guint prop_id,
|
|
+ GValue *value,
|
|
+ GParamSpec *pspec)
|
|
+{
|
|
+ EggRecentModel *model = EGG_RECENT_MODEL (object);
|
|
+
|
|
+ switch (prop_id)
|
|
+ {
|
|
+ case PROP_MIME_FILTERS:
|
|
+ g_value_set_pointer (value, model->priv->mime_filter_values);
|
|
+ break;
|
|
+
|
|
+ case PROP_GROUP_FILTERS:
|
|
+ g_value_set_pointer (value, model->priv->group_filter_values);
|
|
+ break;
|
|
+
|
|
+ case PROP_SCHEME_FILTERS:
|
|
+ g_value_set_pointer (value, model->priv->scheme_filter_values);
|
|
+ break;
|
|
+
|
|
+ case PROP_SORT_TYPE:
|
|
+ g_value_set_int (value, model->priv->sort_type);
|
|
+ break;
|
|
+
|
|
+ case PROP_LIMIT:
|
|
+ g_value_set_int (value, model->priv->limit);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_class_init (EggRecentModelClass * klass)
|
|
+{
|
|
+ GObjectClass *object_class;
|
|
+
|
|
+ parent_class = g_type_class_peek_parent (klass);
|
|
+
|
|
+ parent_class = g_type_class_peek_parent (klass);
|
|
+
|
|
+ object_class = G_OBJECT_CLASS (klass);
|
|
+ object_class->set_property = egg_recent_model_set_property;
|
|
+ object_class->get_property = egg_recent_model_get_property;
|
|
+ object_class->finalize = egg_recent_model_finalize;
|
|
+
|
|
+ model_signals[CHANGED] = g_signal_new ("changed",
|
|
+ G_OBJECT_CLASS_TYPE (object_class),
|
|
+ G_SIGNAL_RUN_LAST,
|
|
+ G_STRUCT_OFFSET (EggRecentModelClass, changed),
|
|
+ NULL, NULL,
|
|
+ g_cclosure_marshal_VOID__POINTER,
|
|
+ G_TYPE_NONE, 1,
|
|
+ G_TYPE_POINTER);
|
|
+
|
|
+
|
|
+ g_object_class_install_property (object_class,
|
|
+ PROP_MIME_FILTERS,
|
|
+ g_param_spec_pointer ("mime-filters",
|
|
+ "Mime Filters",
|
|
+ "List of mime types to be allowed.",
|
|
+ G_PARAM_READWRITE));
|
|
+
|
|
+ g_object_class_install_property (object_class,
|
|
+ PROP_GROUP_FILTERS,
|
|
+ g_param_spec_pointer ("group-filters",
|
|
+ "Group Filters",
|
|
+ "List of groups to be allowed.",
|
|
+ G_PARAM_READWRITE));
|
|
+
|
|
+ g_object_class_install_property (object_class,
|
|
+ PROP_SCHEME_FILTERS,
|
|
+ g_param_spec_pointer ("scheme-filters",
|
|
+ "Scheme Filters",
|
|
+ "List of URI schemes to be allowed.",
|
|
+ G_PARAM_READWRITE));
|
|
+
|
|
+ g_object_class_install_property (object_class,
|
|
+ PROP_SORT_TYPE,
|
|
+ g_param_spec_int ("sort-type",
|
|
+ "Sort Type",
|
|
+ "Type of sorting to be done.",
|
|
+ 0, EGG_RECENT_MODEL_SORT_NONE,
|
|
+ EGG_RECENT_MODEL_SORT_MRU,
|
|
+ G_PARAM_READWRITE));
|
|
+
|
|
+ g_object_class_install_property (object_class,
|
|
+ PROP_LIMIT,
|
|
+ g_param_spec_int ("limit",
|
|
+ "Limit",
|
|
+ "Max number of items allowed.",
|
|
+ -1, EGG_RECENT_MODEL_MAX_ITEMS,
|
|
+ EGG_RECENT_MODEL_DEFAULT_LIMIT,
|
|
+ G_PARAM_READWRITE));
|
|
+
|
|
+ klass->changed = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static void
|
|
+egg_recent_model_limit_changed (GConfClient *client, guint cnxn_id,
|
|
+ GConfEntry *entry, gpointer user_data)
|
|
+{
|
|
+ EggRecentModel *model;
|
|
+ GConfValue *value;
|
|
+
|
|
+ model = EGG_RECENT_MODEL (user_data);
|
|
+
|
|
+ g_return_if_fail (model != NULL);
|
|
+
|
|
+ if (model->priv->use_default_limit == FALSE)
|
|
+ return; /* ignore this key */
|
|
+
|
|
+ /* the key was unset, and the schema has apparently failed */
|
|
+ if (entry == NULL)
|
|
+ return;
|
|
+
|
|
+ value = gconf_entry_get_value (entry);
|
|
+
|
|
+ if (value->type != GCONF_VALUE_INT) {
|
|
+ g_warning ("Expected GConfValue of type integer, "
|
|
+ "got something else");
|
|
+ }
|
|
+
|
|
+
|
|
+ egg_recent_model_set_limit_internal (model, gconf_value_get_int (value));
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_expiration_changed (GConfClient *client, guint cnxn_id,
|
|
+ GConfEntry *entry, gpointer user_data)
|
|
+{
|
|
+
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_init (EggRecentModel * model)
|
|
+{
|
|
+ if (!gnome_vfs_init ()) {
|
|
+ g_warning ("gnome-vfs initialization failed.");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+
|
|
+ model->priv = g_new0 (EggRecentModelPrivate, 1);
|
|
+
|
|
+ model->priv->path = g_strdup_printf ("%s" EGG_RECENT_MODEL_FILE_PATH,
|
|
+ g_get_home_dir ());
|
|
+
|
|
+ model->priv->mime_filter_values = NULL;
|
|
+ model->priv->group_filter_values = NULL;
|
|
+ model->priv->scheme_filter_values = NULL;
|
|
+
|
|
+ model->priv->client = gconf_client_get_default ();
|
|
+ gconf_client_add_dir (model->priv->client, EGG_RECENT_MODEL_KEY_DIR,
|
|
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
|
+
|
|
+ model->priv->limit_change_notify_id =
|
|
+ gconf_client_notify_add (model->priv->client,
|
|
+ EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY,
|
|
+ egg_recent_model_limit_changed,
|
|
+ model, NULL, NULL);
|
|
+
|
|
+ model->priv->expiration_change_notify_id =
|
|
+ gconf_client_notify_add (model->priv->client,
|
|
+ EGG_RECENT_MODEL_EXPIRE_KEY,
|
|
+ egg_recent_model_expiration_changed,
|
|
+ model, NULL, NULL);
|
|
+
|
|
+ model->priv->expire_days = gconf_client_get_int (
|
|
+ model->priv->client,
|
|
+ EGG_RECENT_MODEL_EXPIRE_KEY,
|
|
+ NULL);
|
|
+
|
|
+#if 0
|
|
+ /* keep this out, for now */
|
|
+ model->priv->limit = gconf_client_get_int (
|
|
+ model->priv->client,
|
|
+ EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY, NULL);
|
|
+ model->priv->use_default_limit = TRUE;
|
|
+#endif
|
|
+ model->priv->limit = EGG_RECENT_MODEL_DEFAULT_LIMIT;
|
|
+ model->priv->use_default_limit = FALSE;
|
|
+
|
|
+ model->priv->monitors = g_hash_table_new_full (
|
|
+ g_str_hash, g_str_equal,
|
|
+ (GDestroyNotify) g_free,
|
|
+ (GDestroyNotify) gnome_vfs_monitor_cancel);
|
|
+
|
|
+ model->priv->monitor = NULL;
|
|
+ model->priv->poll_timeout = 0;
|
|
+ model->priv->last_mtime = 0;
|
|
+ egg_recent_model_monitor (model, TRUE);
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_new:
|
|
+ * @sort: the type of sorting to use
|
|
+ * @limit: maximum number of items in the list
|
|
+ *
|
|
+ * This creates a new EggRecentModel object.
|
|
+ *
|
|
+ * Returns: a EggRecentModel object
|
|
+ */
|
|
+EggRecentModel *
|
|
+egg_recent_model_new (EggRecentModelSort sort)
|
|
+{
|
|
+ EggRecentModel *model;
|
|
+
|
|
+ model = EGG_RECENT_MODEL (g_object_new (egg_recent_model_get_type (),
|
|
+ "sort-type", sort, NULL));
|
|
+
|
|
+ g_return_val_if_fail (model, NULL);
|
|
+
|
|
+ return model;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_add_full:
|
|
+ * @model: A EggRecentModel object.
|
|
+ * @item: A EggRecentItem
|
|
+ *
|
|
+ * This function adds an item to the list of recently used URIs.
|
|
+ *
|
|
+ * Returns: gboolean
|
|
+ */
|
|
+gboolean
|
|
+egg_recent_model_add_full (EggRecentModel * model, EggRecentItem *item)
|
|
+{
|
|
+ FILE *file;
|
|
+ GList *list = NULL;
|
|
+ gboolean ret = FALSE;
|
|
+ gboolean updated = FALSE;
|
|
+ char *uri;
|
|
+ time_t t;
|
|
+
|
|
+ g_return_val_if_fail (model != NULL, FALSE);
|
|
+ g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE);
|
|
+
|
|
+ uri = egg_recent_item_get_uri (item);
|
|
+ if (strncmp (uri, "recent-files://", strlen ("recent-files://")) == 0) {
|
|
+ g_free (uri);
|
|
+ return FALSE;
|
|
+ } else {
|
|
+ g_free (uri);
|
|
+ }
|
|
+
|
|
+ file = egg_recent_model_open_file (model, TRUE);
|
|
+ g_return_val_if_fail (file != NULL, FALSE);
|
|
+
|
|
+ time (&t);
|
|
+ egg_recent_item_set_timestamp (item, t);
|
|
+
|
|
+ if (egg_recent_model_lock_file (file)) {
|
|
+
|
|
+ /* read existing stuff */
|
|
+ list = egg_recent_model_read (model, file);
|
|
+
|
|
+ /* if it's already there, we just update it */
|
|
+ updated = egg_recent_model_update_item (list, item);
|
|
+
|
|
+ if (!updated) {
|
|
+ list = g_list_prepend (list, item);
|
|
+
|
|
+ egg_recent_model_enforce_limit (list,
|
|
+ EGG_RECENT_MODEL_MAX_ITEMS);
|
|
+ }
|
|
+
|
|
+ /* write new stuff */
|
|
+ if (!egg_recent_model_write (model, file, list))
|
|
+ g_warning ("Write failed: %s", strerror (errno));
|
|
+
|
|
+ if (!updated)
|
|
+ list = g_list_remove (list, item);
|
|
+
|
|
+ EGG_RECENT_ITEM_LIST_UNREF (list);
|
|
+ ret = TRUE;
|
|
+ } else {
|
|
+ g_warning ("Failed to lock: %s", strerror (errno));
|
|
+ fclose (file);
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ if (!egg_recent_model_unlock_file (file))
|
|
+ g_warning ("Failed to unlock: %s", strerror (errno));
|
|
+
|
|
+ fclose (file);
|
|
+
|
|
+ if (model->priv->monitor == NULL) {
|
|
+ /* since monitoring isn't working, at least give a
|
|
+ * local notification
|
|
+ */
|
|
+ egg_recent_model_changed (model);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_add:
|
|
+ * @model: A EggRecentModel object.
|
|
+ * @uri: A string URI
|
|
+ *
|
|
+ * This function adds an item to the list of recently used URIs.
|
|
+ *
|
|
+ * Returns: gboolean
|
|
+ */
|
|
+gboolean
|
|
+egg_recent_model_add (EggRecentModel *model, const gchar *uri)
|
|
+{
|
|
+ EggRecentItem *item;
|
|
+ gboolean ret = FALSE;
|
|
+
|
|
+ g_return_val_if_fail (model != NULL, FALSE);
|
|
+ g_return_val_if_fail (uri != NULL, FALSE);
|
|
+
|
|
+ item = egg_recent_item_new_from_uri (uri);
|
|
+
|
|
+ g_return_val_if_fail (item != NULL, FALSE);
|
|
+
|
|
+ ret = egg_recent_model_add_full (model, item);
|
|
+
|
|
+ egg_recent_item_unref (item);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_delete:
|
|
+ * @model: A EggRecentModel object.
|
|
+ * @uri: The URI you want to delete.
|
|
+ *
|
|
+ * This function deletes a URI from the file of recently used URIs.
|
|
+ *
|
|
+ * Returns: gboolean
|
|
+ */
|
|
+gboolean
|
|
+egg_recent_model_delete (EggRecentModel * model, const gchar * uri)
|
|
+{
|
|
+ FILE *file;
|
|
+ GList *list;
|
|
+ unsigned int length;
|
|
+ gboolean ret = FALSE;
|
|
+
|
|
+ g_return_val_if_fail (model != NULL, FALSE);
|
|
+ g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE);
|
|
+ g_return_val_if_fail (uri != NULL, FALSE);
|
|
+
|
|
+ file = egg_recent_model_open_file (model, TRUE);
|
|
+ g_return_val_if_fail (file != NULL, FALSE);
|
|
+
|
|
+ if (egg_recent_model_lock_file (file)) {
|
|
+ list = egg_recent_model_read (model, file);
|
|
+
|
|
+ if (list == NULL)
|
|
+ goto out;
|
|
+
|
|
+ length = g_list_length (list);
|
|
+
|
|
+ list = egg_recent_model_delete_from_list (list, uri);
|
|
+
|
|
+ if (length == g_list_length (list)) {
|
|
+ /* nothing was deleted */
|
|
+ EGG_RECENT_ITEM_LIST_UNREF (list);
|
|
+ } else {
|
|
+ egg_recent_model_write (model, file, list);
|
|
+ EGG_RECENT_ITEM_LIST_UNREF (list);
|
|
+ ret = TRUE;
|
|
+
|
|
+ }
|
|
+ } else {
|
|
+ g_warning ("Failed to lock: %s", strerror (errno));
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+out:
|
|
+
|
|
+ if (!egg_recent_model_unlock_file (file))
|
|
+ g_warning ("Failed to unlock: %s", strerror (errno));
|
|
+
|
|
+ fclose (file);
|
|
+
|
|
+ g_hash_table_remove (model->priv->monitors, uri);
|
|
+
|
|
+ if (model->priv->monitor == NULL && ret) {
|
|
+ /* since monitoring isn't working, at least give a
|
|
+ * local notification
|
|
+ */
|
|
+ egg_recent_model_changed (model);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_get_list:
|
|
+ * @model: A EggRecentModel object.
|
|
+ *
|
|
+ * This function gets the current contents of the file
|
|
+ *
|
|
+ * Returns: a GList
|
|
+ */
|
|
+GList *
|
|
+egg_recent_model_get_list (EggRecentModel *model)
|
|
+{
|
|
+ FILE *file;
|
|
+ GList *list = NULL;
|
|
+
|
|
+ file = egg_recent_model_open_file (model, FALSE);
|
|
+ if (file == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ if (egg_recent_model_lock_file (file))
|
|
+ list = egg_recent_model_read (model, file);
|
|
+ else {
|
|
+ g_warning ("Failed to lock: %s", strerror (errno));
|
|
+ fclose (file);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (!egg_recent_model_unlock_file (file))
|
|
+ g_warning ("Failed to unlock: %s", strerror (errno));
|
|
+
|
|
+ if (list != NULL) {
|
|
+ list = egg_recent_model_filter (model, list);
|
|
+ list = egg_recent_model_sort (model, list);
|
|
+
|
|
+ egg_recent_model_enforce_limit (list, model->priv->limit);
|
|
+ }
|
|
+
|
|
+ fclose (file);
|
|
+
|
|
+ return list;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_set_limit:
|
|
+ * @model: A EggRecentModel object.
|
|
+ * @limit: The maximum length of the list
|
|
+ *
|
|
+ * This function sets the maximum length of the list. Note: This only affects
|
|
+ * the length of the list emitted in the "changed" signal, not the list stored
|
|
+ * on disk.
|
|
+ *
|
|
+ * Returns: void
|
|
+ */
|
|
+void
|
|
+egg_recent_model_set_limit (EggRecentModel *model, int limit)
|
|
+{
|
|
+ model->priv->use_default_limit = FALSE;
|
|
+
|
|
+ egg_recent_model_set_limit_internal (model, limit);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_get_limit:
|
|
+ * @model: A EggRecentModel object.
|
|
+ *
|
|
+ * This function gets the maximum length of the list.
|
|
+ *
|
|
+ * Returns: int
|
|
+ */
|
|
+int
|
|
+egg_recent_model_get_limit (EggRecentModel *model)
|
|
+{
|
|
+ return model->priv->limit;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_clear:
|
|
+ * @model: A EggRecentModel object.
|
|
+ *
|
|
+ * This function clears the contents of the file
|
|
+ *
|
|
+ * Returns: void
|
|
+ */
|
|
+void
|
|
+egg_recent_model_clear (EggRecentModel *model)
|
|
+{
|
|
+ FILE *file;
|
|
+ int fd;
|
|
+
|
|
+ file = egg_recent_model_open_file (model, TRUE);
|
|
+ g_return_if_fail (file != NULL);
|
|
+
|
|
+ fd = fileno (file);
|
|
+
|
|
+ if (egg_recent_model_lock_file (file)) {
|
|
+ ftruncate (fd, 0);
|
|
+ } else {
|
|
+ g_warning ("Failed to lock: %s", strerror (errno));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!egg_recent_model_unlock_file (file))
|
|
+ g_warning ("Failed to unlock: %s", strerror (errno));
|
|
+
|
|
+ fclose (file);
|
|
+
|
|
+ if (model->priv->monitor == NULL) {
|
|
+ /* since monitoring isn't working, at least give a
|
|
+ * local notification
|
|
+ */
|
|
+ egg_recent_model_changed (model);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_clear_mime_filter (EggRecentModel *model)
|
|
+{
|
|
+ g_return_if_fail (model != NULL);
|
|
+
|
|
+ if (model->priv->mime_filter_values != NULL) {
|
|
+ g_slist_foreach (model->priv->mime_filter_values,
|
|
+ (GFunc) g_pattern_spec_free, NULL);
|
|
+ g_slist_free (model->priv->mime_filter_values);
|
|
+ model->priv->mime_filter_values = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_set_filter_mime_types:
|
|
+ * @model: A EggRecentModel object.
|
|
+ *
|
|
+ * Sets which mime types are allowed in the list.
|
|
+ *
|
|
+ * Returns: void
|
|
+ */
|
|
+void
|
|
+egg_recent_model_set_filter_mime_types (EggRecentModel *model,
|
|
+ ...)
|
|
+{
|
|
+ va_list valist;
|
|
+ GSList *list = NULL;
|
|
+ gchar *str;
|
|
+
|
|
+ g_return_if_fail (model != NULL);
|
|
+
|
|
+ egg_recent_model_clear_mime_filter (model);
|
|
+
|
|
+ va_start (valist, model);
|
|
+
|
|
+ str = va_arg (valist, gchar*);
|
|
+
|
|
+ while (str != NULL) {
|
|
+ list = g_slist_prepend (list, g_pattern_spec_new (str));
|
|
+
|
|
+ str = va_arg (valist, gchar*);
|
|
+ }
|
|
+
|
|
+ va_end (valist);
|
|
+
|
|
+ model->priv->mime_filter_values = list;
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_clear_group_filter (EggRecentModel *model)
|
|
+{
|
|
+ g_return_if_fail (model != NULL);
|
|
+
|
|
+ if (model->priv->group_filter_values != NULL) {
|
|
+ g_slist_foreach (model->priv->group_filter_values, (GFunc)g_free, NULL);
|
|
+ g_slist_free (model->priv->group_filter_values);
|
|
+ model->priv->group_filter_values = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_set_filter_groups:
|
|
+ * @model: A EggRecentModel object.
|
|
+ *
|
|
+ * Sets which groups are allowed in the list.
|
|
+ *
|
|
+ * Returns: void
|
|
+ */
|
|
+void
|
|
+egg_recent_model_set_filter_groups (EggRecentModel *model,
|
|
+ ...)
|
|
+{
|
|
+ va_list valist;
|
|
+ GSList *list = NULL;
|
|
+ gchar *str;
|
|
+
|
|
+ g_return_if_fail (model != NULL);
|
|
+
|
|
+ egg_recent_model_clear_group_filter (model);
|
|
+
|
|
+ va_start (valist, model);
|
|
+
|
|
+ str = va_arg (valist, gchar*);
|
|
+
|
|
+ while (str != NULL) {
|
|
+ list = g_slist_prepend (list, g_strdup (str));
|
|
+
|
|
+ str = va_arg (valist, gchar*);
|
|
+ }
|
|
+
|
|
+ va_end (valist);
|
|
+
|
|
+ model->priv->group_filter_values = list;
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_clear_scheme_filter (EggRecentModel *model)
|
|
+{
|
|
+ g_return_if_fail (model != NULL);
|
|
+
|
|
+ if (model->priv->scheme_filter_values != NULL) {
|
|
+ g_slist_foreach (model->priv->scheme_filter_values,
|
|
+ (GFunc) g_pattern_spec_free, NULL);
|
|
+ g_slist_free (model->priv->scheme_filter_values);
|
|
+ model->priv->scheme_filter_values = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_set_filter_uri_schemes:
|
|
+ * @model: A EggRecentModel object.
|
|
+ *
|
|
+ * Sets which URI schemes (file, http, ftp, etc) are allowed in the list.
|
|
+ *
|
|
+ * Returns: void
|
|
+ */
|
|
+void
|
|
+egg_recent_model_set_filter_uri_schemes (EggRecentModel *model, ...)
|
|
+{
|
|
+ va_list valist;
|
|
+ GSList *list = NULL;
|
|
+ gchar *str;
|
|
+
|
|
+ g_return_if_fail (model != NULL);
|
|
+
|
|
+ egg_recent_model_clear_scheme_filter (model);
|
|
+
|
|
+ va_start (valist, model);
|
|
+
|
|
+ str = va_arg (valist, gchar*);
|
|
+
|
|
+ while (str != NULL) {
|
|
+ list = g_slist_prepend (list, g_pattern_spec_new (str));
|
|
+
|
|
+ str = va_arg (valist, gchar*);
|
|
+ }
|
|
+
|
|
+ va_end (valist);
|
|
+
|
|
+ model->priv->scheme_filter_values = list;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_set_sort:
|
|
+ * @model: A EggRecentModel object.
|
|
+ * @sort: A EggRecentModelSort type
|
|
+ *
|
|
+ * Sets the type of sorting to be used.
|
|
+ *
|
|
+ * Returns: void
|
|
+ */
|
|
+void
|
|
+egg_recent_model_set_sort (EggRecentModel *model,
|
|
+ EggRecentModelSort sort)
|
|
+{
|
|
+ g_return_if_fail (model != NULL);
|
|
+
|
|
+ model->priv->sort_type = sort;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_changed:
|
|
+ * @model: A EggRecentModel object.
|
|
+ *
|
|
+ * This function causes a "changed" signal to be emitted.
|
|
+ *
|
|
+ * Returns: void
|
|
+ */
|
|
+void
|
|
+egg_recent_model_changed (EggRecentModel *model)
|
|
+{
|
|
+ GList *list = NULL;
|
|
+
|
|
+ if (model->priv->limit > 0) {
|
|
+ list = egg_recent_model_get_list (model);
|
|
+ /* egg_recent_model_monitor_list (model, list); */
|
|
+
|
|
+ g_signal_emit (G_OBJECT (model), model_signals[CHANGED], 0,
|
|
+ list);
|
|
+ }
|
|
+
|
|
+ if (list)
|
|
+ EGG_RECENT_ITEM_LIST_UNREF (list);
|
|
+}
|
|
+
|
|
+static void
|
|
+egg_recent_model_remove_expired_list (EggRecentModel *model, GList *list)
|
|
+{
|
|
+ time_t current_time;
|
|
+ time_t day_seconds;
|
|
+
|
|
+ time (¤t_time);
|
|
+ day_seconds = model->priv->expire_days*24*60*60;
|
|
+
|
|
+ while (list != NULL) {
|
|
+ EggRecentItem *item = list->data;
|
|
+ time_t timestamp;
|
|
+
|
|
+ timestamp = egg_recent_item_get_timestamp (item);
|
|
+
|
|
+ if ((timestamp+day_seconds) < current_time) {
|
|
+ gchar *uri = egg_recent_item_get_uri (item);
|
|
+ egg_recent_model_delete (model, uri);
|
|
+
|
|
+ g_strdup (uri);
|
|
+ }
|
|
+
|
|
+ list = list->next;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_remove_expired:
|
|
+ * @model: A EggRecentModel object.
|
|
+ *
|
|
+ * Goes through the entire list, and removes any items that are older than
|
|
+ * the user-specified expiration period.
|
|
+ *
|
|
+ * Returns: void
|
|
+ */
|
|
+void
|
|
+egg_recent_model_remove_expired (EggRecentModel *model)
|
|
+{
|
|
+ FILE *file;
|
|
+ GList *list=NULL;
|
|
+
|
|
+ g_return_if_fail (model != NULL);
|
|
+
|
|
+ file = egg_recent_model_open_file (model, FALSE);
|
|
+ if (file == NULL)
|
|
+ return;
|
|
+
|
|
+ if (egg_recent_model_lock_file (file)) {
|
|
+ list = egg_recent_model_read (model, file);
|
|
+
|
|
+ } else {
|
|
+ g_warning ("Failed to lock: %s", strerror (errno));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!egg_recent_model_unlock_file (file))
|
|
+ g_warning ("Failed to unlock: %s", strerror (errno));
|
|
+
|
|
+ if (list != NULL) {
|
|
+ egg_recent_model_remove_expired_list (model, list);
|
|
+ EGG_RECENT_ITEM_LIST_UNREF (list);
|
|
+ }
|
|
+
|
|
+ fclose (file);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * egg_recent_model_get_type:
|
|
+ *
|
|
+ * This returns a GType representing a EggRecentModel object.
|
|
+ *
|
|
+ * Returns: a GType
|
|
+ */
|
|
+GType
|
|
+egg_recent_model_get_type (void)
|
|
+{
|
|
+ static GType egg_recent_model_type = 0;
|
|
+
|
|
+ if(!egg_recent_model_type) {
|
|
+ static const GTypeInfo egg_recent_model_info = {
|
|
+ sizeof (EggRecentModelClass),
|
|
+ NULL, /* base init */
|
|
+ NULL, /* base finalize */
|
|
+ (GClassInitFunc)egg_recent_model_class_init, /* class init */
|
|
+ NULL, /* class finalize */
|
|
+ NULL, /* class data */
|
|
+ sizeof (EggRecentModel),
|
|
+ 0,
|
|
+ (GInstanceInitFunc) egg_recent_model_init
|
|
+ };
|
|
+
|
|
+ egg_recent_model_type = g_type_register_static (G_TYPE_OBJECT,
|
|
+ "EggRecentModel",
|
|
+ &egg_recent_model_info, 0);
|
|
+ }
|
|
+
|
|
+ return egg_recent_model_type;
|
|
+}
|
|
+
|
|
diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-model.h gnome-desktop-2.16.1/libgnome-desktop/egg-recent-model.h
|
|
--- gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-model.h 1969-12-31 19:00:00.000000000 -0500
|
|
+++ gnome-desktop-2.16.1/libgnome-desktop/egg-recent-model.h 2006-10-23 16:19:45.000000000 -0400
|
|
@@ -0,0 +1,84 @@
|
|
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
+#ifndef __EGG_RECENT_MODEL_H__
|
|
+#define __EGG_RECENT_MODEL_H__
|
|
+
|
|
+#include "egg-recent-item.h"
|
|
+
|
|
+#ifndef EGG_ENABLE_RECENT_FILES
|
|
+#error "EggRecent has been DEPRECATED by the new recent files code inside GTK 2.9.0. Use EGG_ENABLE_RECENT_FILES to compile this code, but you are using it at your own risk."
|
|
+#endif
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+#define EGG_TYPE_RECENT_MODEL (egg_recent_model_get_type ())
|
|
+#define EGG_RECENT_MODEL(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, EGG_TYPE_RECENT_MODEL, EggRecentModel)
|
|
+#define EGG_RECENT_MODEL_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, EGG_TYPE_RECENT_MODEL, EggRecentModelClass)
|
|
+#define EGG_IS_RECENT_MODEL(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_model_get_type ())
|
|
+
|
|
+typedef struct _EggRecentModel EggRecentModel;
|
|
+typedef struct _EggRecentModelPrivate EggRecentModelPrivate;
|
|
+typedef struct _EggRecentModelClass EggRecentModelClass;
|
|
+
|
|
+struct _EggRecentModel {
|
|
+ GObject parent_instance;
|
|
+
|
|
+ EggRecentModelPrivate *priv;
|
|
+};
|
|
+
|
|
+struct _EggRecentModelClass {
|
|
+ GObjectClass parent_class;
|
|
+
|
|
+ void (*changed) (EggRecentModel *model, GList *list);
|
|
+};
|
|
+
|
|
+typedef enum {
|
|
+ EGG_RECENT_MODEL_SORT_MRU,
|
|
+ EGG_RECENT_MODEL_SORT_LRU,
|
|
+ EGG_RECENT_MODEL_SORT_NONE
|
|
+} EggRecentModelSort;
|
|
+
|
|
+
|
|
+/* Standard group names */
|
|
+#define EGG_RECENT_GROUP_LAUNCHERS "Launchers"
|
|
+
|
|
+
|
|
+GType egg_recent_model_get_type (void);
|
|
+
|
|
+/* constructors */
|
|
+EggRecentModel * egg_recent_model_new (EggRecentModelSort sort);
|
|
+
|
|
+/* public methods */
|
|
+void egg_recent_model_set_filter_mime_types (EggRecentModel *model,
|
|
+ ...);
|
|
+
|
|
+void egg_recent_model_set_filter_groups (EggRecentModel *model, ...);
|
|
+
|
|
+void egg_recent_model_set_filter_uri_schemes (EggRecentModel *model,
|
|
+ ...);
|
|
+
|
|
+void egg_recent_model_set_sort (EggRecentModel *model,
|
|
+ EggRecentModelSort sort);
|
|
+
|
|
+gboolean egg_recent_model_add_full (EggRecentModel *model,
|
|
+ EggRecentItem *item);
|
|
+
|
|
+gboolean egg_recent_model_add (EggRecentModel *model,
|
|
+ const gchar *uri);
|
|
+
|
|
+gboolean egg_recent_model_delete (EggRecentModel *model,
|
|
+ const gchar *uri);
|
|
+
|
|
+void egg_recent_model_clear (EggRecentModel *model);
|
|
+
|
|
+GList * egg_recent_model_get_list (EggRecentModel *model);
|
|
+
|
|
+void egg_recent_model_changed (EggRecentModel *model);
|
|
+
|
|
+void egg_recent_model_set_limit (EggRecentModel *model, int limit);
|
|
+int egg_recent_model_get_limit (EggRecentModel *model);
|
|
+
|
|
+void egg_recent_model_remove_expired (EggRecentModel *model);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __EGG_RECENT_MODEL_H__ */
|
|
diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/gnome-desktop-item.c gnome-desktop-2.16.1/libgnome-desktop/gnome-desktop-item.c
|
|
--- gnome-desktop-2.16.1-pristine/libgnome-desktop/gnome-desktop-item.c 2006-08-05 07:59:08.000000000 -0400
|
|
+++ gnome-desktop-2.16.1/libgnome-desktop/gnome-desktop-item.c 2006-10-23 17:15:41.000000000 -0400
|
|
@@ -59,6 +59,16 @@
|
|
#include <gtk/gtk.h>
|
|
#endif
|
|
|
|
+#include <gtk/gtkversion.h>
|
|
+#if GTK_CHECK_VERSION (2,10,0)
|
|
+# define USE_GTK_RECENT_MANAGER
|
|
+# include <gtk/gtkrecentmanager.h>
|
|
+#else
|
|
+# include "egg-recent-item.h"
|
|
+# define EGG_ENABLE_RECENT_FILES
|
|
+# include "egg-recent-model.h"
|
|
+#endif
|
|
+
|
|
#define sure_string(s) ((s)!=NULL?(s):"")
|
|
|
|
struct _GnomeDesktopItem {
|
|
@@ -128,6 +138,8 @@ static gboolean ditem_save (Gno
|
|
const char *uri,
|
|
GError **error);
|
|
|
|
+static void update_recently_used_apps (const GnomeDesktopItem *item);
|
|
+
|
|
static int
|
|
readbuf_getc (ReadBuf *rb)
|
|
{
|
|
@@ -2103,6 +2115,8 @@ gnome_desktop_item_launch_on_screen_with
|
|
(flags & GNOME_DESKTOP_ITEM_LAUNCH_APPEND_PATHS),
|
|
error);
|
|
|
|
+ update_recently_used_apps (item);
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
@@ -4098,3 +4112,71 @@ gnome_desktop_item_error_quark (void)
|
|
|
|
return q;
|
|
}
|
|
+
|
|
+static void
|
|
+update_recently_used_apps (const GnomeDesktopItem *item)
|
|
+{
|
|
+#ifdef USE_GTK_RECENT_MANAGER
|
|
+ GtkRecentManager *manager;
|
|
+ GtkRecentData recent_data;
|
|
+#else
|
|
+ EggRecentModel *model;
|
|
+ EggRecentItem *recent_item;
|
|
+#endif
|
|
+
|
|
+
|
|
+ if (! item)
|
|
+ return;
|
|
+
|
|
+#ifdef USE_GTK_RECENT_MANAGER
|
|
+ manager = gtk_recent_manager_get_default ();
|
|
+
|
|
+ if (! manager)
|
|
+ return;
|
|
+
|
|
+ recent_data.display_name = NULL;
|
|
+ recent_data.description = NULL;
|
|
+ recent_data.mime_type = g_strdup ("application/x-desktop");
|
|
+ recent_data.is_private = TRUE;
|
|
+
|
|
+ recent_data.app_name = g_strdup (g_get_application_name ());
|
|
+ if (! recent_data.app_name)
|
|
+ recent_data.app_name = g_strdup ("libgnomedesktop");
|
|
+
|
|
+ recent_data.app_exec = g_strdup (gnome_desktop_item_get_string (item, GNOME_DESKTOP_ITEM_EXEC));
|
|
+ if (! recent_data.app_exec)
|
|
+ recent_data.app_exec = g_strdup ("gnome-open %u");
|
|
+
|
|
+ recent_data.groups = g_new0 (gchar *, 2);
|
|
+ recent_data.groups [0] = g_strdup ("recently-used-apps");
|
|
+ recent_data.groups [1] = NULL;
|
|
+
|
|
+ gtk_recent_manager_add_full (manager, gnome_desktop_item_get_location (item), & recent_data);
|
|
+
|
|
+ g_free (recent_data.mime_type);
|
|
+ g_free (recent_data.app_name);
|
|
+ g_free (recent_data.app_exec);
|
|
+ g_free (recent_data.groups [0]);
|
|
+ g_free (recent_data.groups);
|
|
+
|
|
+#else
|
|
+
|
|
+ model = egg_recent_model_new (EGG_RECENT_MODEL_SORT_MRU);
|
|
+
|
|
+ if (! model)
|
|
+ return;
|
|
+
|
|
+ recent_item = egg_recent_item_new_from_uri (gnome_desktop_item_get_location (item));
|
|
+
|
|
+ if (recent_item) {
|
|
+ egg_recent_item_add_group (recent_item, "recently-used-apps");
|
|
+ egg_recent_item_set_private (recent_item, TRUE);
|
|
+
|
|
+ egg_recent_model_add_full (model, recent_item);
|
|
+
|
|
+ egg_recent_item_unref (recent_item);
|
|
+ }
|
|
+
|
|
+ g_object_unref (G_OBJECT (model));
|
|
+#endif
|
|
+}
|