mirror of
https://gitlab.gnome.org/GNOME/nautilus
synced 2024-11-05 16:04:31 +00:00
88aebd7edd
2002-09-04 Dave Camp <dave@ximian.com> * libnautilus-private/nautilus-trash-directory.c: (add_volume): Don't start a search for a trash directory if one is already running.
392 lines
11 KiB
C
392 lines
11 KiB
C
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
|
|
|
|
nautilus-trash-directory.c: Subclass of NautilusDirectory to implement the
|
|
virtual trash directory.
|
|
|
|
Copyright (C) 1999, 2000 Eazel, Inc.
|
|
|
|
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.
|
|
|
|
Author: Darin Adler <darin@bentspoon.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "nautilus-trash-directory.h"
|
|
|
|
#include "nautilus-directory-private.h"
|
|
#include "nautilus-trash-monitor.h"
|
|
#include "nautilus-volume-monitor.h"
|
|
#include <eel/eel-glib-extensions.h>
|
|
#include <eel/eel-stock-dialogs.h>
|
|
#include <gtk/gtkmain.h>
|
|
#include <gtk/gtksignal.h>
|
|
#include <libgnome/gnome-i18n.h>
|
|
#include <libgnome/gnome-macros.h>
|
|
#include <libgnomevfs/gnome-vfs-utils.h>
|
|
|
|
struct NautilusTrashDirectoryDetails {
|
|
GHashTable *volumes;
|
|
};
|
|
|
|
typedef struct {
|
|
NautilusTrashDirectory *trash;
|
|
NautilusVolume *volume;
|
|
|
|
GnomeVFSAsyncHandle *handle;
|
|
NautilusDirectory *real_directory;
|
|
} TrashVolume;
|
|
|
|
static void add_volume (NautilusTrashDirectory *trash,
|
|
NautilusVolume *volume);
|
|
|
|
GNOME_CLASS_BOILERPLATE (NautilusTrashDirectory, nautilus_trash_directory,
|
|
NautilusMergedDirectory, NAUTILUS_TYPE_MERGED_DIRECTORY)
|
|
|
|
#define TRASH_SEARCH_TIMED_WAIT_DELAY 20000
|
|
|
|
static int pending_find_directory_count = 0;
|
|
|
|
static void
|
|
find_directory_start (void)
|
|
{
|
|
if (pending_find_directory_count == 0) {
|
|
eel_timed_wait_start_with_duration (TRASH_SEARCH_TIMED_WAIT_DELAY,
|
|
NULL,
|
|
add_volume,
|
|
_("Searching Disks"),
|
|
_("Nautilus is searching your disks for trash folders."),
|
|
NULL);
|
|
}
|
|
|
|
++pending_find_directory_count;
|
|
}
|
|
|
|
static void
|
|
find_directory_end (void)
|
|
{
|
|
--pending_find_directory_count;
|
|
|
|
if (pending_find_directory_count == 0) {
|
|
eel_timed_wait_stop (NULL, add_volume);
|
|
}
|
|
}
|
|
|
|
static void
|
|
find_directory_callback (GnomeVFSAsyncHandle *handle,
|
|
GList *results,
|
|
gpointer callback_data)
|
|
{
|
|
TrashVolume *trash_volume;
|
|
GnomeVFSFindDirectoryResult *result;
|
|
char *uri;
|
|
NautilusDirectory *directory;
|
|
|
|
trash_volume = callback_data;
|
|
|
|
g_assert (eel_g_list_exactly_one_item (results));
|
|
g_assert (trash_volume != NULL);
|
|
g_assert (NAUTILUS_IS_TRASH_DIRECTORY (trash_volume->trash));
|
|
g_assert (trash_volume->real_directory == NULL);
|
|
g_assert (trash_volume->handle == handle);
|
|
|
|
find_directory_end ();
|
|
|
|
/* We are done with the async. I/O. */
|
|
trash_volume->handle = NULL;
|
|
|
|
/* If we can't find the trash, ignore it silently. */
|
|
result = results->data;
|
|
if (result->result != GNOME_VFS_OK) {
|
|
return;
|
|
}
|
|
|
|
/* If we can't make a directory object, ignore it silently. */
|
|
uri = gnome_vfs_uri_to_string (result->uri,
|
|
GNOME_VFS_URI_HIDE_NONE);
|
|
directory = nautilus_directory_get (uri);
|
|
g_free (uri);
|
|
if (directory == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* Add the directory object. */
|
|
trash_volume->real_directory = directory;
|
|
nautilus_merged_directory_add_real_directory
|
|
(NAUTILUS_MERGED_DIRECTORY (trash_volume->trash),
|
|
directory);
|
|
}
|
|
|
|
static gboolean
|
|
get_trash_volume (NautilusTrashDirectory *trash,
|
|
NautilusVolume *volume,
|
|
TrashVolume **trash_volume,
|
|
GnomeVFSURI **volume_mount_uri)
|
|
{
|
|
char *uri_str;
|
|
NautilusVolume *volume_copy;
|
|
|
|
/* Quick out if we already know about this volume. */
|
|
*trash_volume = g_hash_table_lookup (trash->details->volumes,
|
|
volume);
|
|
|
|
if (*trash_volume != NULL && (*trash_volume)->real_directory != NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!nautilus_volume_should_integrate_trash (volume)) {
|
|
return FALSE;
|
|
}
|
|
|
|
uri_str = gnome_vfs_get_uri_from_local_path (nautilus_volume_get_mount_path (volume));
|
|
*volume_mount_uri = gnome_vfs_uri_new (uri_str);
|
|
g_free (uri_str);
|
|
|
|
if (*trash_volume == NULL) {
|
|
/* Make the structure used to track the trash for this volume. */
|
|
*trash_volume = g_new0 (TrashVolume, 1);
|
|
(*trash_volume)->trash = trash;
|
|
volume_copy = nautilus_volume_copy (volume);
|
|
(*trash_volume)->volume = volume_copy;
|
|
g_hash_table_insert (trash->details->volumes, volume_copy, *trash_volume);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
add_volume (NautilusTrashDirectory *trash,
|
|
NautilusVolume *volume)
|
|
{
|
|
TrashVolume *trash_volume;
|
|
GnomeVFSURI *volume_mount_uri;
|
|
GList vfs_uri_as_list;
|
|
|
|
if (!get_trash_volume (trash, volume, &trash_volume, &volume_mount_uri)) {
|
|
return;
|
|
}
|
|
|
|
if (trash_volume->handle) {
|
|
/* Already searching for trash */
|
|
gnome_vfs_uri_unref (volume_mount_uri);
|
|
return;
|
|
}
|
|
|
|
/* Find the real trash directory for this one. */
|
|
vfs_uri_as_list.data = volume_mount_uri;
|
|
vfs_uri_as_list.next = NULL;
|
|
vfs_uri_as_list.prev = NULL;
|
|
|
|
/* Search for Trash directories but don't create new ones. */
|
|
find_directory_start ();
|
|
gnome_vfs_async_find_directory
|
|
(&trash_volume->handle, &vfs_uri_as_list,
|
|
GNOME_VFS_DIRECTORY_KIND_TRASH, FALSE, TRUE, 0777,
|
|
GNOME_VFS_PRIORITY_DEFAULT,
|
|
find_directory_callback, trash_volume);
|
|
|
|
gnome_vfs_uri_unref (volume_mount_uri);
|
|
}
|
|
|
|
static void
|
|
check_trash_created (NautilusTrashDirectory *trash,
|
|
NautilusVolume *volume)
|
|
{
|
|
GnomeVFSResult result;
|
|
TrashVolume *trash_volume;
|
|
GnomeVFSURI *volume_mount_uri;
|
|
GnomeVFSURI *trash_uri;
|
|
char *uri;
|
|
|
|
if (!get_trash_volume (trash, volume, &trash_volume, &volume_mount_uri)) {
|
|
return;
|
|
}
|
|
|
|
/* Do a synch trash lookup -- if the trash directory was just created, it's location will
|
|
* be known and returned immediately without any blocking.
|
|
*/
|
|
result = gnome_vfs_find_directory (volume_mount_uri, GNOME_VFS_DIRECTORY_KIND_TRASH,
|
|
&trash_uri, FALSE, FALSE, 077);
|
|
|
|
gnome_vfs_uri_unref (volume_mount_uri);
|
|
if (result != GNOME_VFS_OK) {
|
|
return;
|
|
}
|
|
|
|
uri = gnome_vfs_uri_to_string (trash_uri,
|
|
GNOME_VFS_URI_HIDE_NONE);
|
|
trash_volume->real_directory = nautilus_directory_get (uri);
|
|
g_free (uri);
|
|
gnome_vfs_uri_unref (trash_uri);
|
|
if (trash_volume->real_directory == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* Add the directory object. */
|
|
nautilus_merged_directory_add_real_directory
|
|
(NAUTILUS_MERGED_DIRECTORY (trash_volume->trash),
|
|
trash_volume->real_directory);
|
|
}
|
|
|
|
static void
|
|
remove_trash_volume (TrashVolume *trash_volume, gboolean finalizing)
|
|
{
|
|
g_hash_table_remove (trash_volume->trash->details->volumes,
|
|
trash_volume->volume);
|
|
|
|
if (trash_volume->handle != NULL) {
|
|
gnome_vfs_async_cancel (trash_volume->handle);
|
|
find_directory_end ();
|
|
}
|
|
if (trash_volume->real_directory != NULL) {
|
|
if (! finalizing) {
|
|
nautilus_merged_directory_remove_real_directory
|
|
(NAUTILUS_MERGED_DIRECTORY (trash_volume->trash),
|
|
trash_volume->real_directory);
|
|
}
|
|
nautilus_directory_unref (trash_volume->real_directory);
|
|
}
|
|
nautilus_volume_free (trash_volume->volume);
|
|
g_free (trash_volume);
|
|
}
|
|
|
|
static void
|
|
remove_volume (NautilusTrashDirectory *trash,
|
|
NautilusVolume *volume)
|
|
{
|
|
TrashVolume *trash_volume;
|
|
|
|
/* Quick out if don't already know about this volume. */
|
|
trash_volume = g_hash_table_lookup (trash->details->volumes, volume);
|
|
if (trash_volume != NULL) {
|
|
remove_trash_volume (trash_volume, FALSE);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
add_one_volume (const NautilusVolume *volume,
|
|
gpointer callback_data)
|
|
{
|
|
/* The const is a kinda silly idea which we must cast away. */
|
|
add_volume (NAUTILUS_TRASH_DIRECTORY (callback_data),
|
|
(NautilusVolume *) volume);
|
|
return FALSE; /* don't stop iterating */
|
|
}
|
|
|
|
static void
|
|
check_trash_directory_added_callback (NautilusVolumeMonitor *monitor,
|
|
NautilusVolume *volume,
|
|
NautilusTrashDirectory *trash)
|
|
{
|
|
check_trash_created (trash, volume);
|
|
}
|
|
|
|
static void
|
|
volume_unmount_started_callback (NautilusVolumeMonitor *monitor,
|
|
NautilusVolume *volume,
|
|
NautilusTrashDirectory *trash)
|
|
{
|
|
remove_volume (trash, volume);
|
|
}
|
|
|
|
static void
|
|
volume_mounted_callback (NautilusVolumeMonitor *monitor,
|
|
NautilusVolume *volume,
|
|
NautilusTrashDirectory *trash)
|
|
{
|
|
add_volume (trash, volume);
|
|
}
|
|
|
|
static void
|
|
nautilus_trash_directory_instance_init (NautilusTrashDirectory *trash)
|
|
{
|
|
NautilusVolumeMonitor *volume_monitor;
|
|
|
|
trash->details = g_new0 (NautilusTrashDirectoryDetails, 1);
|
|
trash->details->volumes = g_hash_table_new ((GHashFunc)nautilus_volume_hash,
|
|
(GEqualFunc)nautilus_volume_is_equal);
|
|
|
|
volume_monitor = nautilus_volume_monitor_get ();
|
|
|
|
g_signal_connect_object (volume_monitor, "volume_mounted",
|
|
G_CALLBACK (volume_mounted_callback), trash, 0);
|
|
g_signal_connect_object (volume_monitor, "volume_unmount_started",
|
|
G_CALLBACK (volume_unmount_started_callback), trash, 0);
|
|
}
|
|
|
|
/* Finish initializing a new NautilusTrashDirectory. We have to do the
|
|
* remaining initialization here rather than in nautilus_trash_directory_init
|
|
* because of a cyclic dependency between the NautilusTrashDirectory and
|
|
* NautilusTrashMonitor instances.
|
|
*/
|
|
void
|
|
nautilus_trash_directory_finish_initializing (NautilusTrashDirectory *trash)
|
|
{
|
|
NautilusVolumeMonitor *volume_monitor;
|
|
|
|
volume_monitor = nautilus_volume_monitor_get ();
|
|
|
|
g_signal_connect_object (nautilus_trash_monitor_get (), "check_trash_directory_added",
|
|
G_CALLBACK (check_trash_directory_added_callback), trash, 0);
|
|
nautilus_volume_monitor_each_mounted_volume
|
|
(volume_monitor, add_one_volume, trash);
|
|
}
|
|
|
|
static void
|
|
remove_trash_volume_finalizing_cover (gpointer key, gpointer value, gpointer callback_data)
|
|
{
|
|
TrashVolume *trash_volume;
|
|
|
|
g_assert (key != NULL);
|
|
g_assert (value != NULL);
|
|
g_assert (callback_data == NULL);
|
|
|
|
trash_volume = value;
|
|
|
|
g_assert (NAUTILUS_IS_TRASH_DIRECTORY (trash_volume->trash));
|
|
g_assert (trash_volume->volume == key);
|
|
|
|
remove_trash_volume (trash_volume, TRUE);
|
|
}
|
|
|
|
static void
|
|
trash_finalize (GObject *object)
|
|
{
|
|
NautilusTrashDirectory *trash;
|
|
|
|
trash = NAUTILUS_TRASH_DIRECTORY (object);
|
|
|
|
eel_g_hash_table_safe_for_each (trash->details->volumes,
|
|
remove_trash_volume_finalizing_cover, NULL);
|
|
g_hash_table_destroy (trash->details->volumes);
|
|
g_free (trash->details);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static char *
|
|
trash_get_name_for_self_as_new_file (NautilusDirectory *directory)
|
|
{
|
|
g_assert (NAUTILUS_IS_TRASH_DIRECTORY (directory));
|
|
return g_strdup (_("Trash"));
|
|
}
|
|
|
|
static void
|
|
nautilus_trash_directory_class_init (NautilusTrashDirectoryClass *class)
|
|
{
|
|
G_OBJECT_CLASS (class)->finalize = trash_finalize;
|
|
NAUTILUS_DIRECTORY_CLASS (class)->get_name_for_self_as_new_file = trash_get_name_for_self_as_new_file;
|
|
}
|
|
|