[gvfs] Add a recent files backend



commit e3890ddd806a7e2f89ac17d61a4aac0fa4eb7006
Author: William Jon McCann <jmccann redhat com>
Date:   Tue Jul 10 15:05:10 2012 -0400

    Add a recent files backend
    
    https://bugzilla.gnome.org/show_bug.cgi?id=679719

 configure.ac               |   25 ++
 daemon/Makefile.am         |   20 ++
 daemon/gvfsbackendrecent.c |  686 ++++++++++++++++++++++++++++++++++++++++++++
 daemon/gvfsbackendrecent.h |   25 ++
 daemon/recent.mount.in     |    5 +
 5 files changed, 761 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 5052afb..2f3260c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -558,6 +558,30 @@ AC_SUBST(SAMBA_LIBS)
 dnl ==========================================================================
 
 dnl ****************************
+dnl *** Check for GTK        ***
+dnl ****************************
+
+AC_ARG_ENABLE(gtk, AS_HELP_STRING([--disable-gtk],[build without GTK+]))
+msg_gtk=no
+GTK_LIBS=
+GTK_CFLAGS=
+GTK_REQUIRED=3.0
+
+if test "x$enable_gtk" != "xno"; then
+  PKG_CHECK_EXISTS([gtk+-3.0 >= $GTK_REQUIRED], msg_gtk=yes)
+
+  if test "x$msg_gtk" = "xyes"; then
+    PKG_CHECK_MODULES([GTK],[gtk+-3.0 >= $GTK_REQUIRED])
+    AC_DEFINE(HAVE_GTK, 1, [Define to 1 if GTK+ is available])
+  fi
+fi
+
+AM_CONDITIONAL(USE_GTK, [test "$msg_gtk" = "yes"])
+
+AC_SUBST(GTK_CFLAGS)
+AC_SUBST(GTK_LIBS)
+
+dnl ****************************
 dnl *** Check for libarchive ***
 dnl ****************************
 
@@ -824,6 +848,7 @@ echo "
 	Build udisks2 volume monitor: $msg_udisks2
         Use libsystemd-login:         $msg_libsystemd_login
 	GNOME Keyring support:        $msg_keyring
+	GTK+ support:                 $msg_gtk
 	Bash-completion support:      $msg_bash_completion
 "
 
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 55cf029..b6e999c 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -44,6 +44,12 @@ libexec_PROGRAMS=gvfsd gvfsd-sftp gvfsd-trash gvfsd-computer gvfsd-burn gvfsd-lo
 mount_in_files = sftp.mount.in ftp.mount.in trash.mount.in computer.mount.in burn.mount.in localtest.mount.in network.mount.in
 mount_DATA =  sftp.mount ftp.mount trash.mount computer.mount burn.mount localtest.mount network.mount
 
+mount_in_files +=recent.mount.in
+if USE_GTK
+mount_DATA += recent.mount
+libexec_PROGRAMS += gvfsd-recent
+endif
+
 mount_in_files += http.mount.in dav.mount.in dav+sd.mount.in
 if HAVE_HTTP
 mount_DATA += http.mount dav.mount
@@ -306,6 +312,20 @@ gvfsd_trash_CPPFLAGS = \
 
 gvfsd_trash_LDADD = trashlib/libtrash.a $(libraries)
 
+gvfsd_recent_SOURCES = \
+	gvfsbackendrecent.c gvfsbackendrecent.h \
+	daemon-main.c daemon-main.h \
+	daemon-main-generic.c
+
+gvfsd_recent_CPPFLAGS = \
+	-DBACKEND_HEADER=gvfsbackendrecent.h \
+	-DDEFAULT_BACKEND_TYPE=recent \
+	-DMAX_JOB_THREADS=10 \
+	-DBACKEND_TYPES='"recent", G_VFS_TYPE_BACKEND_RECENT,' \
+	$(GTK_CFLAGS)
+
+gvfsd_recent_LDADD = $(libraries) $(GTK_LIBS)
+
 gvfsd_computer_SOURCES = \
 	gvfsbackendcomputer.c gvfsbackendcomputer.h \
 	daemon-main.c daemon-main.h \
