diff options
-rw-r--r-- | ChangeLog | 65 | ||||
-rw-r--r-- | libnautilus-extensions/nautilus-file-operations.c | 653 | ||||
-rw-r--r-- | libnautilus-extensions/nautilus-trash-directory.c | 37 | ||||
-rw-r--r-- | libnautilus-extensions/nautilus-trash-monitor.c | 54 | ||||
-rw-r--r-- | libnautilus-extensions/nautilus-trash-monitor.h | 3 | ||||
-rw-r--r-- | libnautilus-extensions/nautilus-volume-monitor.c | 22 | ||||
-rw-r--r-- | libnautilus-extensions/nautilus-volume-monitor.h | 2 | ||||
-rw-r--r-- | libnautilus-private/nautilus-file-operations.c | 653 | ||||
-rw-r--r-- | libnautilus-private/nautilus-trash-directory.c | 37 | ||||
-rw-r--r-- | libnautilus-private/nautilus-trash-monitor.c | 54 | ||||
-rw-r--r-- | libnautilus-private/nautilus-trash-monitor.h | 3 | ||||
-rw-r--r-- | libnautilus-private/nautilus-volume-monitor.c | 22 | ||||
-rw-r--r-- | libnautilus-private/nautilus-volume-monitor.h | 2 |
13 files changed, 986 insertions, 621 deletions
@@ -1,3 +1,67 @@ +2000-12-12 Pavel Cisler <pavel@eazel.com> + + reviewed by: Darin Adler <darin@eazel.com> + + Fixed 4954 Nautilus crashes attempting to empty a .Trash + containing undeletable files + + * libnautilus-extensions/nautilus-file-operations.c: + (build_error_string), (handle_xfer_vfs_error) + Reworked the error message building logic. Split up + the routine into a part that decides what the interesting + input for building the error message is and a part that + actually builds the error message. Fixed a number + of cases where a wrong error message would be used. The + actual bug in the bug report was one of the cases -- an assert + catching a case that was not handled properly. + + Fixed: + part of -- 638 When a new volume gets mounted, integrate its Trash + directory. + part of -- 4143 Files dragged to trash icon on desktop seem to + vanish, + part of -- 4963 Files dragged to Trash from floppy don't appear + in Trash, + + * libnautilus-extensions/nautilus-trash-monitor.c: + * libnautilus-extensions/nautilus-trash-monitor.h: + (add_one_volume_trash), + (nautilus_trash_monitor_get_trash_directories): + Added a new call that returns a list of all the Trash directories + on all the mouted volumes. + + * libnautilus-extensions/nautilus-trash-directory.c: (add_volume): + Got rid of get_volume_vfs_uri_if_writable. + Used the new nautilus_volume_monitor_should_integrate_trash instead + to pick the volumes that can support trash. + + * libnautilus-extensions/nautilus-volume-monitor.c: + (nautilus_volume_monitor_should_integrate_trash), + (nautilus_volume_monitor_get_volume_mount_uri): + * libnautilus-extensions/nautilus-volume-monitor.h: + Add new helper calls used by the routine that collects all the + trash uris from all mouted volumes. + + * libnautilus-extensions/nautilus-file-operations.c: + (do_empty_trash): + Pass in a list of Trash directories instead of just the + single trash in the user's home directory. + + Fixed: + 2443 Trash is only found on EXT2 volumes + 2444 Trash on read-only volumes is not displayed + remaining part of -- 4143 Files dragged to trash icon on desktop + seem to vanish, + remaining part of -- 4963 Files dragged to Trash from floppy + don't appear in Trash, + + * libnautilus-extensions/nautilus-volume-monitor.c: + (nautilus_volume_monitor_should_integrate_trash): + Removed the old logic from get_volume_vfs_uri_if_writable + that only displayed trash from writable ext2 volumes. + Enumerated all the file system types that we know can + support trash. + 2000-12-12 Eskil Heyn Olsen <eskil@eazel.com> * components/services/install/lib/eazel-package-system-private.h: @@ -149,7 +213,6 @@ that did a hide followed by a show. Corrected an assert that should have been a return_if_fail. ->>>>>>> 1.3120 2000-12-12 John Sullivan <sullivan@eazel.com> reviewed by: Darin Adler <darin@eazel.com> diff --git a/libnautilus-extensions/nautilus-file-operations.c b/libnautilus-extensions/nautilus-file-operations.c index bda30ff96..a73fc78b0 100644 --- a/libnautilus-extensions/nautilus-file-operations.c +++ b/libnautilus-extensions/nautilus-file-operations.c @@ -44,6 +44,7 @@ #include <libnautilus-extensions/nautilus-global-preferences.h> #include <libnautilus-extensions/nautilus-link.h> #include <libnautilus-extensions/nautilus-stock-dialogs.h> +#include <libnautilus-extensions/nautilus-trash-monitor.h> typedef enum { XFER_MOVE, @@ -467,338 +468,451 @@ handle_xfer_ok (const GnomeVFSXferProgressInfo *progress_info, } } +typedef enum { + ERROR_READ_ONLY, + ERROR_NOT_READABLE, + ERROR_NOT_WRITABLE, + ERROR_NOT_ENOUGH_PERMISSIONS, + ERROR_NO_SPACE, + ERROR_OTHER +} NautilusFileOperationsErrorKind; -static int -handle_xfer_vfs_error (const GnomeVFSXferProgressInfo *progress_info, - XferInfo *xfer_info) +typedef enum { + ERROR_LOCATION_UNKNOWN, + ERROR_LOCATION_SOURCE, + ERROR_LOCATION_SOURCE_PARENT, + ERROR_LOCATION_SOURCE_OR_PARENT, + ERROR_LOCATION_TARGET +} NautilusFileOperationsErrorLocation; + + +static char * +build_error_string (const char *source_name, const char *target_name, + XferKind operation_kind, NautilusFileOperationsErrorKind error_kind, + NautilusFileOperationsErrorLocation error_location, GnomeVFSResult error) { - /* Notice that the error mode in `xfer_info' is the one we have been - * requested, but the transfer is always performed in mode - * `GNOME_VFS_XFER_ERROR_MODE_QUERY'. - */ + /* Avoid clever message composing here, just use brute force and + * duplicate the different flavors of error messages for all the + * possible permutations. + * That way localizers have an easier time and can even rearrange the + * order of the words in the messages easily. + */ - int result; - char *text; - char *unescaped_name; - const char *dialog_title; const char *error_string; + char *result; - switch (xfer_info->error_mode) { - case GNOME_VFS_XFER_ERROR_MODE_QUERY: + error_string = NULL; + result = NULL; - /* transfer error, prompt the user to continue or stop */ + if (error_location == ERROR_LOCATION_SOURCE_PARENT) { - unescaped_name = NULL; + switch (operation_kind) { + case XFER_MOVE: + case XFER_MOVE_TO_TRASH: + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved because you do not have " + "permissions to change its parent folder."); + } else if (error_kind == ERROR_READ_ONLY) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved because its parent folder " + "is read-only."); + } else if (error_kind == ERROR_NOT_WRITABLE) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved because its parent folder " + "is not writable."); + } + break; - if (progress_info->source_name != NULL) { - unescaped_name = nautilus_format_name_for_display (progress_info->source_name); + case XFER_DELETE: + case XFER_EMPTY_TRASH: + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while deleting.\n" + "\"%s\" cannot be deleted because you do not have " + "permissions to change its parent folder."); + } else if (error_kind == ERROR_READ_ONLY) { + error_string = _("Error while deleting.\n" + "\"%s\" cannot be deleted because its parent folder " + "is read-only."); + } else if (error_kind == ERROR_NOT_WRITABLE) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved because its parent folder " + "is not writable."); + } + break; + + default: + g_assert_not_reached (); + break; + } + + if (error_string != NULL) { + g_assert (source_name != NULL); + result = g_strdup_printf (error_string, source_name); } - /* Resist the temptation to do clever message composing here, just - * use brute force and duplicate the different flavors of error messages. - * That way localizers have an easier time and can even rearrange the - * order of the words in the messages easily. - */ + } else if (error_location == ERROR_LOCATION_SOURCE_OR_PARENT) { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - dialog_title = _("Error while copying."); - break; + g_assert (source_name != NULL); + + switch (operation_kind) { case XFER_MOVE: - dialog_title = _("Error while moving."); - break; - case XFER_LINK: - dialog_title = _("Error while linking."); + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved because you do not have " + "permissions to change it or its parent folder."); + } break; - case XFER_DELETE: - case XFER_EMPTY_TRASH: case XFER_MOVE_TO_TRASH: - dialog_title = _("Error while deleting."); + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved to trash because you do not have " + "permissions to change it or its parent folder."); + } break; + default: - dialog_title = NULL; + g_assert_not_reached (); break; } - /* special case read only target errors or non-readable sources - * and other predictable failures - */ - - if (progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM - || progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY - || progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED) { - - if ((xfer_info->kind == XFER_MOVE || xfer_info->kind == XFER_MOVE_TO_TRASH) - && progress_info->phase != GNOME_VFS_XFER_CHECKING_DESTINATION) { - /* we are failing because we are moving from a directory that - * is not writable - */ - if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED) { - error_string = _("Error while moving.\n" - "%s cannot be moved because you do not have " - "permissions to change it's parent folder."); - } else { - error_string = _("Error while moving.\n" - "%s cannot be moved because its parent folder " - "is read-only."); - } - } else if (progress_info->phase == GNOME_VFS_XFER_PHASE_OPENSOURCE - || progress_info->phase == GNOME_VFS_XFER_PHASE_COLLECTING - || progress_info->phase == GNOME_VFS_XFER_PHASE_INITIAL) { - - if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED) { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying.\n" - "You do not have permissions to read %s."); - break; - case XFER_MOVE: - error_string = _("Error while moving.\n" - "You do not have permissions to read %s."); - break; - case XFER_LINK: - error_string = _("Error while linking.\n" - "You do not have permissions to read %s."); - break; - case XFER_DELETE: - case XFER_EMPTY_TRASH: - case XFER_MOVE_TO_TRASH: - error_string = _("Error while deleting.\n" - "You do not have permissions to read %s."); - break; - default: - error_string = ""; - break; - } - } else { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying.\n" - "%s is not readable."); - break; - case XFER_MOVE: - error_string = _("Error while moving.\n" - "%s is not readable."); - break; - case XFER_LINK: - error_string = _("Error while linking.\n" - "%s is not readable."); - break; - case XFER_DELETE: - case XFER_EMPTY_TRASH: - case XFER_MOVE_TO_TRASH: - error_string = _("Error while deleting.\n" - "%s is not readable."); - break; - default: - error_string = ""; - break; - } - } + if (error_string != NULL) { + g_assert (source_name != NULL); + result = g_strdup_printf (error_string, source_name); + } - } else { - if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED) { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying items to \"%s\".\n" - "You do not have permissions to write to the destination."); - break; - case XFER_MOVE_TO_TRASH: - case XFER_MOVE: - error_string = _("Error while moving items to \"%s\".\n" - "You do not have permissions to write to the destination."); - break; - case XFER_LINK: - error_string = _("Error while linking items to \"%s\".\n" - "You do not have permissions to write to the destination."); - break; - default: - g_assert_not_reached (); - error_string = ""; - break; - } - } else { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying items to \"%s\".\n" - "The destination is not writable."); - break; - case XFER_MOVE_TO_TRASH: - case XFER_MOVE: - error_string = _("Error while moving items to \"%s\".\n" - "The destination is not writable."); - break; - case XFER_LINK: - error_string = _("Error while linking items to \"%s\".\n" - "The destination is not writable."); - break; - default: - g_assert_not_reached (); - error_string = ""; - break; - } - } + } else if (error_location == ERROR_LOCATION_SOURCE) { - if (progress_info->target_name != NULL) { - g_free (unescaped_name); - unescaped_name = nautilus_format_name_for_display ( - progress_info->target_name); - } + g_assert (source_name != NULL); + + switch (operation_kind) { + case XFER_COPY: + case XFER_DUPLICATE: + if (error_kind == ERROR_NOT_READABLE) { + error_string = _("Error while copying.\n" + "\"%s\" is not readable."); } - text = g_strdup_printf (error_string, unescaped_name); - g_free (unescaped_name); - - result = nautilus_simple_dialog - (parent_for_error_dialog (xfer_info), TRUE, text, - dialog_title, _("Stop"), NULL); - g_free (text); + break; + case XFER_LINK: + if (error_kind == ERROR_NOT_READABLE) { + error_string = _("Error while linking\n" + "\"%s\" is not readable."); + } + break; - return GNOME_VFS_XFER_ERROR_ACTION_ABORT; + default: + g_assert_not_reached (); + break; } - /* special case read only target errors */ - if (progress_info->vfs_status == GNOME_VFS_ERROR_NO_SPACE) { - - if (unescaped_name != NULL) { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying \"%s\".\n" - "There is no space on the destination."); - break; - case XFER_MOVE_TO_TRASH: - case XFER_MOVE: - error_string = _("Error while moving \"%s\".\n" - "There is no space on the destination."); - break; - case XFER_LINK: - error_string = _("Error while linking \"%s\".\n" - "There is no space on the destination."); - break; - default: - g_assert_not_reached (); - error_string = ""; - break; - } - text = g_strdup (error_string); - } else { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying.\n" - "There is no space on the destination."); - break; - case XFER_MOVE_TO_TRASH: - case XFER_MOVE: - error_string = _("Error while moving.\n" - "There is no space on the destination."); - break; - case XFER_LINK: - error_string = _("Error while linking.\n" - "There is no space on the destination."); - break; - default: - g_assert_not_reached (); - error_string = ""; - break; - } - text = g_strdup_printf (error_string, unescaped_name); - } - g_free (unescaped_name); - - result = nautilus_simple_dialog - (parent_for_error_dialog (xfer_info), TRUE, text, - dialog_title, _("Stop"), NULL); - g_free (text); - return GNOME_VFS_XFER_ERROR_ACTION_ABORT; + if (error_string != NULL) { + g_assert (source_name != NULL); + result = g_strdup_printf (error_string, source_name); } - if (unescaped_name != NULL) { - - switch (xfer_info->kind) { + } else if (error_location == ERROR_LOCATION_TARGET) { + + if (error_kind == ERROR_NO_SPACE) { + switch (operation_kind) { case XFER_COPY: case XFER_DUPLICATE: - error_string = _("Error \"%s\" while copying.\n" + error_string = _("Error while copying \"%s\".\n" + "There is no space on the destination."); + break; + case XFER_MOVE_TO_TRASH: + case XFER_MOVE: + error_string = _("Error while moving \"%s\".\n" + "There is no space on the destination."); + break; + case XFER_LINK: + error_string = _("Error while linking \"%s\".\n" + "There is no space on the destination."); + break; + default: + g_assert_not_reached (); + break; + } + } else { + switch (operation_kind) { + case XFER_COPY: + case XFER_DUPLICATE: + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while copying items to \"%s\".\n" + "You do not have permissions to write to " + "the destination."); + } else if (error_kind == ERROR_NOT_WRITABLE) { + error_string = _("Error while copying items to \"%s\".\n" + "The destination is not writable."); + } + break; + case XFER_MOVE: + case XFER_MOVE_TO_TRASH: + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while moving items \"%s\".\n" + "You do not have permissions to write to " + "the destination."); + } else if (error_kind == ERROR_NOT_WRITABLE) { + error_string = _("Error while moving items \"%s\".\n" + "The destination is not writable."); + } + + break; + case XFER_LINK: + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while creating links in \"%s\".\n" + "You do not have permissions to write to " + "the destination."); + } else if (error_kind == ERROR_NOT_WRITABLE) { + error_string = _("Error while creating links in \"%s\".\n" + "The destination is not writable."); + } + break; + default: + g_assert_not_reached (); + break; + } + } + if (error_string != NULL) { + g_assert (target_name != NULL); + result = g_strdup_printf (error_string, target_name); + } + } + + if (result == NULL) { + /* None of the specific error messages apply, use a catch-all + * generic error + */ + if (source_name != NULL) { + switch (operation_kind) { + case XFER_COPY: + case XFER_DUPLICATE: + error_string = _("Error \"%s\" while copying \"%s\".\n" "Would you like to continue?"); break; case XFER_MOVE: - error_string = _("Error \"%s\" while moving.\n" + error_string = _("Error \"%s\" while moving \"%s\".\n" "Would you like to continue?"); break; case XFER_LINK: - error_string = _("Error \"%s\" while linking.\n" + error_string = _("Error \"%s\" while linking \"%s\".\n" "Would you like to continue?"); break; case XFER_DELETE: case XFER_EMPTY_TRASH: case XFER_MOVE_TO_TRASH: - error_string = _("Error \"%s\" while deleting.\n" + error_string = _("Error \"%s\" while deleting \"%s\".\n" "Would you like to continue?"); break; default: - error_string = ""; + g_assert_not_reached (); break; } - text = g_strdup_printf (error_string, - gnome_vfs_result_to_string (progress_info->vfs_status)); + result = g_strdup_printf (error_string, + gnome_vfs_result_to_string (error), + source_name); } else { - - switch (xfer_info->kind) { + switch (operation_kind) { case XFER_COPY: case XFER_DUPLICATE: - error_string = _("Error \"%s\" while copying \"%s\".\n" + error_string = _("Error \"%s\" while copying.\n" "Would you like to continue?"); break; case XFER_MOVE: - error_string = _("Error \"%s\" while moving \"%s\".\n" + error_string = _("Error \"%s\" while moving.\n" "Would you like to continue?"); break; case XFER_LINK: - error_string = _("Error \"%s\" while linking \"%s\".\n" + error_string = _("Error \"%s\" while linking.\n" "Would you like to continue?"); break; case XFER_DELETE: case XFER_EMPTY_TRASH: case XFER_MOVE_TO_TRASH: - error_string = _("Error \"%s\" while deleting \"%s\".\n" + error_string = _("Error \"%s\" while deleting.\n" "Would you like to continue?"); break; default: - error_string = ""; + g_assert_not_reached (); break; } - text = g_strdup_printf (error_string, - gnome_vfs_result_to_string (progress_info->vfs_status), - unescaped_name); + result = g_strdup_printf (error_string, + gnome_vfs_result_to_string (error)); } - g_free (unescaped_name); + } + return result; +} - result = nautilus_simple_dialog - (parent_for_error_dialog (xfer_info), TRUE, text, - dialog_title, - _("Skip"), _("Retry"), _("Stop"), NULL); +static int +handle_xfer_vfs_error (const GnomeVFSXferProgressInfo *progress_info, + XferInfo *xfer_info) +{ + /* Notice that the error mode in `xfer_info' is the one we have been + * requested, but the transfer is always performed in mode + * `GNOME_VFS_XFER_ERROR_MODE_QUERY'. + */ - g_free (text); + int error_dialog_button_pressed; + int error_dialog_result; + char *text; + char *unescaped_source_name; + char *unescaped_target_name; + const char *dialog_title; + NautilusFileOperationsErrorKind error_kind; + NautilusFileOperationsErrorLocation error_location; + + switch (xfer_info->error_mode) { + case GNOME_VFS_XFER_ERROR_MODE_QUERY: + + /* transfer error, prompt the user to continue or stop */ + + unescaped_source_name = NULL; + unescaped_target_name = NULL; + + if (progress_info->source_name != NULL) { + unescaped_source_name = nautilus_format_name_for_display + (progress_info->source_name); + } + + if (progress_info->target_name != NULL) { + unescaped_target_name = nautilus_format_name_for_display + (progress_info->target_name); + } + + error_kind = ERROR_OTHER; + error_location = ERROR_LOCATION_UNKNOWN; - switch (result) { - case 0: - return GNOME_VFS_XFER_ERROR_ACTION_SKIP; - case 1: - return GNOME_VFS_XFER_ERROR_ACTION_RETRY; + /* Single out a few common error conditions for which we have + * custom-taylored error messages. + */ + if ((progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM + || progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY) + && (xfer_info->kind == XFER_DELETE + || xfer_info->kind == XFER_EMPTY_TRASH)) { + error_location = ERROR_LOCATION_SOURCE_PARENT; + error_kind = ERROR_READ_ONLY; + } else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED + && (xfer_info->kind == XFER_DELETE + || xfer_info->kind == XFER_EMPTY_TRASH)) { + error_location = ERROR_LOCATION_SOURCE_PARENT; + error_kind = ERROR_NOT_ENOUGH_PERMISSIONS; + } else if ((progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM + || progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY) + && (xfer_info->kind == XFER_MOVE + || xfer_info->kind == XFER_MOVE_TO_TRASH) + && progress_info->phase != GNOME_VFS_XFER_CHECKING_DESTINATION) { + error_location = ERROR_LOCATION_SOURCE_PARENT; + error_kind = ERROR_READ_ONLY; + } else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED + && (xfer_info->kind == XFER_MOVE + || xfer_info->kind == XFER_MOVE_TO_TRASH) + && progress_info->phase != GNOME_VFS_XFER_CHECKING_DESTINATION) { + error_location = ERROR_LOCATION_SOURCE_OR_PARENT; + error_kind = ERROR_NOT_ENOUGH_PERMISSIONS; + } else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED + && (xfer_info->kind == XFER_COPY + || xfer_info->kind == XFER_DUPLICATE) + && (progress_info->phase == GNOME_VFS_XFER_PHASE_OPENSOURCE + || progress_info->phase == GNOME_VFS_XFER_PHASE_COLLECTING + || progress_info->phase == GNOME_VFS_XFER_PHASE_INITIAL)) { + error_location = ERROR_LOCATION_SOURCE; + error_kind = ERROR_NOT_READABLE; + } else if ((progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM + || progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY) + && progress_info->phase == GNOME_VFS_XFER_CHECKING_DESTINATION) { + error_location = ERROR_LOCATION_TARGET; + error_kind = ERROR_NOT_WRITABLE; + } else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED + && progress_info->phase == GNOME_VFS_XFER_CHECKING_DESTINATION) { + error_location = ERROR_LOCATION_TARGET; + error_kind = ERROR_NOT_ENOUGH_PERMISSIONS; + } else if (progress_info->vfs_status == GNOME_VFS_ERROR_NO_SPACE) { + error_location = ERROR_LOCATION_TARGET; + error_kind = ERROR_NO_SPACE; + } + + text = build_error_string (unescaped_source_name, unescaped_target_name, + xfer_info->kind, error_kind, error_location, progress_info->vfs_status); + + switch (xfer_info->kind) { + case XFER_COPY: + case XFER_DUPLICATE: + dialog_title = _("Error while copying."); + break; + case XFER_MOVE: + dialog_title = _("Error while moving."); + break; + case XFER_LINK: + dialog_title = _("Error while linking."); + break; + case XFER_DELETE: + case XFER_EMPTY_TRASH: + case XFER_MOVE_TO_TRASH: + dialog_title = _("Error while deleting."); + break; default: - g_assert_not_reached (); - /* fall through */ - case 2: - return GNOME_VFS_XFER_ERROR_ACTION_ABORT; - } + dialog_title = NULL; + break; + } + if (error_location == ERROR_LOCATION_TARGET) { + /* We can't continue, just tell the user. */ + nautilus_simple_dialog (parent_for_error_dialog (xfer_info), + TRUE, text, dialog_title, _("Stop"), NULL); + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; + + } else if ((error_location == ERROR_LOCATION_SOURCE + || error_location == ERROR_LOCATION_SOURCE_PARENT + || error_location == ERROR_LOCATION_SOURCE_OR_PARENT) + && (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS + || error_kind == ERROR_NOT_READABLE)) { + /* The error could have happened on any of the files + * in the moved/copied/deleted hierarchy, we can probably + * continue. Allow the user to skip. + */ + error_dialog_button_pressed = nautilus_simple_dialog + (parent_for_error_dialog (xfer_info), TRUE, text, + dialog_title, + _("Skip"), _("Stop"), NULL); + + switch (error_dialog_button_pressed) { + case 0: + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_SKIP; + break; + case 1: + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; + break; + default: + g_assert_not_reached (); + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; + } + + } else { + /* Generic error, offer to retry and skip. */ + error_dialog_button_pressed = nautilus_simple_dialog + (parent_for_error_dialog (xfer_info), TRUE, text, + dialog_title, + _("Skip"), _("Retry"), _("Stop"), NULL); + + switch (error_dialog_button_pressed) { + case 0: + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_SKIP; + break; + case 1: + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_RETRY; + break; + case 2: + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; + break; + default: + g_assert_not_reached (); + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; + } + } + + g_free (text); + return error_dialog_result; case GNOME_VFS_XFER_ERROR_MODE_ABORT: default: @@ -1946,24 +2060,11 @@ nautilus_file_operations_delete (const GList *item_uris, static void do_empty_trash (GtkWidget *parent_view) { - GnomeVFSURI *trash_dir_uri; - GnomeVFSResult result; XferInfo *xfer_info; GList *trash_dir_list; - /* FIXME bugzilla.eazel.com 638: - * add the different trash directories from the different volumes - */ - - trash_dir_uri = NULL; - trash_dir_list = NULL; - - result = gnome_vfs_find_directory (NULL, GNOME_VFS_DIRECTORY_KIND_TRASH, - &trash_dir_uri, FALSE, FALSE, 0777); - - if (result == GNOME_VFS_OK) { - g_assert (trash_dir_uri != NULL); - + trash_dir_list = nautilus_trash_monitor_get_trash_directories (); + if (trash_dir_list != NULL) { /* set up the move parameters */ xfer_info = g_new0 (XferInfo, 1); xfer_info->parent_view = parent_view; @@ -1982,8 +2083,6 @@ do_empty_trash (GtkWidget *parent_view) xfer_info->overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE; xfer_info->kind = XFER_EMPTY_TRASH; - trash_dir_list = g_list_append (NULL, trash_dir_uri); - gnome_vfs_async_xfer (&xfer_info->handle, trash_dir_list, NULL, GNOME_VFS_XFER_EMPTY_DIRECTORIES, GNOME_VFS_XFER_ERROR_MODE_QUERY, diff --git a/libnautilus-extensions/nautilus-trash-directory.c b/libnautilus-extensions/nautilus-trash-directory.c index 01d38f7e3..0e7210881 100644 --- a/libnautilus-extensions/nautilus-trash-directory.c +++ b/libnautilus-extensions/nautilus-trash-directory.c @@ -55,32 +55,6 @@ NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusTrashDirectory, nautilus_trash_directory, NAUTILUS_TYPE_MERGED_DIRECTORY) -static GnomeVFSURI * -get_volume_vfs_uri_if_writable (NautilusVolume *volume) -{ - char *uri; - GnomeVFSURI *vfs_uri; - - /* FIXME bugzilla.eazel.com 2443: - * Why is trash only found on EXT2 volumes? This seems - * like an incorrect check. - */ - - /* FIXME bugzilla.eazel.com 2444: - * Why can't we view the trash on a read-only volume - * if it happens to be there? - */ - if (volume->type != NAUTILUS_VOLUME_EXT2 || volume->is_read_only) { - return NULL; - } - - uri = gnome_vfs_get_uri_from_local_path (volume->mount_path); - vfs_uri = gnome_vfs_uri_new (uri); - g_free (uri); - - return vfs_uri; -} - static void find_directory_callback (GnomeVFSAsyncHandle *handle, GList *results, @@ -129,7 +103,7 @@ add_volume (NautilusTrashDirectory *trash, NautilusVolume *volume) { TrashVolume *trash_volume; - GnomeVFSURI *vfs_uri; + GnomeVFSURI *volume_mount_uri; GList vfs_uri_as_list; /* Quick out if we already know about this volume. */ @@ -139,11 +113,12 @@ add_volume (NautilusTrashDirectory *trash, return; } - /* If we can't get the URI, we jus don't do trash on this volume. */ - vfs_uri = get_volume_vfs_uri_if_writable (volume); - if (vfs_uri == NULL) { + if (!nautilus_volume_monitor_should_integrate_trash (volume)) { return; } + + volume_mount_uri = gnome_vfs_uri_new ( + nautilus_volume_monitor_get_volume_mount_uri (volume)); /* Make the structure used to track the trash for this volume. */ trash_volume = g_new0 (TrashVolume, 1); @@ -152,7 +127,7 @@ add_volume (NautilusTrashDirectory *trash, g_hash_table_insert (trash->details->volumes, volume, trash_volume); /* Find the real trash directory for this one. */ - vfs_uri_as_list.data = vfs_uri; + 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. */ diff --git a/libnautilus-extensions/nautilus-trash-monitor.c b/libnautilus-extensions/nautilus-trash-monitor.c index b70a565da..aca0a9d29 100644 --- a/libnautilus-extensions/nautilus-trash-monitor.c +++ b/libnautilus-extensions/nautilus-trash-monitor.c @@ -187,3 +187,57 @@ nautilus_trash_monitor_is_empty (void) { return nautilus_trash_monitor_get ()->details->empty; } + +static gboolean +add_one_volume_trash (const NautilusVolume *volume, + gpointer callback_data) +{ + GnomeVFSURI *volume_mount_point_uri; + GnomeVFSURI *trash_uri; + GList **result; + + result = (GList **)callback_data; + + if (nautilus_volume_monitor_should_integrate_trash (volume)) { + + /* Get the uri of the volume mount point as the place + * "near" which to look for trash on the given volume. + */ + volume_mount_point_uri = gnome_vfs_uri_new ( + nautilus_volume_monitor_get_volume_mount_uri (volume)); + + g_assert (volume_mount_point_uri != NULL); + + /* Look for trash. It is OK to use a sync call here because + * the options we use (don't create, don't look for it if we + * already don't know where it is) do not cause any IO. + */ + if (gnome_vfs_find_directory (volume_mount_point_uri, + GNOME_VFS_DIRECTORY_KIND_TRASH, &trash_uri, + FALSE, FALSE, 0777) == GNOME_VFS_OK) { + + /* found trash, put it on the list */ + *result = g_list_append (*result, trash_uri); + } + + gnome_vfs_uri_unref (volume_mount_point_uri); + } + + /* don't stop iterating */ + return FALSE; +} + +GList * +nautilus_trash_monitor_get_trash_directories (void) +{ + GList *result; + + result = NULL; + + /* Collect the trash directories on all the mounted volumes. */ + nautilus_volume_monitor_each_mounted_volume + (nautilus_volume_monitor_get (), add_one_volume_trash, &result); + + return result; +} + diff --git a/libnautilus-extensions/nautilus-trash-monitor.h b/libnautilus-extensions/nautilus-trash-monitor.h index 19139d514..41c726dcc 100644 --- a/libnautilus-extensions/nautilus-trash-monitor.h +++ b/libnautilus-extensions/nautilus-trash-monitor.h @@ -60,7 +60,6 @@ GtkType nautilus_trash_monitor_get_type (void); NautilusTrashMonitor *nautilus_trash_monitor_get (void); gboolean nautilus_trash_monitor_is_empty (void); -void nautilus_trash_monitor_async_get_trash_directories (GnomeVFSAsyncFindDirectoryCallback callback, - gpointer context); +GList * nautilus_trash_monitor_get_trash_directories (void); #endif diff --git a/libnautilus-extensions/nautilus-volume-monitor.c b/libnautilus-extensions/nautilus-volume-monitor.c index 0a75b3c03..dfd98d045 100644 --- a/libnautilus-extensions/nautilus-volume-monitor.c +++ b/libnautilus-extensions/nautilus-volume-monitor.c @@ -318,6 +318,28 @@ nautilus_volume_monitor_volume_is_mounted (const NautilusVolume *volume) return FALSE; } +gboolean +nautilus_volume_monitor_should_integrate_trash (const NautilusVolume *volume) +{ + /* Hand-pick a bunch of file system types that we know we can support + * trash on. It would probably be harder to keep a list of the ones + * we can't try to support trash on because the list would have to be + * more definitive. + */ + return volume->type == NAUTILUS_VOLUME_EXT2 + || volume->type == NAUTILUS_VOLUME_FAT + || volume->type == NAUTILUS_VOLUME_NFS + || volume->type == NAUTILUS_VOLUME_VFAT + || volume->type == NAUTILUS_VOLUME_FLOPPY + || volume->type == NAUTILUS_VOLUME_SMB; +} + +const char * +nautilus_volume_monitor_get_volume_mount_uri (const NautilusVolume *volume) +{ + return volume->mount_path; +} + static void mount_volume_cdrom_set_state (NautilusVolumeMonitor *monitor, NautilusVolume *volume) { diff --git a/libnautilus-extensions/nautilus-volume-monitor.h b/libnautilus-extensions/nautilus-volume-monitor.h index 08984fc65..d376e3b08 100644 --- a/libnautilus-extensions/nautilus-volume-monitor.h +++ b/libnautilus-extensions/nautilus-volume-monitor.h @@ -111,6 +111,8 @@ gboolean nautilus_volume_monitor_is_volume_link (con void nautilus_volume_monitor_each_volume (NautilusVolumeMonitor *monitor, NautilusEachVolumeFunction function, gpointer context); +gboolean nautilus_volume_monitor_should_integrate_trash (const NautilusVolume *volume); +const char *nautilus_volume_monitor_get_volume_mount_uri (const NautilusVolume *volume); void nautilus_volume_monitor_each_mounted_volume (NautilusVolumeMonitor *monitor, NautilusEachVolumeFunction function, gpointer context); diff --git a/libnautilus-private/nautilus-file-operations.c b/libnautilus-private/nautilus-file-operations.c index bda30ff96..a73fc78b0 100644 --- a/libnautilus-private/nautilus-file-operations.c +++ b/libnautilus-private/nautilus-file-operations.c @@ -44,6 +44,7 @@ #include <libnautilus-extensions/nautilus-global-preferences.h> #include <libnautilus-extensions/nautilus-link.h> #include <libnautilus-extensions/nautilus-stock-dialogs.h> +#include <libnautilus-extensions/nautilus-trash-monitor.h> typedef enum { XFER_MOVE, @@ -467,338 +468,451 @@ handle_xfer_ok (const GnomeVFSXferProgressInfo *progress_info, } } +typedef enum { + ERROR_READ_ONLY, + ERROR_NOT_READABLE, + ERROR_NOT_WRITABLE, + ERROR_NOT_ENOUGH_PERMISSIONS, + ERROR_NO_SPACE, + ERROR_OTHER +} NautilusFileOperationsErrorKind; -static int -handle_xfer_vfs_error (const GnomeVFSXferProgressInfo *progress_info, - XferInfo *xfer_info) +typedef enum { + ERROR_LOCATION_UNKNOWN, + ERROR_LOCATION_SOURCE, + ERROR_LOCATION_SOURCE_PARENT, + ERROR_LOCATION_SOURCE_OR_PARENT, + ERROR_LOCATION_TARGET +} NautilusFileOperationsErrorLocation; + + +static char * +build_error_string (const char *source_name, const char *target_name, + XferKind operation_kind, NautilusFileOperationsErrorKind error_kind, + NautilusFileOperationsErrorLocation error_location, GnomeVFSResult error) { - /* Notice that the error mode in `xfer_info' is the one we have been - * requested, but the transfer is always performed in mode - * `GNOME_VFS_XFER_ERROR_MODE_QUERY'. - */ + /* Avoid clever message composing here, just use brute force and + * duplicate the different flavors of error messages for all the + * possible permutations. + * That way localizers have an easier time and can even rearrange the + * order of the words in the messages easily. + */ - int result; - char *text; - char *unescaped_name; - const char *dialog_title; const char *error_string; + char *result; - switch (xfer_info->error_mode) { - case GNOME_VFS_XFER_ERROR_MODE_QUERY: + error_string = NULL; + result = NULL; - /* transfer error, prompt the user to continue or stop */ + if (error_location == ERROR_LOCATION_SOURCE_PARENT) { - unescaped_name = NULL; + switch (operation_kind) { + case XFER_MOVE: + case XFER_MOVE_TO_TRASH: + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved because you do not have " + "permissions to change its parent folder."); + } else if (error_kind == ERROR_READ_ONLY) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved because its parent folder " + "is read-only."); + } else if (error_kind == ERROR_NOT_WRITABLE) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved because its parent folder " + "is not writable."); + } + break; - if (progress_info->source_name != NULL) { - unescaped_name = nautilus_format_name_for_display (progress_info->source_name); + case XFER_DELETE: + case XFER_EMPTY_TRASH: + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while deleting.\n" + "\"%s\" cannot be deleted because you do not have " + "permissions to change its parent folder."); + } else if (error_kind == ERROR_READ_ONLY) { + error_string = _("Error while deleting.\n" + "\"%s\" cannot be deleted because its parent folder " + "is read-only."); + } else if (error_kind == ERROR_NOT_WRITABLE) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved because its parent folder " + "is not writable."); + } + break; + + default: + g_assert_not_reached (); + break; + } + + if (error_string != NULL) { + g_assert (source_name != NULL); + result = g_strdup_printf (error_string, source_name); } - /* Resist the temptation to do clever message composing here, just - * use brute force and duplicate the different flavors of error messages. - * That way localizers have an easier time and can even rearrange the - * order of the words in the messages easily. - */ + } else if (error_location == ERROR_LOCATION_SOURCE_OR_PARENT) { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - dialog_title = _("Error while copying."); - break; + g_assert (source_name != NULL); + + switch (operation_kind) { case XFER_MOVE: - dialog_title = _("Error while moving."); - break; - case XFER_LINK: - dialog_title = _("Error while linking."); + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved because you do not have " + "permissions to change it or its parent folder."); + } break; - case XFER_DELETE: - case XFER_EMPTY_TRASH: case XFER_MOVE_TO_TRASH: - dialog_title = _("Error while deleting."); + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while moving.\n" + "\"%s\" cannot be moved to trash because you do not have " + "permissions to change it or its parent folder."); + } break; + default: - dialog_title = NULL; + g_assert_not_reached (); break; } - /* special case read only target errors or non-readable sources - * and other predictable failures - */ - - if (progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM - || progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY - || progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED) { - - if ((xfer_info->kind == XFER_MOVE || xfer_info->kind == XFER_MOVE_TO_TRASH) - && progress_info->phase != GNOME_VFS_XFER_CHECKING_DESTINATION) { - /* we are failing because we are moving from a directory that - * is not writable - */ - if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED) { - error_string = _("Error while moving.\n" - "%s cannot be moved because you do not have " - "permissions to change it's parent folder."); - } else { - error_string = _("Error while moving.\n" - "%s cannot be moved because its parent folder " - "is read-only."); - } - } else if (progress_info->phase == GNOME_VFS_XFER_PHASE_OPENSOURCE - || progress_info->phase == GNOME_VFS_XFER_PHASE_COLLECTING - || progress_info->phase == GNOME_VFS_XFER_PHASE_INITIAL) { - - if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED) { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying.\n" - "You do not have permissions to read %s."); - break; - case XFER_MOVE: - error_string = _("Error while moving.\n" - "You do not have permissions to read %s."); - break; - case XFER_LINK: - error_string = _("Error while linking.\n" - "You do not have permissions to read %s."); - break; - case XFER_DELETE: - case XFER_EMPTY_TRASH: - case XFER_MOVE_TO_TRASH: - error_string = _("Error while deleting.\n" - "You do not have permissions to read %s."); - break; - default: - error_string = ""; - break; - } - } else { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying.\n" - "%s is not readable."); - break; - case XFER_MOVE: - error_string = _("Error while moving.\n" - "%s is not readable."); - break; - case XFER_LINK: - error_string = _("Error while linking.\n" - "%s is not readable."); - break; - case XFER_DELETE: - case XFER_EMPTY_TRASH: - case XFER_MOVE_TO_TRASH: - error_string = _("Error while deleting.\n" - "%s is not readable."); - break; - default: - error_string = ""; - break; - } - } + if (error_string != NULL) { + g_assert (source_name != NULL); + result = g_strdup_printf (error_string, source_name); + } - } else { - if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED) { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying items to \"%s\".\n" - "You do not have permissions to write to the destination."); - break; - case XFER_MOVE_TO_TRASH: - case XFER_MOVE: - error_string = _("Error while moving items to \"%s\".\n" - "You do not have permissions to write to the destination."); - break; - case XFER_LINK: - error_string = _("Error while linking items to \"%s\".\n" - "You do not have permissions to write to the destination."); - break; - default: - g_assert_not_reached (); - error_string = ""; - break; - } - } else { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying items to \"%s\".\n" - "The destination is not writable."); - break; - case XFER_MOVE_TO_TRASH: - case XFER_MOVE: - error_string = _("Error while moving items to \"%s\".\n" - "The destination is not writable."); - break; - case XFER_LINK: - error_string = _("Error while linking items to \"%s\".\n" - "The destination is not writable."); - break; - default: - g_assert_not_reached (); - error_string = ""; - break; - } - } + } else if (error_location == ERROR_LOCATION_SOURCE) { - if (progress_info->target_name != NULL) { - g_free (unescaped_name); - unescaped_name = nautilus_format_name_for_display ( - progress_info->target_name); - } + g_assert (source_name != NULL); + + switch (operation_kind) { + case XFER_COPY: + case XFER_DUPLICATE: + if (error_kind == ERROR_NOT_READABLE) { + error_string = _("Error while copying.\n" + "\"%s\" is not readable."); } - text = g_strdup_printf (error_string, unescaped_name); - g_free (unescaped_name); - - result = nautilus_simple_dialog - (parent_for_error_dialog (xfer_info), TRUE, text, - dialog_title, _("Stop"), NULL); - g_free (text); + break; + case XFER_LINK: + if (error_kind == ERROR_NOT_READABLE) { + error_string = _("Error while linking\n" + "\"%s\" is not readable."); + } + break; - return GNOME_VFS_XFER_ERROR_ACTION_ABORT; + default: + g_assert_not_reached (); + break; } - /* special case read only target errors */ - if (progress_info->vfs_status == GNOME_VFS_ERROR_NO_SPACE) { - - if (unescaped_name != NULL) { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying \"%s\".\n" - "There is no space on the destination."); - break; - case XFER_MOVE_TO_TRASH: - case XFER_MOVE: - error_string = _("Error while moving \"%s\".\n" - "There is no space on the destination."); - break; - case XFER_LINK: - error_string = _("Error while linking \"%s\".\n" - "There is no space on the destination."); - break; - default: - g_assert_not_reached (); - error_string = ""; - break; - } - text = g_strdup (error_string); - } else { - switch (xfer_info->kind) { - case XFER_COPY: - case XFER_DUPLICATE: - error_string = _("Error while copying.\n" - "There is no space on the destination."); - break; - case XFER_MOVE_TO_TRASH: - case XFER_MOVE: - error_string = _("Error while moving.\n" - "There is no space on the destination."); - break; - case XFER_LINK: - error_string = _("Error while linking.\n" - "There is no space on the destination."); - break; - default: - g_assert_not_reached (); - error_string = ""; - break; - } - text = g_strdup_printf (error_string, unescaped_name); - } - g_free (unescaped_name); - - result = nautilus_simple_dialog - (parent_for_error_dialog (xfer_info), TRUE, text, - dialog_title, _("Stop"), NULL); - g_free (text); - return GNOME_VFS_XFER_ERROR_ACTION_ABORT; + if (error_string != NULL) { + g_assert (source_name != NULL); + result = g_strdup_printf (error_string, source_name); } - if (unescaped_name != NULL) { - - switch (xfer_info->kind) { + } else if (error_location == ERROR_LOCATION_TARGET) { + + if (error_kind == ERROR_NO_SPACE) { + switch (operation_kind) { case XFER_COPY: case XFER_DUPLICATE: - error_string = _("Error \"%s\" while copying.\n" + error_string = _("Error while copying \"%s\".\n" + "There is no space on the destination."); + break; + case XFER_MOVE_TO_TRASH: + case XFER_MOVE: + error_string = _("Error while moving \"%s\".\n" + "There is no space on the destination."); + break; + case XFER_LINK: + error_string = _("Error while linking \"%s\".\n" + "There is no space on the destination."); + break; + default: + g_assert_not_reached (); + break; + } + } else { + switch (operation_kind) { + case XFER_COPY: + case XFER_DUPLICATE: + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while copying items to \"%s\".\n" + "You do not have permissions to write to " + "the destination."); + } else if (error_kind == ERROR_NOT_WRITABLE) { + error_string = _("Error while copying items to \"%s\".\n" + "The destination is not writable."); + } + break; + case XFER_MOVE: + case XFER_MOVE_TO_TRASH: + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while moving items \"%s\".\n" + "You do not have permissions to write to " + "the destination."); + } else if (error_kind == ERROR_NOT_WRITABLE) { + error_string = _("Error while moving items \"%s\".\n" + "The destination is not writable."); + } + + break; + case XFER_LINK: + if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) { + error_string = _("Error while creating links in \"%s\".\n" + "You do not have permissions to write to " + "the destination."); + } else if (error_kind == ERROR_NOT_WRITABLE) { + error_string = _("Error while creating links in \"%s\".\n" + "The destination is not writable."); + } + break; + default: + g_assert_not_reached (); + break; + } + } + if (error_string != NULL) { + g_assert (target_name != NULL); + result = g_strdup_printf (error_string, target_name); + } + } + + if (result == NULL) { + /* None of the specific error messages apply, use a catch-all + * generic error + */ + if (source_name != NULL) { + switch (operation_kind) { + case XFER_COPY: + case XFER_DUPLICATE: + error_string = _("Error \"%s\" while copying \"%s\".\n" "Would you like to continue?"); break; case XFER_MOVE: - error_string = _("Error \"%s\" while moving.\n" + error_string = _("Error \"%s\" while moving \"%s\".\n" "Would you like to continue?"); break; case XFER_LINK: - error_string = _("Error \"%s\" while linking.\n" + error_string = _("Error \"%s\" while linking \"%s\".\n" "Would you like to continue?"); break; case XFER_DELETE: case XFER_EMPTY_TRASH: case XFER_MOVE_TO_TRASH: - error_string = _("Error \"%s\" while deleting.\n" + error_string = _("Error \"%s\" while deleting \"%s\".\n" "Would you like to continue?"); break; default: - error_string = ""; + g_assert_not_reached (); break; } - text = g_strdup_printf (error_string, - gnome_vfs_result_to_string (progress_info->vfs_status)); + result = g_strdup_printf (error_string, + gnome_vfs_result_to_string (error), + source_name); } else { - - switch (xfer_info->kind) { + switch (operation_kind) { case XFER_COPY: case XFER_DUPLICATE: - error_string = _("Error \"%s\" while copying \"%s\".\n" + error_string = _("Error \"%s\" while copying.\n" "Would you like to continue?"); break; case XFER_MOVE: - error_string = _("Error \"%s\" while moving \"%s\".\n" + error_string = _("Error \"%s\" while moving.\n" "Would you like to continue?"); break; case XFER_LINK: - error_string = _("Error \"%s\" while linking \"%s\".\n" + error_string = _("Error \"%s\" while linking.\n" "Would you like to continue?"); break; case XFER_DELETE: case XFER_EMPTY_TRASH: case XFER_MOVE_TO_TRASH: - error_string = _("Error \"%s\" while deleting \"%s\".\n" + error_string = _("Error \"%s\" while deleting.\n" "Would you like to continue?"); break; default: - error_string = ""; + g_assert_not_reached (); break; } - text = g_strdup_printf (error_string, - gnome_vfs_result_to_string (progress_info->vfs_status), - unescaped_name); + result = g_strdup_printf (error_string, + gnome_vfs_result_to_string (error)); } - g_free (unescaped_name); + } + return result; +} - result = nautilus_simple_dialog - (parent_for_error_dialog (xfer_info), TRUE, text, - dialog_title, - _("Skip"), _("Retry"), _("Stop"), NULL); +static int +handle_xfer_vfs_error (const GnomeVFSXferProgressInfo *progress_info, + XferInfo *xfer_info) +{ + /* Notice that the error mode in `xfer_info' is the one we have been + * requested, but the transfer is always performed in mode + * `GNOME_VFS_XFER_ERROR_MODE_QUERY'. + */ - g_free (text); + int error_dialog_button_pressed; + int error_dialog_result; + char *text; + char *unescaped_source_name; + char *unescaped_target_name; + const char *dialog_title; + NautilusFileOperationsErrorKind error_kind; + NautilusFileOperationsErrorLocation error_location; + + switch (xfer_info->error_mode) { + case GNOME_VFS_XFER_ERROR_MODE_QUERY: + + /* transfer error, prompt the user to continue or stop */ + + unescaped_source_name = NULL; + unescaped_target_name = NULL; + + if (progress_info->source_name != NULL) { + unescaped_source_name = nautilus_format_name_for_display + (progress_info->source_name); + } + + if (progress_info->target_name != NULL) { + unescaped_target_name = nautilus_format_name_for_display + (progress_info->target_name); + } + + error_kind = ERROR_OTHER; + error_location = ERROR_LOCATION_UNKNOWN; - switch (result) { - case 0: - return GNOME_VFS_XFER_ERROR_ACTION_SKIP; - case 1: - return GNOME_VFS_XFER_ERROR_ACTION_RETRY; + /* Single out a few common error conditions for which we have + * custom-taylored error messages. + */ + if ((progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM + || progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY) + && (xfer_info->kind == XFER_DELETE + || xfer_info->kind == XFER_EMPTY_TRASH)) { + error_location = ERROR_LOCATION_SOURCE_PARENT; + error_kind = ERROR_READ_ONLY; + } else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED + && (xfer_info->kind == XFER_DELETE + || xfer_info->kind == XFER_EMPTY_TRASH)) { + error_location = ERROR_LOCATION_SOURCE_PARENT; + error_kind = ERROR_NOT_ENOUGH_PERMISSIONS; + } else if ((progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM + || progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY) + && (xfer_info->kind == XFER_MOVE + || xfer_info->kind == XFER_MOVE_TO_TRASH) + && progress_info->phase != GNOME_VFS_XFER_CHECKING_DESTINATION) { + error_location = ERROR_LOCATION_SOURCE_PARENT; + error_kind = ERROR_READ_ONLY; + } else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED + && (xfer_info->kind == XFER_MOVE + || xfer_info->kind == XFER_MOVE_TO_TRASH) + && progress_info->phase != GNOME_VFS_XFER_CHECKING_DESTINATION) { + error_location = ERROR_LOCATION_SOURCE_OR_PARENT; + error_kind = ERROR_NOT_ENOUGH_PERMISSIONS; + } else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED + && (xfer_info->kind == XFER_COPY + || xfer_info->kind == XFER_DUPLICATE) + && (progress_info->phase == GNOME_VFS_XFER_PHASE_OPENSOURCE + || progress_info->phase == GNOME_VFS_XFER_PHASE_COLLECTING + || progress_info->phase == GNOME_VFS_XFER_PHASE_INITIAL)) { + error_location = ERROR_LOCATION_SOURCE; + error_kind = ERROR_NOT_READABLE; + } else if ((progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM + || progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY) + && progress_info->phase == GNOME_VFS_XFER_CHECKING_DESTINATION) { + error_location = ERROR_LOCATION_TARGET; + error_kind = ERROR_NOT_WRITABLE; + } else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED + && progress_info->phase == GNOME_VFS_XFER_CHECKING_DESTINATION) { + error_location = ERROR_LOCATION_TARGET; + error_kind = ERROR_NOT_ENOUGH_PERMISSIONS; + } else if (progress_info->vfs_status == GNOME_VFS_ERROR_NO_SPACE) { + error_location = ERROR_LOCATION_TARGET; + error_kind = ERROR_NO_SPACE; + } + + text = build_error_string (unescaped_source_name, unescaped_target_name, + xfer_info->kind, error_kind, error_location, progress_info->vfs_status); + + switch (xfer_info->kind) { + case XFER_COPY: + case XFER_DUPLICATE: + dialog_title = _("Error while copying."); + break; + case XFER_MOVE: + dialog_title = _("Error while moving."); + break; + case XFER_LINK: + dialog_title = _("Error while linking."); + break; + case XFER_DELETE: + case XFER_EMPTY_TRASH: + case XFER_MOVE_TO_TRASH: + dialog_title = _("Error while deleting."); + break; default: - g_assert_not_reached (); - /* fall through */ - case 2: - return GNOME_VFS_XFER_ERROR_ACTION_ABORT; - } + dialog_title = NULL; + break; + } + if (error_location == ERROR_LOCATION_TARGET) { + /* We can't continue, just tell the user. */ + nautilus_simple_dialog (parent_for_error_dialog (xfer_info), + TRUE, text, dialog_title, _("Stop"), NULL); + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; + + } else if ((error_location == ERROR_LOCATION_SOURCE + || error_location == ERROR_LOCATION_SOURCE_PARENT + || error_location == ERROR_LOCATION_SOURCE_OR_PARENT) + && (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS + || error_kind == ERROR_NOT_READABLE)) { + /* The error could have happened on any of the files + * in the moved/copied/deleted hierarchy, we can probably + * continue. Allow the user to skip. + */ + error_dialog_button_pressed = nautilus_simple_dialog + (parent_for_error_dialog (xfer_info), TRUE, text, + dialog_title, + _("Skip"), _("Stop"), NULL); + + switch (error_dialog_button_pressed) { + case 0: + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_SKIP; + break; + case 1: + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; + break; + default: + g_assert_not_reached (); + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; + } + + } else { + /* Generic error, offer to retry and skip. */ + error_dialog_button_pressed = nautilus_simple_dialog + (parent_for_error_dialog (xfer_info), TRUE, text, + dialog_title, + _("Skip"), _("Retry"), _("Stop"), NULL); + + switch (error_dialog_button_pressed) { + case 0: + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_SKIP; + break; + case 1: + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_RETRY; + break; + case 2: + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; + break; + default: + g_assert_not_reached (); + error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; + } + } + + g_free (text); + return error_dialog_result; case GNOME_VFS_XFER_ERROR_MODE_ABORT: default: @@ -1946,24 +2060,11 @@ nautilus_file_operations_delete (const GList *item_uris, static void do_empty_trash (GtkWidget *parent_view) { - GnomeVFSURI *trash_dir_uri; - GnomeVFSResult result; XferInfo *xfer_info; GList *trash_dir_list; - /* FIXME bugzilla.eazel.com 638: - * add the different trash directories from the different volumes - */ - - trash_dir_uri = NULL; - trash_dir_list = NULL; - - result = gnome_vfs_find_directory (NULL, GNOME_VFS_DIRECTORY_KIND_TRASH, - &trash_dir_uri, FALSE, FALSE, 0777); - - if (result == GNOME_VFS_OK) { - g_assert (trash_dir_uri != NULL); - + trash_dir_list = nautilus_trash_monitor_get_trash_directories (); + if (trash_dir_list != NULL) { /* set up the move parameters */ xfer_info = g_new0 (XferInfo, 1); xfer_info->parent_view = parent_view; @@ -1982,8 +2083,6 @@ do_empty_trash (GtkWidget *parent_view) xfer_info->overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE; xfer_info->kind = XFER_EMPTY_TRASH; - trash_dir_list = g_list_append (NULL, trash_dir_uri); - gnome_vfs_async_xfer (&xfer_info->handle, trash_dir_list, NULL, GNOME_VFS_XFER_EMPTY_DIRECTORIES, GNOME_VFS_XFER_ERROR_MODE_QUERY, diff --git a/libnautilus-private/nautilus-trash-directory.c b/libnautilus-private/nautilus-trash-directory.c index 01d38f7e3..0e7210881 100644 --- a/libnautilus-private/nautilus-trash-directory.c +++ b/libnautilus-private/nautilus-trash-directory.c @@ -55,32 +55,6 @@ NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusTrashDirectory, nautilus_trash_directory, NAUTILUS_TYPE_MERGED_DIRECTORY) -static GnomeVFSURI * -get_volume_vfs_uri_if_writable (NautilusVolume *volume) -{ - char *uri; - GnomeVFSURI *vfs_uri; - - /* FIXME bugzilla.eazel.com 2443: - * Why is trash only found on EXT2 volumes? This seems - * like an incorrect check. - */ - - /* FIXME bugzilla.eazel.com 2444: - * Why can't we view the trash on a read-only volume - * if it happens to be there? - */ - if (volume->type != NAUTILUS_VOLUME_EXT2 || volume->is_read_only) { - return NULL; - } - - uri = gnome_vfs_get_uri_from_local_path (volume->mount_path); - vfs_uri = gnome_vfs_uri_new (uri); - g_free (uri); - - return vfs_uri; -} - static void find_directory_callback (GnomeVFSAsyncHandle *handle, GList *results, @@ -129,7 +103,7 @@ add_volume (NautilusTrashDirectory *trash, NautilusVolume *volume) { TrashVolume *trash_volume; - GnomeVFSURI *vfs_uri; + GnomeVFSURI *volume_mount_uri; GList vfs_uri_as_list; /* Quick out if we already know about this volume. */ @@ -139,11 +113,12 @@ add_volume (NautilusTrashDirectory *trash, return; } - /* If we can't get the URI, we jus don't do trash on this volume. */ - vfs_uri = get_volume_vfs_uri_if_writable (volume); - if (vfs_uri == NULL) { + if (!nautilus_volume_monitor_should_integrate_trash (volume)) { return; } + + volume_mount_uri = gnome_vfs_uri_new ( + nautilus_volume_monitor_get_volume_mount_uri (volume)); /* Make the structure used to track the trash for this volume. */ trash_volume = g_new0 (TrashVolume, 1); @@ -152,7 +127,7 @@ add_volume (NautilusTrashDirectory *trash, g_hash_table_insert (trash->details->volumes, volume, trash_volume); /* Find the real trash directory for this one. */ - vfs_uri_as_list.data = vfs_uri; + 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. */ diff --git a/libnautilus-private/nautilus-trash-monitor.c b/libnautilus-private/nautilus-trash-monitor.c index b70a565da..aca0a9d29 100644 --- a/libnautilus-private/nautilus-trash-monitor.c +++ b/libnautilus-private/nautilus-trash-monitor.c @@ -187,3 +187,57 @@ nautilus_trash_monitor_is_empty (void) { return nautilus_trash_monitor_get ()->details->empty; } + +static gboolean +add_one_volume_trash (const NautilusVolume *volume, + gpointer callback_data) +{ + GnomeVFSURI *volume_mount_point_uri; + GnomeVFSURI *trash_uri; + GList **result; + + result = (GList **)callback_data; + + if (nautilus_volume_monitor_should_integrate_trash (volume)) { + + /* Get the uri of the volume mount point as the place + * "near" which to look for trash on the given volume. + */ + volume_mount_point_uri = gnome_vfs_uri_new ( + nautilus_volume_monitor_get_volume_mount_uri (volume)); + + g_assert (volume_mount_point_uri != NULL); + + /* Look for trash. It is OK to use a sync call here because + * the options we use (don't create, don't look for it if we + * already don't know where it is) do not cause any IO. + */ + if (gnome_vfs_find_directory (volume_mount_point_uri, + GNOME_VFS_DIRECTORY_KIND_TRASH, &trash_uri, + FALSE, FALSE, 0777) == GNOME_VFS_OK) { + + /* found trash, put it on the list */ + *result = g_list_append (*result, trash_uri); + } + + gnome_vfs_uri_unref (volume_mount_point_uri); + } + + /* don't stop iterating */ + return FALSE; +} + +GList * +nautilus_trash_monitor_get_trash_directories (void) +{ + GList *result; + + result = NULL; + + /* Collect the trash directories on all the mounted volumes. */ + nautilus_volume_monitor_each_mounted_volume + (nautilus_volume_monitor_get (), add_one_volume_trash, &result); + + return result; +} + diff --git a/libnautilus-private/nautilus-trash-monitor.h b/libnautilus-private/nautilus-trash-monitor.h index 19139d514..41c726dcc 100644 --- a/libnautilus-private/nautilus-trash-monitor.h +++ b/libnautilus-private/nautilus-trash-monitor.h @@ -60,7 +60,6 @@ GtkType nautilus_trash_monitor_get_type (void); NautilusTrashMonitor *nautilus_trash_monitor_get (void); gboolean nautilus_trash_monitor_is_empty (void); -void nautilus_trash_monitor_async_get_trash_directories (GnomeVFSAsyncFindDirectoryCallback callback, - gpointer context); +GList * nautilus_trash_monitor_get_trash_directories (void); #endif diff --git a/libnautilus-private/nautilus-volume-monitor.c b/libnautilus-private/nautilus-volume-monitor.c index 0a75b3c03..dfd98d045 100644 --- a/libnautilus-private/nautilus-volume-monitor.c +++ b/libnautilus-private/nautilus-volume-monitor.c @@ -318,6 +318,28 @@ nautilus_volume_monitor_volume_is_mounted (const NautilusVolume *volume) return FALSE; } +gboolean +nautilus_volume_monitor_should_integrate_trash (const NautilusVolume *volume) +{ + /* Hand-pick a bunch of file system types that we know we can support + * trash on. It would probably be harder to keep a list of the ones + * we can't try to support trash on because the list would have to be + * more definitive. + */ + return volume->type == NAUTILUS_VOLUME_EXT2 + || volume->type == NAUTILUS_VOLUME_FAT + || volume->type == NAUTILUS_VOLUME_NFS + || volume->type == NAUTILUS_VOLUME_VFAT + || volume->type == NAUTILUS_VOLUME_FLOPPY + || volume->type == NAUTILUS_VOLUME_SMB; +} + +const char * +nautilus_volume_monitor_get_volume_mount_uri (const NautilusVolume *volume) +{ + return volume->mount_path; +} + static void mount_volume_cdrom_set_state (NautilusVolumeMonitor *monitor, NautilusVolume *volume) { diff --git a/libnautilus-private/nautilus-volume-monitor.h b/libnautilus-private/nautilus-volume-monitor.h index 08984fc65..d376e3b08 100644 --- a/libnautilus-private/nautilus-volume-monitor.h +++ b/libnautilus-private/nautilus-volume-monitor.h @@ -111,6 +111,8 @@ gboolean nautilus_volume_monitor_is_volume_link (con void nautilus_volume_monitor_each_volume (NautilusVolumeMonitor *monitor, NautilusEachVolumeFunction function, gpointer context); +gboolean nautilus_volume_monitor_should_integrate_trash (const NautilusVolume *volume); +const char *nautilus_volume_monitor_get_volume_mount_uri (const NautilusVolume *volume); void nautilus_volume_monitor_each_mounted_volume (NautilusVolumeMonitor *monitor, NautilusEachVolumeFunction function, gpointer context); |