diff --git a/daemon/gvfsbackendrecent.c b/daemon/gvfsbackendrecent.c
new file mode 100644
index 0000000..2ff8df3
--- /dev/null
+++ b/daemon/gvfsbackendrecent.c
@@ -0,0 +1,686 @@
+/*
+ * Copyright  2012 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#include "gvfsbackendrecent.h"
+
+#include <glib/gi18n.h> /* _() */
+#include <gtk/gtk.h>
+#include <string.h>
+
+#include "gvfsjobcreatemonitor.h"
+#include "gvfsjobopenforread.h"
+#include "gvfsjobqueryfsinfo.h"
+#include "gvfsjobqueryinfo.h"
+#include "gvfsjobenumerate.h"
+#include "gvfsjobseekread.h"
+#include "gvfsjobread.h"
+
+typedef GVfsBackendClass GVfsBackendRecentClass;
+
+typedef struct {
+  char *guid;
+  char *uri;
+  char *display_name;
+  GFile *file;
+} RecentItem;
+
+struct OPAQUE_TYPE__GVfsBackendRecent
+{
+  GVfsBackend parent_instance;
+
+  GtkRecentManager *recent_manager;
+  GHashTable *uri_map;
+  GHashTable *items;
+
+  GVfsMonitor *file_monitor;
+  GVfsMonitor *dir_monitor;
+};
+
+G_DEFINE_TYPE (GVfsBackendRecent, g_vfs_backend_recent, G_VFS_TYPE_BACKEND);
+
+static GVfsMonitor *
+recent_backend_get_file_monitor (GVfsBackendRecent *backend,
+                                 gboolean           create)
+{
+  if (backend->file_monitor == NULL && create == FALSE)
+    return NULL;
+
+  else if (backend->file_monitor == NULL)
+    {
+      /* 'create' is only ever set in the main thread, so we will have
+       * no possibility here for creating more than one new monitor.
+       */
+      /* FIXME */
+
+      backend->file_monitor = g_vfs_monitor_new (G_VFS_BACKEND (backend));
+    }
+
+  return g_object_ref (backend->file_monitor);
+}
+
+static GVfsMonitor *
+recent_backend_get_dir_monitor (GVfsBackendRecent *backend,
+                                gboolean           create)
+{
+  if (backend->dir_monitor == NULL && create == FALSE)
+    return NULL;
+
+  else if (backend->dir_monitor == NULL)
+    {
+      backend->dir_monitor = g_vfs_monitor_new (G_VFS_BACKEND (backend));
+    }
+
+  return g_object_ref (backend->dir_monitor);
+}
+
+static GFile *
+recent_backend_get_file (GVfsBackendRecent *backend,
+                         const char        *filename,
+                         RecentItem       **item_ret,
+                         GError           **error)
+{
+  GFile *file = NULL;
+  RecentItem *item;
+
+  filename++;
+
+  item = g_hash_table_lookup (backend->items, filename);
+  if (item)
+    {
+      file = g_object_ref (item->file);
+      if (item_ret)
+        *item_ret = item;
+    }
+
+  if (file == NULL)
+    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                         _("No such file or directory"));
+
+  return file;
+}
+
+/* ======================= method implementations ======================= */
+static gboolean
+recent_backend_open_for_read (GVfsBackend        *vfs_backend,
+                              GVfsJobOpenForRead *job,
+                              const char         *filename)
+{
+  GVfsBackendRecent *backend = G_VFS_BACKEND_RECENT (vfs_backend);
+  GError *error = NULL;
+
+  if (filename[1] == '\0')
+    g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+                         _("Can't open directory"));
+
+  else
+    {
+      GFile *real;
+
+      real = recent_backend_get_file (backend, filename, NULL, &error);
+
+      if (real)
+        {
+          GFileInputStream *stream;
+
+          stream = g_file_read (real, G_VFS_JOB (job)->cancellable, &error);
+          g_object_unref (real);
+
+          if (stream)
+            {
+              g_vfs_job_open_for_read_set_handle (job, stream);
+              g_vfs_job_open_for_read_set_can_seek (job, TRUE);
+              g_vfs_job_succeeded (G_VFS_JOB (job));
+
+              return TRUE;
+            }
+        }
+    }
+
+  g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+  g_error_free (error);
+
+  return TRUE;
+}
+
+static gboolean
+recent_backend_read (GVfsBackend       *vfs_backend,
+                     GVfsJobRead       *job,
+                     GVfsBackendHandle  handle,
+                     char              *buffer,
+                     gsize              bytes_requested)
+{
+  GError *error = NULL;
+  gssize bytes;
+
+  bytes = g_input_stream_read (handle, buffer, bytes_requested,
+                               G_VFS_JOB (job)->cancellable, &error);
+
+  if (bytes >= 0)
+    {
+      g_vfs_job_read_set_size (job, bytes);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+
+      return TRUE;
+    }
+
+  g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+  g_error_free (error);
+
+  return TRUE;
+}
+
+static gboolean
+recent_backend_seek_on_read (GVfsBackend       *vfs_backend,
+                             GVfsJobSeekRead   *job,
+                             GVfsBackendHandle  handle,
+                             goffset            offset,
+                             GSeekType          type)
+{
+  GError *error = NULL;
+
+  if (g_seekable_seek (handle, offset, type, NULL, &error))
+    {
+      g_vfs_job_seek_read_set_offset (job, g_seekable_tell (handle));
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+
+      return TRUE;
+    }
+
+  g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+  g_error_free (error);
+
+  return TRUE;
+}
+
+static gboolean
+recent_backend_close_read (GVfsBackend       *vfs_backend,
+                           GVfsJobCloseRead  *job,
+                           GVfsBackendHandle  handle)
+{
+  GError *error = NULL;
+
+  if (g_input_stream_close (handle, G_VFS_JOB (job)->cancellable, &error))
+    {
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+      g_object_unref (handle);
+
+      return TRUE;
+    }
+
+  g_object_unref (handle);
+
+  g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+  g_error_free (error);
+
+  return TRUE;
+}
+
+static gboolean
+recent_backend_delete (GVfsBackend   *vfs_backend,
+                       GVfsJobDelete *job,
+                       const char    *filename)
+{
+  GVfsBackendRecent *backend = G_VFS_BACKEND_RECENT (vfs_backend);
+  GError *error = NULL;
+  g_debug ("before job: %d\n", G_OBJECT(job)->ref_count);
+
+  if (filename[1] == '\0')
+    {
+      g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                           _("The recent folder may not be deleted"));
+    }
+  else
+    {
+      RecentItem *item;
+
+      item = g_hash_table_lookup (backend->items, filename + 1);
+      if (item)
+        {
+          gboolean res;
+          res = gtk_recent_manager_remove_item (backend->recent_manager,
+                                                item->uri,
+                                                &error);
+          if (res)
+            {
+              g_vfs_job_succeeded (G_VFS_JOB (job));
+              return TRUE;
+            }
+        }
+      else
+        g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                             _("No such file or directory"));
+    }
+
+  g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+  g_error_free (error);
+
+  return TRUE;
+}
+
+static void
+recent_backend_add_info (RecentItem *item,
+                         GFileInfo  *info)
+{
+  g_assert (item != NULL);
+
+  g_file_info_set_name (info, item->guid);
+  g_file_info_set_display_name (info, item->display_name);
+  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, item->uri);
+
+  g_file_info_set_attribute_boolean (info,
+                                     G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+                                     FALSE);
+  g_file_info_set_attribute_boolean (info,
+                                     G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
+                                     FALSE);
+  g_file_info_set_attribute_boolean (info,
+                                     G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME,
+                                     FALSE);
+  g_file_info_set_attribute_boolean (info,
+                                     G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
+                                     FALSE);
+  g_file_info_set_attribute_boolean (info,
+                                     G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
+                                     TRUE);
+}
+
+static gboolean
+recent_backend_enumerate (GVfsBackend           *vfs_backend,
+                          GVfsJobEnumerate      *job,
+                          const char            *filename,
+                          GFileAttributeMatcher *attribute_matcher,
+                          GFileQueryInfoFlags    flags)
+{
+  GVfsBackendRecent *backend = G_VFS_BACKEND_RECENT (vfs_backend);
+  GHashTableIter iter;
+  gpointer key, value;
+
+  g_assert (filename[0] == '/');
+
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+
+  g_hash_table_iter_init (&iter, backend->items);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      RecentItem *item = value;
+      GFileInfo *info;
+
+      info = g_file_query_info (item->file,
+                                job->attributes,
+                                flags,
+                                G_VFS_JOB (job)->cancellable,
+                                NULL);
+      if (info)
+        {
+          g_file_info_set_attribute_mask (info, attribute_matcher);
+          recent_backend_add_info (item, info);
+          g_vfs_job_enumerate_add_info (job, info);
+        }
+    }
+  g_vfs_job_enumerate_done (job);
+
+  return TRUE;
+}
+
+static void
+recent_item_free (RecentItem *item)
+{
+  g_free (item->uri);
+  g_free (item->display_name);
+  g_free (item->guid);
+  g_clear_object (&item->file);
+  g_free (item);
+}
+
+static gboolean
+recent_item_update (RecentItem    *item,
+                    GtkRecentInfo *info)
+{
+  gboolean changed = FALSE;
+  const char *uri;
+  const char *display_name;
+
+  uri = gtk_recent_info_get_uri (info);
+  if (g_strcmp0 (item->uri, uri) != 0)
+    {
+      changed = TRUE;
+      g_free (item->uri);
+      item->uri = g_strdup (uri);
+
+      g_clear_object (&item->file);
+      item->file = g_file_new_for_uri (item->uri);
+    }
+
+  display_name = gtk_recent_info_get_display_name (info);
+  if (g_strcmp0 (item->display_name, display_name) != 0)
+    {
+      changed = TRUE;
+      g_free (item->display_name);
+      item->display_name = g_strdup (display_name);
+    }
+
+  return changed;
+}
+
+static RecentItem *
+recent_item_new (GtkRecentInfo *info)
+{
+  RecentItem *item;
+  item = g_new0 (RecentItem, 1);
+  item->guid = g_dbus_generate_guid ();
+
+  recent_item_update (item, info);
+
+  return item;
+}
+
+static void
+reload_recent_items (GVfsBackendRecent *backend)
+{
+  GVfsMonitor *monitor;
+  GList *items;
+  GList *node;
+  GList *added = NULL;
+  GList *changed = NULL;
+  GList *not_seen_items = NULL;
+  GList *l;
+
+  not_seen_items = g_hash_table_get_values (backend->items);
+  items = gtk_recent_manager_get_items (backend->recent_manager);
+  for (node = items; node; node = node->next)
+    {
+      GtkRecentInfo *recent_info = node->data;
+      const char *uri;
+      const char *guid;
+
+      if (!gtk_recent_info_is_local (recent_info)
+          || g_strcmp0 (gtk_recent_info_get_mime_type (recent_info), "inode/directory") == 0)
+        continue;
+
+      uri = gtk_recent_info_get_uri (recent_info);
+      guid = g_hash_table_lookup (backend->uri_map, uri);
+      if (guid)
+        {
+          if (gtk_recent_info_exists (recent_info))
+            {
+              RecentItem *item;
+              item = g_hash_table_lookup (backend->items, guid);
+              if (recent_item_update (item, recent_info))
+                changed = g_list_prepend (changed, item->guid);
+              not_seen_items = g_list_remove (not_seen_items, item);
+            }
+        }
+      else
+        {
+          RecentItem *item;
+          item = recent_item_new (recent_info);
+          added = g_list_prepend (added, item->guid);
+          g_hash_table_insert (backend->items, item->guid, item);
+          g_hash_table_insert (backend->uri_map, item->uri, item->guid);
+        }
+      gtk_recent_info_unref (recent_info);
+    }
+
+  g_list_free (items);
+
+  monitor = recent_backend_get_dir_monitor (backend, FALSE);
+
+  /* process removals */
+  for (l = not_seen_items; l; l = l->next)
+    {
+      RecentItem *item = l->data;
+      g_hash_table_remove (backend->uri_map, item->uri);
+      g_hash_table_steal (backend->items, item->guid);
+      if (monitor)
+        g_vfs_monitor_emit_event (monitor, G_FILE_MONITOR_EVENT_DELETED, item->guid, NULL);
+      recent_item_free (item);
+    }
+  g_list_free (not_seen_items);
+
+  /* process additions */
+  if (monitor)
+    {
+      for (l = added; l; l = l->next)
+        g_vfs_monitor_emit_event (monitor, G_FILE_MONITOR_EVENT_CREATED, l->data, NULL);
+    }
+  g_list_free (added);
+
+  /* process changes */
+  for (l = changed; l; l = l->next)
+    {
+      /* FIXME: signals */
+    }
+  g_list_free (changed);
+
+  if (monitor)
+    g_object_unref (monitor);
+}
+
+static void
+on_recent_manager_changed (GtkRecentManager  *manager,
+                           GVfsBackendRecent *backend)
+{
+  reload_recent_items (backend);
+}
+
+static gboolean
+recent_backend_mount (GVfsBackend  *vfs_backend,
+                      GVfsJobMount *job,
+                      GMountSpec   *mount_spec,
+                      GMountSource *mount_source,
+                      gboolean      is_automount)
+{
+  GVfsBackendRecent *backend = G_VFS_BACKEND_RECENT (vfs_backend);
+
+  backend->recent_manager = gtk_recent_manager_get_default ();
+  g_signal_connect (backend->recent_manager,
+                    "changed",
+                    G_CALLBACK (on_recent_manager_changed),
+                    backend);
+  reload_recent_items (backend);
+
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+
+  return TRUE;
+}
+
+static gboolean
+recent_backend_query_info (GVfsBackend           *vfs_backend,
+                           GVfsJobQueryInfo      *job,
+                           const char            *filename,
+                           GFileQueryInfoFlags    flags,
+                           GFileInfo             *info,
+                           GFileAttributeMatcher *matcher)
+{
+  GVfsBackendRecent *backend = G_VFS_BACKEND_RECENT (vfs_backend);
+
+  g_assert (filename[0] == '/');
+
+  if (filename[1])
+    {
+      GError *error = NULL;
+      RecentItem *item = NULL;
+      GFile *real;
+
+      real = recent_backend_get_file (backend, filename, &item, &error);
+
+      if (real)
+        {
+          GFileInfo *real_info;
+
+          real_info = g_file_query_info (real,
+                                         job->attributes,
+                                         flags,
+                                         G_VFS_JOB (job)->cancellable,
+                                         &error);
+          g_object_unref (real);
+
+          if (real_info)
+            {
+              g_file_info_copy_into (real_info, info);
+              recent_backend_add_info (item, info);
+              g_vfs_job_succeeded (G_VFS_JOB (job));
+              g_object_unref (real_info);
+
+              return TRUE;
+            }
+        }
+
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      g_error_free (error);
+    }
+  else
+    {
+      GIcon *icon;
+
+      g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+      g_file_info_set_name (info, "/");
+      /* Translators: this is the display name of the backend */
+      g_file_info_set_display_name (info, _("Recent"));
+      g_file_info_set_content_type (info, "inode/directory");
+      g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
+      g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, TRUE);
+      g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
+
+      icon = g_themed_icon_new ("document-open-recent-symbolic");
+      g_file_info_set_icon (info, icon);
+      g_object_unref (icon);
+
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+
+  return TRUE;
+}
+
+static gboolean
+recent_backend_query_fs_info (GVfsBackend           *vfs_backend,
+                              GVfsJobQueryFsInfo    *job,
+                              const char            *filename,
+                              GFileInfo             *info,
+                              GFileAttributeMatcher *matcher)
+{
+  g_file_info_set_attribute_string (info,
+                                    G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
+                                    "recent");
+
+  g_file_info_set_attribute_boolean (info,
+                                     G_FILE_ATTRIBUTE_FILESYSTEM_READONLY,
+                                     TRUE);
+
+  g_file_info_set_attribute_uint32 (info,
+                                    G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
+                                    G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
+
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+
+  return TRUE;
+}
+
+static gboolean
+recent_backend_create_dir_monitor (GVfsBackend          *vfs_backend,
+                                   GVfsJobCreateMonitor *job,
+                                   const char           *filename,
+                                   GFileMonitorFlags     flags)
+{
+  GVfsBackendRecent *backend = G_VFS_BACKEND_RECENT (vfs_backend);
+  GVfsMonitor *monitor;
+
+  if (filename[1])
+    monitor = g_vfs_monitor_new (vfs_backend);
+  else
+    monitor = recent_backend_get_dir_monitor (backend, TRUE);
+
+  g_vfs_job_create_monitor_set_monitor (job, monitor);
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+  g_object_unref (monitor);
+
+  return TRUE;
+}
+
+static gboolean
+recent_backend_create_file_monitor (GVfsBackend          *vfs_backend,
+                                    GVfsJobCreateMonitor *job,
+                                    const char           *filename,
+                                    GFileMonitorFlags     flags)
+{
+  GVfsBackendRecent *backend = G_VFS_BACKEND_RECENT (vfs_backend);
+  GVfsMonitor *monitor;
+
+  if (filename[1])
+    monitor = g_vfs_monitor_new (vfs_backend);
+  else
+    monitor = recent_backend_get_file_monitor (backend, TRUE);
+
+  g_vfs_job_create_monitor_set_monitor (job, monitor);
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+  g_object_unref (monitor);
+
+  return TRUE;
+}
+
+static void
+recent_backend_finalize (GObject *object)
+{
+  GVfsBackendRecent *backend = G_VFS_BACKEND_RECENT (object);
+
+  g_clear_object (&backend->dir_monitor);
+  g_clear_object (&backend->file_monitor);
+
+  g_hash_table_destroy (backend->items);
+  g_hash_table_destroy (backend->uri_map);
+
+  g_signal_handlers_disconnect_by_func (backend->recent_manager, on_recent_manager_changed, backend);
+
+  if (G_OBJECT_CLASS (g_vfs_backend_recent_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_vfs_backend_recent_parent_class)->finalize) (object);
+}
+
+static void
+g_vfs_backend_recent_init (GVfsBackendRecent *backend)
+{
+  GVfsBackend *vfs_backend = G_VFS_BACKEND (backend);
+  GMountSpec *mount_spec;
+
+  backend->items = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)recent_item_free);
+  backend->uri_map = g_hash_table_new (g_str_hash, g_str_equal);
+
+  gtk_init (NULL, NULL);
+
+  /* translators: This is the name of the backend */
+  g_vfs_backend_set_display_name (vfs_backend, _("Recent"));
+  g_vfs_backend_set_icon_name (vfs_backend, "document-open-recent-symbolic");
+  g_vfs_backend_set_user_visible (vfs_backend, FALSE);
+
+  mount_spec = g_mount_spec_new ("recent");
+  g_vfs_backend_set_mount_spec (vfs_backend, mount_spec);
+  g_mount_spec_unref (mount_spec);
+}
+
+static void
+g_vfs_backend_recent_class_init (GVfsBackendRecentClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+  GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (class);
+
+  gobject_class->finalize = recent_backend_finalize;
+
+  backend_class->try_mount = recent_backend_mount;
+  backend_class->try_open_for_read = recent_backend_open_for_read;
+  backend_class->try_read = recent_backend_read;
+  backend_class->try_seek_on_read = recent_backend_seek_on_read;
+  backend_class->try_close_read = recent_backend_close_read;
+  backend_class->try_query_info = recent_backend_query_info;
+  backend_class->try_query_fs_info = recent_backend_query_fs_info;
+  backend_class->try_enumerate = recent_backend_enumerate;
+  backend_class->try_delete = recent_backend_delete;
+  backend_class->try_create_dir_monitor = recent_backend_create_dir_monitor;
+  backend_class->try_create_file_monitor = recent_backend_create_file_monitor;
+}
diff --git a/daemon/gvfsbackendrecent.h b/daemon/gvfsbackendrecent.h
new file mode 100644
index 0000000..f4cb9a0
--- /dev/null
+++ b/daemon/gvfsbackendrecent.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright  2012 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef _gvfsbackendrecent_h_
+#define _gvfsbackendrecent_h_
+
+#include <gvfsbackend.h>
+
+#define G_VFS_TYPE_BACKEND_RECENT    (g_vfs_backend_recent_get_type ())
+#define G_VFS_BACKEND_RECENT(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+                                     G_VFS_TYPE_BACKEND_RECENT, \
+                                     GVfsBackendRecent))
+
+typedef struct OPAQUE_TYPE__GVfsBackendRecent GVfsBackendRecent;
+GType g_vfs_backend_recent_get_type (void);
+
+#endif /* _gvfsbackendrecent_h_ */
diff --git a/daemon/recent.mount.in b/daemon/recent.mount.in
new file mode 100644
index 0000000..6903633
--- /dev/null
+++ b/daemon/recent.mount.in
@@ -0,0 +1,5 @@
+[Mount]
+Type=recent
+Exec= libexecdir@/gvfsd-recent
+AutoMount=true
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]