[nautilus/wip/csoriano/operations: 1/4] progress-info: allow finished infos to remain alive



commit 4dcf7e34d338db5fc13239ee607a311233146480
Author: Carlos Soriano <csoriano gnome org>
Date:   Tue Jul 14 10:13:48 2015 +0200

    progress-info: allow finished infos to remain alive
    
    We want to not hide the operations and the operations button
    in the toolbar if the popover is open. For that, we need to
    allow operations widgets to remain alive even when the operation
    is finished.
    
    For that we need to add a new status state to copying/moving/trashing,
    like copied/moved/trashed.
    On the way, improve the wording of file-operations to match the mockups,
    and also make the report operations code more consistent to each other.

 libnautilus-private/nautilus-file-operations.c     |  569 ++++++++++++++------
 .../nautilus-progress-info-manager.c               |   29 +-
 .../nautilus-progress-info-manager.h               |    1 +
 libnautilus-private/nautilus-progress-info.c       |  271 ++++++----
 libnautilus-private/nautilus-progress-info.h       |    1 +
 src/nautilus-progress-info-widget.c                |   31 +-
 src/nautilus-progress-info-widget.h                |    2 +
 src/nautilus-toolbar-ui.xml                        |    1 +
 src/nautilus-toolbar.c                             |   57 ++-
 9 files changed, 666 insertions(+), 296 deletions(-)
---
diff --git a/libnautilus-private/nautilus-file-operations.c b/libnautilus-private/nautilus-file-operations.c
index 7762916..38bdf39 100644
--- a/libnautilus-private/nautilus-file-operations.c
+++ b/libnautilus-private/nautilus-file-operations.c
@@ -1453,29 +1453,50 @@ report_delete_progress (CommonJob *job,
        double elapsed, transfer_rate;
        int remaining_time;
        gint64 now;
-       char *files_left_s;
+       char *details;
+        char *status;
+        DeleteJob *delete_job;
 
+        delete_job = (DeleteJob *) job;
        now = g_get_monotonic_time ();
-       if (transfer_info->last_report_time != 0 &&
-           ABS ((gint64)(transfer_info->last_report_time - now)) < 100 * NSEC_PER_MICROSEC) {
-               return;
-       }
-       transfer_info->last_report_time = now;
-       
        files_left = source_info->num_files - transfer_info->num_files;
 
        /* Races and whatnot could cause this to be negative... */
        if (files_left < 0) {
-               files_left = 1;
+               files_left = 0;
        }
 
-       files_left_s = f (ngettext ("%'d file left to delete",
-                                   "%'d files left to delete",
-                                   files_left),
-                         files_left);
+        /* If the number of files left is 0, we want to update the status withouth
+         * considering this time, since we want to change the status to completed
+         * and probably we won't get more calls to this function */
+       if (transfer_info->last_report_time != 0 &&
+           ABS ((gint64)(transfer_info->last_report_time - now)) < 100 * NSEC_PER_MICROSEC &&
+            files_left > 0) {
+               return;
+       }
 
-       nautilus_progress_info_take_status (job->progress,
-                                           f (_("Deleting files")));
+       transfer_info->last_report_time = now;
+
+        if (source_info->num_files == 1) {
+                if (files_left == 0) {
+                        status = _("Deleted “%B”");
+                } else {
+                        status = _("Deleting “%B”");
+                }
+               nautilus_progress_info_take_status (job->progress,
+                                                   f (status,
+                                                       (GFile*) delete_job->files->data));
+
+        } else {
+                if (files_left == 0) {
+                        status = _("Deleted %'d files");
+                } else {
+                        status = _("Deleting %'d files");
+                }
+               nautilus_progress_info_take_status (job->progress,
+                                                   f (status,
+                                                       source_info->num_files));
+        }
 
        elapsed = g_timer_elapsed (job->time, NULL);
         transfer_rate = 0;
@@ -1487,23 +1508,35 @@ report_delete_progress (CommonJob *job,
        }
 
        if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE) {
-               nautilus_progress_info_set_details (job->progress, files_left_s);
+                if (files_left > 0) {
+                        details = f (_("%'d / %'d"),
+                                     transfer_info->num_files + 1,
+                                     source_info->num_files);
+                } else {
+                        details = f (_("%'d / %'d"),
+                                     transfer_info->num_files,
+                                     source_info->num_files);
+                }
        } else {
-               char *details, *time_left_s;
-
-               /* To translators: %T will expand to a time like "2 minutes".
-                * The singular/plural form will be used depending on the remaining time (i.e. the %T 
argument).
-                */
-               time_left_s = f (ngettext ("%T left",
-                                          "%T left",
-                                          seconds_count_format_time_units (remaining_time)),
-                                remaining_time);
-
-               details = g_strconcat (files_left_s, " \xE2\x80\x94 ", time_left_s, NULL);
-               nautilus_progress_info_take_details (job->progress, details);
-
-               g_free (time_left_s);
+                if (files_left > 0) {
+                       /* To translators: %T will expand to a time duration like "2 minutes".
+                         * So the whole thing will be something like "1 of 5 -- 2 hours left (4 files/sec)"
+                        *
+                        * The singular/plural form will be used depending on the remaining time (i.e. the %T 
argument).
+                        */
+                       details = f (ngettext ("%'d / %'d \xE2\x80\x94 %T left (%d files/sec)",
+                                              "%'d / %'d \xE2\x80\x94 %T left (%d files/sec)",
+                                              seconds_count_format_time_units (remaining_time)),
+                                     transfer_info->num_files + 1, source_info->num_files,
+                                     remaining_time,
+                                     (int) transfer_rate + 0.5);
+                } else {
+                        details = f (_("%'d / %'d"),
+                                     transfer_info->num_files,
+                                     source_info->num_files);
+                }
        }
+       nautilus_progress_info_set_details (job->progress, details);
 
         if (elapsed > SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE) {
                 nautilus_progress_info_set_remaining_time (job->progress,
@@ -1511,7 +1544,6 @@ report_delete_progress (CommonJob *job,
                 nautilus_progress_info_set_elapsed_time (job->progress,
                                                          elapsed);
         }
-       g_free (files_left_s);
 
        if (source_info->num_files != 0) {
                nautilus_progress_info_set_progress (job->progress, transfer_info->num_files, 
source_info->num_files);
@@ -1786,114 +1818,234 @@ delete_files (CommonJob *job, GList *files, int *files_skipped)
 }
 
 static void
-report_trash_progress (CommonJob *job,
-                      int files_trashed,
-                      int total_files)
+report_trash_progress (CommonJob    *job,
+                       SourceInfo   *source_info,
+                       TransferInfo *transfer_info)
 {
        int files_left;
-       char *s;
+       double elapsed, transfer_rate;
+       int remaining_time;
+       gint64 now;
+       char *details;
+        char *status;
+        DeleteJob *delete_job;
 
-       files_left = total_files - files_trashed;
+        delete_job = (DeleteJob *) job;
+       now = g_get_monotonic_time ();
+       files_left = source_info->num_files - transfer_info->num_files;
 
-       nautilus_progress_info_take_status (job->progress,
-                                           f (_("Moving files to trash")));
+       /* Races and whatnot could cause this to be negative... */
+       if (files_left < 0) {
+               files_left = 0;
+       }
+
+        /* If the number of files left is 0, we want to update the status withouth
+         * considering this time, since we want to change the status to completed
+         * and probably we won't get more calls to this function */
+       if (transfer_info->last_report_time != 0 &&
+           ABS ((gint64)(transfer_info->last_report_time - now)) < 100 * NSEC_PER_MICROSEC &&
+            files_left > 0) {
+               return;
+       }
+
+       transfer_info->last_report_time = now;
+
+        if (source_info->num_files == 1) {
+                if (files_left > 0) {
+                        status = _("Trashing “%B”");
+                } else {
+                        status = _("Trashed “%B”");
+                }
+               nautilus_progress_info_take_status (job->progress,
+                                                   f (status,
+                                                       (GFile*) delete_job->files->data));
+
+        } else {
+                if (files_left > 0) {
+                        status = _("Trashing %'d files");
+                } else {
+                        status = _("Trashed %'d files");
+                }
+               nautilus_progress_info_take_status (job->progress,
+                                                   f (status,
+                                                       source_info->num_files));
+        }
 
-       s = f (ngettext ("%'d file left to trash",
-                        "%'d files left to trash",
-                        files_left),
-              files_left);
-       nautilus_progress_info_take_details (job->progress, s);
 
-       if (total_files != 0) {
-               nautilus_progress_info_set_progress (job->progress, files_trashed, total_files);
+       elapsed = g_timer_elapsed (job->time, NULL);
+        transfer_rate = 0;
+        remaining_time = INT_MAX;
+       if (elapsed > 0) {
+               transfer_rate = transfer_info->num_files / elapsed;
+                if (transfer_rate > 0)
+                       remaining_time = (source_info->num_files - transfer_info->num_files) / transfer_rate;
        }
-}
 
+       if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE) {
+                if (files_left > 0) {
+                        details = f (_("%'d / %'d"),
+                                     transfer_info->num_files + 1,
+                                     source_info->num_files);
+                } else {
+                        details = f (_("%'d / %'d"),
+                                     transfer_info->num_files,
+                                     source_info->num_files);
+                }
+       } else {
+                if (files_left > 0) {
+                       /* To translators: %T will expand to a time duration like "2 minutes".
+                         * So the whole thing will be something like "1 of 5 -- 2 hours left (4 files/sec)"
+                        *
+                        * The singular/plural form will be used depending on the remaining time (i.e. the %T 
argument).
+                        */
+                       details = f (ngettext ("%'d / %'d \xE2\x80\x94 %T left (%d files/sec)",
+                                              "%'d / %'d \xE2\x80\x94 %T left (%d files/sec)",
+                                              seconds_count_format_time_units (remaining_time)),
+                                     transfer_info->num_files + 1, source_info->num_files,
+                                     remaining_time,
+                                     (int) transfer_rate + 0.5);
+                } else {
+                        details = f (_("%'d / %'d"),
+                                     transfer_info->num_files,
+                                     source_info->num_files);
+                }
+       }
+       nautilus_progress_info_set_details (job->progress, details);
+
+        if (elapsed > SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE) {
+                nautilus_progress_info_set_remaining_time (job->progress,
+                                                           remaining_time);
+                nautilus_progress_info_set_elapsed_time (job->progress,
+                                                         elapsed);
+        }
+
+       if (source_info->num_files != 0) {
+               nautilus_progress_info_set_progress (job->progress, transfer_info->num_files, 
source_info->num_files);
+       }
+}
 
 static void
-trash_files (CommonJob *job, GList *files, int *files_skipped)
+trash_file (CommonJob    *job,
+            GFile        *file,
+            gboolean     *skipped_file,
+            SourceInfo   *source_info,
+            TransferInfo *transfer_info,
+            gboolean      toplevel,
+            GList        *to_delete)
 {
-       GList *l;
-       GFile *file;
-       GList *to_delete;
        GError *error;
-       int total_files, files_trashed;
        char *primary, *secondary, *details;
        int response;
 
-       if (job_aborted (job)) {
+       if (should_skip_file (job, file)) {
+               *skipped_file = TRUE;
                return;
        }
 
-       total_files = g_list_length (files);
-       files_trashed = 0;
+       error = NULL;
 
-       report_trash_progress (job, files_trashed, total_files);
+       if (g_file_trash (file, job->cancellable, &error)) {
+               transfer_info->num_files ++;
+               nautilus_file_changes_queue_file_removed (file);
 
-       to_delete = NULL;
-       for (l = files;
-            l != NULL && !job_aborted (job);
-            l = l->next) {
-               file = l->data;
+               if (job->undo_info != NULL) {
+                       nautilus_file_undo_info_trash_add_file (NAUTILUS_FILE_UNDO_INFO_TRASH 
(job->undo_info), file);
+               }
 
-               error = NULL;
+               report_trash_progress (job, source_info, transfer_info);
+                return;
+       }
 
-               if (!g_file_trash (file, job->cancellable, &error)) {
-                       if (job->skip_all_error) {
-                               (*files_skipped)++;
-                               goto skip;
-                       }
+       if (job->skip_all_error) {
+               *skipped_file = TRUE;
+               goto skip;
+       }
 
-                       if (job->delete_all) {
-                               to_delete = g_list_prepend (to_delete, file);
-                               goto skip;
-                       }
+       if (job->delete_all) {
+               to_delete = g_list_prepend (to_delete, file);
+               goto skip;
+       }
 
-                       /* Translators: %B is a file name */
-                       primary = f (_("“%B” can't be put in the trash. Do you want to delete it 
immediately?"), file);
-                       details = NULL;
-                       secondary = NULL;
-                       if (!IS_IO_ERROR (error, NOT_SUPPORTED)) {
-                               details = error->message;
-                       } else if (!g_file_is_native (file)) {
-                               secondary = f (_("This remote location does not support sending items to the 
trash."));
-                       }
+       /* Translators: %B is a file name */
+       primary = f (_("“%B” can't be put in the trash. Do you want to delete it immediately?"), file);
+       details = NULL;
+       secondary = NULL;
+       if (!IS_IO_ERROR (error, NOT_SUPPORTED)) {
+               details = error->message;
+       } else if (!g_file_is_native (file)) {
+               secondary = f (_("This remote location does not support sending items to the trash."));
+       }
 
-                       response = run_question (job,
-                                                primary,
-                                                secondary,
-                                                details,
-                                                (total_files - files_trashed) > 1,
-                                                CANCEL, SKIP_ALL, SKIP, DELETE_ALL, DELETE,
-                                                NULL);
+       response = run_question (job,
+                                primary,
+                                secondary,
+                                details,
+                                (source_info->num_files - transfer_info->num_files) > 1,
+                                CANCEL, SKIP_ALL, SKIP, DELETE_ALL, DELETE,
+                                NULL);
 
-                       if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
-                               ((DeleteJob *) job)->user_cancel = TRUE;                                
-                               abort_job (job);
-                       } else if (response == 1) { /* skip all */
-                               (*files_skipped)++;
-                               job->skip_all_error = TRUE;
-                       } else if (response == 2) { /* skip */
-                               (*files_skipped)++;
-                       } else if (response == 3) { /* delete all */
-                               to_delete = g_list_prepend (to_delete, file);
-                               job->delete_all = TRUE;
-                       } else if (response == 4) { /* delete */
-                               to_delete = g_list_prepend (to_delete, file);
-                       }
+       if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+               ((DeleteJob *) job)->user_cancel = TRUE;
+               abort_job (job);
+       } else if (response == 1) { /* skip all */
+               *skipped_file = TRUE;
+               job->skip_all_error = TRUE;
+       } else if (response == 2) { /* skip */
+               *skipped_file = TRUE;
+               job->skip_all_error = TRUE;
+       } else if (response == 3) { /* delete all */
+               to_delete = g_list_prepend (to_delete, file);
+               job->delete_all = TRUE;
+       } else if (response == 4) { /* delete */
+               to_delete = g_list_prepend (to_delete, file);
+       }
 
-               skip:
-                       g_error_free (error);
-                       total_files--;
-               } else {
-                       nautilus_file_changes_queue_file_removed (file);
+skip:
+       g_error_free (error);
+}
 
-                       if (job->undo_info != NULL) {
-                               nautilus_file_undo_info_trash_add_file (NAUTILUS_FILE_UNDO_INFO_TRASH 
(job->undo_info), file);
-                       }
+static void
+trash_files (CommonJob *job,
+             GList     *files,
+             int       *files_skipped)
+{
+       GList *l;
+       GFile *file;
+       GList *to_delete;
+       SourceInfo source_info;
+       TransferInfo transfer_info;
+        gboolean skipped_file;
+
+       if (job_aborted (job)) {
+               return;
+       }
 
-                       files_trashed++;
-                       report_trash_progress (job, files_trashed, total_files);
+       scan_sources (files,
+                     &source_info,
+                     job,
+                     OP_KIND_TRASH);
+       if (job_aborted (job)) {
+               return;
+       }
+
+       g_timer_start (job->time);
+
+       memset (&transfer_info, 0, sizeof (transfer_info));
+       report_trash_progress (job, &source_info, &transfer_info);
+
+       to_delete = NULL;
+       for (l = files;
+            l != NULL && !job_aborted (job);
+            l = l->next) {
+               file = l->data;
+
+               skipped_file = FALSE;
+                trash_file (job, file,
+                            &skipped_file,
+                            &source_info, &transfer_info,
+                            TRUE, to_delete);
+               if (skipped_file) {
+                       (*files_skipped)++;
                }
        }
 
@@ -2968,6 +3120,8 @@ report_copy_progress (CopyMoveJob *copy_job,
        guint64 now;
        CommonJob *job;
        gboolean is_move;
+        gchar *status;
+        char *details;
 
        job = (CommonJob *)copy_job;
 
@@ -2975,73 +3129,103 @@ report_copy_progress (CopyMoveJob *copy_job,
        
        now = g_get_monotonic_time ();
 
-       if (transfer_info->last_report_time != 0 &&
-           ABS ((gint64)(transfer_info->last_report_time - now)) < 100 * NSEC_PER_MICROSEC) {
-               return;
-       }
-       transfer_info->last_report_time = now;
-       
        files_left = source_info->num_files - transfer_info->num_files;
 
        /* Races and whatnot could cause this to be negative... */
        if (files_left < 0) {
-               files_left = 1;
+               files_left = 0;
        }
 
+        /* If the number of files left is 0, we want to update the status withouth
+         * considering this time, since we want to change the status to completed
+         * and probably we won't get more calls to this function */
+       if (transfer_info->last_report_time != 0 &&
+           ABS ((gint64)(transfer_info->last_report_time - now)) < 100 * NSEC_PER_MICROSEC &&
+            files_left > 0) {
+               return;
+       }
+       transfer_info->last_report_time = now;
+
        if (files_left != transfer_info->last_reported_files_left ||
            transfer_info->last_reported_files_left == 0) {
                /* Avoid changing this unless files_left changed since last time */
                transfer_info->last_reported_files_left = files_left;
-               
+
                if (source_info->num_files == 1) {
                        if (copy_job->destination != NULL) {
+                                if (is_move) {
+                                        if (files_left > 0) {
+                                                status = _("Moving “%B” to “%B”");
+                                        } else {
+                                                status = _("Moved “%B” to “%B”");
+                                        }
+                                } else {
+                                        if (files_left > 0) {
+                                                status = _("Copying “%B” to “%B”");
+                                        } else {
+                                                status = _("Copied “%B” to “%B”");
+                                        }
+                                }
                                nautilus_progress_info_take_status (job->progress,
-                                                                   f (is_move ?
-                                                                      _("Moving “%B” to “%B”"):
-                                                                      _("Copying “%B” to “%B”"),
+                                                                   f (status,
                                                                       copy_job->fake_display_source != NULL ?
                                                                       copy_job->fake_display_source :
                                                                       (GFile *)copy_job->files->data,
                                                                       copy_job->destination));
                        } else {
+                                if (files_left > 0) {
+                                        status = _("Duplicating “%B”");
+                                } else {
+                                        status = _("Duplicated “%B”");
+                                }
                                nautilus_progress_info_take_status (job->progress,
-                                                                   f (_("Duplicating “%B”"),
-                                                                      (GFile *)copy_job->files->data));
-                       }
-               } else if (copy_job->files != NULL &&
-                          copy_job->files->next == NULL) {
-                       if (copy_job->destination != NULL) {
-                               nautilus_progress_info_take_status (job->progress,
-                                                                   f (is_move ?
-                                                                      _("Moving file %'d of %'d (in “%B”) to 
“%B”")
-                                                                      :
-                                                                      _("Copying file %'d of %'d (in “%B”) 
to “%B”"),
-                                                                      transfer_info->num_files + 1,
-                                                                      source_info->num_files,
-                                                                      (GFile *)copy_job->files->data,
-                                                                      copy_job->destination));
-                       } else {
-                               nautilus_progress_info_take_status (job->progress,
-                                                                   f (_("Duplicating file %'d of %'d (in 
“%B”)"),
-                                                                      transfer_info->num_files + 1,
-                                                                      source_info->num_files,
+                                                                   f (status,
                                                                       (GFile *)copy_job->files->data));
                        }
-               } else {
+               } else if (copy_job->files != NULL) {
                        if (copy_job->destination != NULL) {
-                               nautilus_progress_info_take_status (job->progress,
-                                                                   f (is_move ?
-                                                                      _("Moving file %'d of %'d to “%B”")
-                                                                      :
-                                                                      _ ("Copying file %'d of %'d to “%B”"),
-                                                                      transfer_info->num_files + 1,
-                                                                      source_info->num_files,
-                                                                      copy_job->destination));
+                                if (files_left > 0) {
+                                        if (is_move) {
+                                                status = _("Moving %'d files to “%B”");
+                                        } else {
+                                                status = _("Copying %'d files to “%B”");
+                                        }
+                                       nautilus_progress_info_take_status (job->progress,
+                                                                           f (status,
+                                                                              source_info->num_files,
+                                                                              (GFile *)copy_job->destination,
+                                                                              copy_job->destination));
+                                } else {
+                                        if (is_move) {
+                                                status = _("Moved %'d files to “%B”");
+                                        } else {
+                                                status = _("Copied %'d files to “%B”");
+                                        }
+                                       nautilus_progress_info_take_status (job->progress,
+                                                                           f (status,
+                                                                              source_info->num_files,
+                                                                              (GFile *)copy_job->destination,
+                                                                              copy_job->destination));
+                                }
                        } else {
-                               nautilus_progress_info_take_status (job->progress,
-                                                                   f (_("Duplicating file %'d of %'d"),
-                                                                      transfer_info->num_files + 1,
-                                                                      source_info->num_files));
+                                GFile *parent;
+
+                                parent = g_file_get_parent (copy_job->files->data);
+                                if (files_left > 0) {
+                                        status = _("Duplicating %'d files in “%B”");
+                                       nautilus_progress_info_take_status (job->progress,
+                                                                           f (status,
+                                                                              transfer_info->num_files + 1,
+                                                                              source_info->num_files,
+                                                                              parent));
+                                } else {
+                                        status = _("Duplicated %'d files in “%B”");
+                                       nautilus_progress_info_take_status (job->progress,
+                                                                           f (status,
+                                                                              source_info->num_files,
+                                                                              parent));
+                                }
+                                g_object_unref (parent);
                        }
                }
        }
@@ -3057,28 +3241,63 @@ report_copy_progress (CopyMoveJob *copy_job,
                        remaining_time = (total_size - transfer_info->num_bytes) / transfer_rate;
        }
 
-       if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE &&
-           transfer_rate > 0) {
-               char *s;
-               /* To translators: %S will expand to a size like "2 bytes" or "3 MB", so something like "4 kb 
of 4 MB" */               
-               s = f (_("%S of %S"), transfer_info->num_bytes, total_size);
-               nautilus_progress_info_take_details (job->progress, s);
+       if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE &&
+            transfer_rate > 0) {
+                if (source_info->num_files == 1) {
+                       /* To translators: %S will expand to a size like "2 bytes" or "3 MB", so something 
like "4 kb of 4 MB" */
+                       details = f (_("%S / %S"), transfer_info->num_bytes, total_size);
+                } else {
+                        if (files_left > 0) {
+                               details = f (_("%'d / %'d"),
+                                             transfer_info->num_files + 1,
+                                             source_info->num_files);
+                        } else {
+                                details = f (_("%'d / %'d"),
+                                             transfer_info->num_files,
+                                             source_info->num_files);
+                        }
+                }
        } else {
-               char *s;
-
-               /* To translators: %S will expand to a size like "2 bytes" or "3 MB", %T to a time duration 
like
-                * "2 minutes". So the whole thing will be something like "2 kb of 4 MB -- 2 hours left 
(4kb/sec)"
-                *
-                * The singular/plural form will be used depending on the remaining time (i.e. the %T 
argument).
-                */             
-               s = f (ngettext ("%S of %S \xE2\x80\x94 %T left (%S/sec)",
-                                "%S of %S \xE2\x80\x94 %T left (%S/sec)",
-                                seconds_count_format_time_units (remaining_time)),
-                      transfer_info->num_bytes, total_size,
-                      remaining_time,
-                      (goffset)transfer_rate);
-               nautilus_progress_info_take_details (job->progress, s);
+                if (source_info->num_files == 1) {
+                        if (files_left > 0) {
+                               /* To translators: %S will expand to a size like "2 bytes" or "3 MB", %T to a 
time duration like
+                                * "2 minutes". So the whole thing will be something like "2 kb / 4 MB -- 2 
hours left (4kb/sec)"
+                                *
+                                * The singular/plural form will be used depending on the remaining time 
(i.e. the %T argument).
+                                */
+                               details = f (ngettext ("%S / %S \xE2\x80\x94 %T left (%S/sec)",
+                                                      "%S / %S \xE2\x80\x94 %T left (%S/sec)",
+                                                      seconds_count_format_time_units (remaining_time)),
+                                             transfer_info->num_bytes, total_size,
+                                             remaining_time,
+                                             (goffset)transfer_rate);
+                        } else {
+                               /* To translators: %S will expand to a size like "2 bytes" or "3 MB". */
+                                details = f (_("%S / %S"),
+                                             transfer_info->num_bytes,
+                                             total_size);
+                        }
+                } else {
+                        if (files_left > 0) {
+                               /* To translators: %T will expand to a time duration like "2 minutes".
+                                 * So the whole thing will be something like "1 / 5 -- 2 hours left 
(4kb/sec)"
+                                *
+                                * The singular/plural form will be used depending on the remaining time 
(i.e. the %T argument).
+                                */
+                               details = f (ngettext ("%'d / %'d \xE2\x80\x94 %T left (%S/sec)",
+                                                      "%'d / %'d \xE2\x80\x94 %T left (%S/sec)",
+                                                      seconds_count_format_time_units (remaining_time)),
+                                             transfer_info->num_files + 1, source_info->num_files,
+                                             remaining_time,
+                                             (goffset)transfer_rate);
+                        } else {
+                                details = f (_("%'d / %'d"),
+                                             transfer_info->num_files,
+                                             source_info->num_files);
+                        }
+                }
        }
+       nautilus_progress_info_take_details (job->progress, details);
 
         if (elapsed > SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE) {
                 nautilus_progress_info_set_remaining_time (job->progress,
diff --git a/libnautilus-private/nautilus-progress-info-manager.c 
b/libnautilus-private/nautilus-progress-info-manager.c
index 661f5da..e2358ac 100644
--- a/libnautilus-private/nautilus-progress-info-manager.c
+++ b/libnautilus-private/nautilus-progress-info-manager.c
@@ -102,14 +102,6 @@ nautilus_progress_info_manager_class_init (NautilusProgressInfoManagerClass *kla
        g_type_class_add_private (klass, sizeof (NautilusProgressInfoManagerPriv));
 }
 
-static void
-progress_info_finished_cb (NautilusProgressInfo *info,
-                          NautilusProgressInfoManager *self)
-{
-       self->priv->progress_infos =
-               g_list_remove (self->priv->progress_infos, info);
-}
-
 NautilusProgressInfoManager *
 nautilus_progress_info_manager_dup_singleton (void)
 {
@@ -128,12 +120,27 @@ nautilus_progress_info_manager_add_new_info (NautilusProgressInfoManager *self,
        self->priv->progress_infos =
                g_list_prepend (self->priv->progress_infos, g_object_ref (info));
 
-       g_signal_connect (info, "finished",
-                         G_CALLBACK (progress_info_finished_cb), self);
-
        g_signal_emit (self, signals[NEW_PROGRESS_INFO], 0, info);
 }
 
+void
+nautilus_progress_info_manager_remove_finished_infos (NautilusProgressInfoManager *self)
+{
+        GList *l;
+        GList *next;
+
+        l = self->priv->progress_infos;
+        while (l != NULL) {
+                next = l->next;
+                if (nautilus_progress_info_get_is_finished (l->data) ||
+                    nautilus_progress_info_get_is_cancelled (l->data)) {
+                        self->priv->progress_infos = g_list_remove (self->priv->progress_infos,
+                                                                    l->data);
+                }
+                l = next;
+        }
+}
+
 GList *
 nautilus_progress_info_manager_get_all_infos (NautilusProgressInfoManager *self)
 {
diff --git a/libnautilus-private/nautilus-progress-info-manager.h 
b/libnautilus-private/nautilus-progress-info-manager.h
index 7198bd3..39a377b 100644
--- a/libnautilus-private/nautilus-progress-info-manager.h
+++ b/libnautilus-private/nautilus-progress-info-manager.h
@@ -62,6 +62,7 @@ NautilusProgressInfoManager* nautilus_progress_info_manager_dup_singleton (void)
 void nautilus_progress_info_manager_add_new_info (NautilusProgressInfoManager *self,
                                                   NautilusProgressInfo *info);
 GList *nautilus_progress_info_manager_get_all_infos (NautilusProgressInfoManager *self);
+void nautilus_progress_info_manager_remove_finished_infos (NautilusProgressInfoManager *self);
 
 G_END_DECLS
 
diff --git a/libnautilus-private/nautilus-progress-info.c b/libnautilus-private/nautilus-progress-info.c
index 389aaeb..ddec248 100644
--- a/libnautilus-private/nautilus-progress-info.c
+++ b/libnautilus-private/nautilus-progress-info.c
@@ -34,6 +34,7 @@ enum {
   PROGRESS_CHANGED,
   STARTED,
   FINISHED,
+  CANCELLED,
   LAST_SIGNAL
 };
 
@@ -46,6 +47,8 @@ struct _NautilusProgressInfo
        GObject parent_instance;
        
        GCancellable *cancellable;
+        guint cancellable_id;
+        GCancellable *details_in_thread_cancellable;
        
        char *status;
        char *details;
@@ -62,6 +65,7 @@ struct _NautilusProgressInfo
        
        gboolean start_at_idle;
        gboolean finish_at_idle;
+       gboolean cancel_at_idle;
        gboolean changed_at_idle;
        gboolean progress_at_idle;
 };
@@ -84,7 +88,10 @@ nautilus_progress_info_finalize (GObject *object)
 
        g_free (info->status);
        g_free (info->details);
+        g_cancellable_disconnect (info->cancellable, info->cancellable_id);
        g_object_unref (info->cancellable);
+        g_cancellable_cancel (info->details_in_thread_cancellable);
+        g_clear_object (&info->details_in_thread_cancellable);
        
        if (G_OBJECT_CLASS (nautilus_progress_info_parent_class)->finalize) {
                (*G_OBJECT_CLASS (nautilus_progress_info_parent_class)->finalize) (object);
@@ -155,6 +162,155 @@ nautilus_progress_info_class_init (NautilusProgressInfoClass *klass)
                              g_cclosure_marshal_VOID__VOID,
                              G_TYPE_NONE, 0);
        
+       signals[CANCELLED] =
+               g_signal_new ("cancelled",
+                             NAUTILUS_TYPE_PROGRESS_INFO,
+                             G_SIGNAL_RUN_LAST,
+                             0,
+                             NULL, NULL,
+                             g_cclosure_marshal_VOID__VOID,
+                             G_TYPE_NONE, 0);
+}
+
+static gboolean
+idle_callback (gpointer data)
+{
+       NautilusProgressInfo *info = data;
+       gboolean start_at_idle;
+       gboolean finish_at_idle;
+       gboolean changed_at_idle;
+       gboolean progress_at_idle;
+       gboolean cancelled_at_idle;
+       GSource *source;
+
+       source = g_main_current_source ();
+
+       G_LOCK (progress_info);
+
+       /* Protect agains races where the source has
+          been destroyed on another thread while it
+          was being dispatched.
+          Similar to what gdk_threads_add_idle does.
+       */
+       if (g_source_is_destroyed (source)) {
+               G_UNLOCK (progress_info);
+               return FALSE;
+       }
+
+       /* We hadn't destroyed the source, so take a ref.
+        * This might ressurect the object from dispose, but
+        * that should be ok.
+        */
+       g_object_ref (info);
+
+       g_assert (source == info->idle_source);
+
+       g_source_unref (source);
+       info->idle_source = NULL;
+
+       start_at_idle = info->start_at_idle;
+       finish_at_idle = info->finish_at_idle;
+       changed_at_idle = info->changed_at_idle;
+       progress_at_idle = info->progress_at_idle;
+       cancelled_at_idle = info->cancel_at_idle;
+
+       info->start_at_idle = FALSE;
+       info->finish_at_idle = FALSE;
+       info->changed_at_idle = FALSE;
+       info->progress_at_idle = FALSE;
+       info->cancel_at_idle = FALSE;
+
+       G_UNLOCK (progress_info);
+
+       if (start_at_idle) {
+               g_signal_emit (info,
+                              signals[STARTED],
+                              0);
+       }
+
+       if (changed_at_idle) {
+               g_signal_emit (info,
+                              signals[CHANGED],
+                              0);
+       }
+
+       if (progress_at_idle) {
+               g_signal_emit (info,
+                              signals[PROGRESS_CHANGED],
+                              0);
+       }
+
+       if (finish_at_idle) {
+               g_signal_emit (info,
+                              signals[FINISHED],
+                              0);
+       }
+
+       if (cancelled_at_idle) {
+               g_signal_emit (info,
+                              signals[CANCELLED],
+                              0);
+       }
+
+       g_object_unref (info);
+
+       return FALSE;
+}
+
+
+/* Called with lock held */
+static void
+queue_idle (NautilusProgressInfo *info, gboolean now)
+{
+       if (info->idle_source == NULL ||
+           (now && !info->source_is_now)) {
+               if (info->idle_source) {
+                       g_source_destroy (info->idle_source);
+                       g_source_unref (info->idle_source);
+                       info->idle_source = NULL;
+               }
+
+               info->source_is_now = now;
+               if (now) {
+                       info->idle_source = g_idle_source_new ();
+               } else {
+                       info->idle_source = g_timeout_source_new (SIGNAL_DELAY_MSEC);
+               }
+               g_source_set_callback (info->idle_source, idle_callback, info, NULL);
+               g_source_attach (info->idle_source, NULL);
+       }
+}
+
+static void
+set_details_in_thread (GTask                *task,
+                       NautilusProgressInfo *info,
+                       gpointer              user_data,
+                       GCancellable         *cancellable)
+{
+        if (!g_cancellable_is_cancelled (cancellable)) {
+                nautilus_progress_info_set_details  (info, _("Cancelled"));
+                G_LOCK (progress_info);
+               info->cancel_at_idle = TRUE;
+               queue_idle (info, TRUE);
+                G_UNLOCK (progress_info);
+        }
+}
+
+static void
+on_canceled (GCancellable         *cancellable,
+             NautilusProgressInfo *info)
+{
+        GTask *task;
+
+        /* We can't do any lock operaton here, since this is probably the main
+         * thread, so modify the details in another thread. Also it can happens
+         * that we were finalizing the object, so create a new cancellable here
+         * so it can be cancelled in finalize */
+        info->details_in_thread_cancellable = g_cancellable_new ();
+        task = g_task_new (info, info->details_in_thread_cancellable, NULL, NULL);
+        g_task_run_in_thread (task, (GTaskThreadFunc) set_details_in_thread);
+
+        g_object_unref (task);
 }
 
 static void
@@ -163,6 +319,10 @@ nautilus_progress_info_init (NautilusProgressInfo *info)
        NautilusProgressInfoManager *manager;
 
        info->cancellable = g_cancellable_new ();
+        info->cancellable_id = g_cancellable_connect (info->cancellable,
+                                                      G_CALLBACK (on_canceled),
+                                                      info,
+                                                      NULL);
 
        manager = nautilus_progress_info_manager_dup_singleton ();
        nautilus_progress_info_manager_add_new_info (manager, info);
@@ -258,6 +418,18 @@ nautilus_progress_info_get_cancellable (NautilusProgressInfo *info)
 }
 
 gboolean
+nautilus_progress_info_get_is_cancelled (NautilusProgressInfo *info)
+{
+        gboolean cancelled;
+
+        G_LOCK (progress_info);
+        cancelled = g_cancellable_is_cancelled (info->cancellable);
+        G_UNLOCK (progress_info);
+
+        return cancelled;
+}
+
+gboolean
 nautilus_progress_info_get_is_started (NautilusProgressInfo *info)
 {
        gboolean res;
@@ -299,105 +471,6 @@ nautilus_progress_info_get_is_paused (NautilusProgressInfo *info)
        return res;
 }
 
-static gboolean
-idle_callback (gpointer data)
-{
-       NautilusProgressInfo *info = data;
-       gboolean start_at_idle;
-       gboolean finish_at_idle;
-       gboolean changed_at_idle;
-       gboolean progress_at_idle;
-       GSource *source;
-
-       source = g_main_current_source ();
-       
-       G_LOCK (progress_info);
-
-       /* Protect agains races where the source has
-          been destroyed on another thread while it
-          was being dispatched.
-          Similar to what gdk_threads_add_idle does.
-       */
-       if (g_source_is_destroyed (source)) {
-               G_UNLOCK (progress_info);
-               return FALSE;
-       }
-
-       /* We hadn't destroyed the source, so take a ref.
-        * This might ressurect the object from dispose, but
-        * that should be ok.
-        */
-       g_object_ref (info);
-
-       g_assert (source == info->idle_source);
-       
-       g_source_unref (source);
-       info->idle_source = NULL;
-       
-       start_at_idle = info->start_at_idle;
-       finish_at_idle = info->finish_at_idle;
-       changed_at_idle = info->changed_at_idle;
-       progress_at_idle = info->progress_at_idle;
-       
-       info->start_at_idle = FALSE;
-       info->finish_at_idle = FALSE;
-       info->changed_at_idle = FALSE;
-       info->progress_at_idle = FALSE;
-       
-       G_UNLOCK (progress_info);
-       
-       if (start_at_idle) {
-               g_signal_emit (info,
-                              signals[STARTED],
-                              0);
-       }
-       
-       if (changed_at_idle) {
-               g_signal_emit (info,
-                              signals[CHANGED],
-                              0);
-       }
-       
-       if (progress_at_idle) {
-               g_signal_emit (info,
-                              signals[PROGRESS_CHANGED],
-                              0);
-       }
-       
-       if (finish_at_idle) {
-               g_signal_emit (info,
-                              signals[FINISHED],
-                              0);
-       }
-       
-       g_object_unref (info);
-       
-       return FALSE;
-}
-
-/* Called with lock held */
-static void
-queue_idle (NautilusProgressInfo *info, gboolean now)
-{
-       if (info->idle_source == NULL ||
-           (now && !info->source_is_now)) {
-               if (info->idle_source) {
-                       g_source_destroy (info->idle_source);
-                       g_source_unref (info->idle_source);
-                       info->idle_source = NULL;
-               }
-               
-               info->source_is_now = now;
-               if (now) {
-                       info->idle_source = g_idle_source_new ();
-               } else {
-                       info->idle_source = g_timeout_source_new (SIGNAL_DELAY_MSEC);
-               }
-               g_source_set_callback (info->idle_source, idle_callback, info, NULL);
-               g_source_attach (info->idle_source, NULL);
-       }
-}
-
 void
 nautilus_progress_info_pause (NautilusProgressInfo *info)
 {
diff --git a/libnautilus-private/nautilus-progress-info.h b/libnautilus-private/nautilus-progress-info.h
index 0ce0ad1..44b812c 100644
--- a/libnautilus-private/nautilus-progress-info.h
+++ b/libnautilus-private/nautilus-progress-info.h
@@ -60,6 +60,7 @@ void          nautilus_progress_info_cancel          (NautilusProgressInfo *info
 gboolean      nautilus_progress_info_get_is_started  (NautilusProgressInfo *info);
 gboolean      nautilus_progress_info_get_is_finished (NautilusProgressInfo *info);
 gboolean      nautilus_progress_info_get_is_paused   (NautilusProgressInfo *info);
+gboolean      nautilus_progress_info_get_is_cancelled (NautilusProgressInfo *info);
 
 void          nautilus_progress_info_start           (NautilusProgressInfo *info);
 void          nautilus_progress_info_finish          (NautilusProgressInfo *info);
diff --git a/src/nautilus-progress-info-widget.c b/src/nautilus-progress-info-widget.c
index 7834676..ba0334e 100644
--- a/src/nautilus-progress-info-widget.c
+++ b/src/nautilus-progress-info-widget.c
@@ -47,7 +47,13 @@ G_DEFINE_TYPE_WITH_PRIVATE (NautilusProgressInfoWidget, nautilus_progress_info_w
 static void
 info_finished (NautilusProgressInfoWidget *self)
 {
-       gtk_widget_destroy (GTK_WIDGET (self));
+       gtk_widget_set_sensitive (self->priv->cancel, FALSE);
+}
+
+static void
+info_cancelled (NautilusProgressInfoWidget *self)
+{
+       gtk_widget_set_sensitive (self->priv->cancel, FALSE);
 }
 
 static void
@@ -85,7 +91,6 @@ cancel_clicked (GtkWidget *button,
                NautilusProgressInfoWidget *self)
 {
        nautilus_progress_info_cancel (self->priv->info);
-       gtk_widget_set_sensitive (button, FALSE);
 }
 
 static void
@@ -117,6 +122,9 @@ nautilus_progress_info_widget_constructed (GObject *obj)
        g_signal_connect_swapped (self->priv->info,
                                  "finished",
                                  G_CALLBACK (info_finished), self);
+       g_signal_connect_swapped (self->priv->info,
+                                 "cancelled",
+                                 G_CALLBACK (info_cancelled), self);
 
        update_data (self);
        update_progress (self);
@@ -183,10 +191,27 @@ nautilus_progress_info_widget_class_init (NautilusProgressInfoWidgetClass *klass
        gtk_widget_class_bind_template_child_private (widget_class, NautilusProgressInfoWidget, cancel);
 }
 
+void
+nautilus_progress_info_widget_destroy_if_finished (NautilusProgressInfoWidget *self)
+{
+        if (nautilus_progress_info_get_is_finished (self->priv->info) ||
+            nautilus_progress_info_get_is_cancelled (self->priv->info)) {
+                gtk_widget_destroy (GTK_WIDGET (self));
+        }
+}
+
 GtkWidget *
 nautilus_progress_info_widget_new (NautilusProgressInfo *info)
 {
-       return g_object_new (NAUTILUS_TYPE_PROGRESS_INFO_WIDGET,
+        NautilusProgressInfoWidget *self;
+
+       self = g_object_new (NAUTILUS_TYPE_PROGRESS_INFO_WIDGET,
                             "info", info,
                             NULL);
+
+        gtk_widget_set_sensitive (self->priv->cancel,
+                                  !nautilus_progress_info_get_is_finished (self->priv->info) &&
+                                  !nautilus_progress_info_get_is_cancelled (self->priv->info));
+
+        return GTK_WIDGET (self);
 }
diff --git a/src/nautilus-progress-info-widget.h b/src/nautilus-progress-info-widget.h
index f1c99c0..22bf7e3 100644
--- a/src/nautilus-progress-info-widget.h
+++ b/src/nautilus-progress-info-widget.h
@@ -58,4 +58,6 @@ GType nautilus_progress_info_widget_get_type (void);
 
 GtkWidget * nautilus_progress_info_widget_new (NautilusProgressInfo *info);
 
+void nautilus_progress_info_widget_destroy_if_finished (NautilusProgressInfoWidget *self);
+
 #endif /* __NAUTILUS_PROGRESS_INFO_WIDGET_H__ */
diff --git a/src/nautilus-toolbar-ui.xml b/src/nautilus-toolbar-ui.xml
index 42051b2..791ad37 100644
--- a/src/nautilus-toolbar-ui.xml
+++ b/src/nautilus-toolbar-ui.xml
@@ -179,6 +179,7 @@
   <object class="GtkPopover" id="operations_popover">
     <property name="modal">false</property>
     <property name="relative-to">operations_button</property>
+    <signal name="closed" handler="remove_finished_operations" object="NautilusToolbar" swapped="yes"/>
     <child>
       <object class="GtkScrolledWindow">
         <property name="visible">True</property>
diff --git a/src/nautilus-toolbar.c b/src/nautilus-toolbar.c
index 6602279..aa1ae1a 100644
--- a/src/nautilus-toolbar.c
+++ b/src/nautilus-toolbar.c
@@ -434,8 +434,7 @@ view_menu_popover_closed (GtkPopover *popover,
 }
 
 static void
-on_progress_info_finished (NautilusProgressInfo *info,
-                           NautilusToolbar      *self)
+update_operations_button_visibility (NautilusToolbar *self)
 {
         GList *progress_infos;
 
@@ -447,6 +446,35 @@ on_progress_info_finished (NautilusProgressInfo *info,
 }
 
 static void
+remove_finished_operations (NautilusToolbar *self)
+{
+        nautilus_progress_info_manager_remove_finished_infos (self->priv->progress_manager);
+        gtk_container_foreach (GTK_CONTAINER (self->priv->operations_container),
+                               (GtkCallback) nautilus_progress_info_widget_destroy_if_finished,
+                               NULL);
+        update_operations_button_visibility (self);
+}
+
+static void
+on_progress_info_finished (NautilusProgressInfo *info,
+                           NautilusToolbar      *self)
+{
+        /* Update the pie chart progress */
+        gtk_widget_queue_draw (self->priv->operations_icon);
+        if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->operations_button))) {
+                remove_finished_operations (self);
+        }
+}
+
+static void
+on_progress_info_cancelled (NautilusProgressInfo *info,
+                            NautilusToolbar      *self)
+{
+        /* Update the pie chart progress */
+        gtk_widget_queue_draw (self->priv->operations_icon);
+}
+
+static void
 on_progress_info_progress_changed (NautilusProgressInfo *info,
                                    NautilusToolbar      *self)
 {
@@ -476,6 +504,8 @@ update_operations (NautilusToolbar *self)
 
                        g_signal_connect (l->data, "finished",
                                          G_CALLBACK (on_progress_info_finished), self);
+                       g_signal_connect (l->data, "cancelled",
+                                         G_CALLBACK (on_progress_info_cancelled), self);
                        g_signal_connect (l->data, "progress-changed",
                                          G_CALLBACK (on_progress_info_progress_changed), self);
                         progress = nautilus_progress_info_widget_new (l->data);
@@ -540,21 +570,31 @@ on_operations_icon_draw (GtkWidget       *widget,
         GList *l;
         guint width;
         guint height;
+        gboolean all_cancelled;
         GdkRGBA background = {.red = 0, .green = 0, .blue = 0, .alpha = 0.2 };
         GdkRGBA foreground = {.red = 0, .green = 0, .blue = 0, .alpha = 0.7 };
 
+        all_cancelled = TRUE;
         progress_infos = nautilus_progress_info_manager_get_all_infos (self->priv->progress_manager);
         for (l = progress_infos; l != NULL; l = l->next) {
-                remaining_progress += nautilus_progress_info_get_remaining_time (l->data);
-                elapsed_progress += nautilus_progress_info_get_elapsed_time (l->data);
+                if (!nautilus_progress_info_get_is_cancelled (l->data)) {
+                        all_cancelled = FALSE;
+                        remaining_progress += nautilus_progress_info_get_remaining_time (l->data);
+                        elapsed_progress += nautilus_progress_info_get_elapsed_time (l->data);
+                }
         }
 
         total_progress = remaining_progress + elapsed_progress;
 
-        if (total_progress > 0)
-                ratio = MAX (0.05, elapsed_progress / total_progress);
-        else
-                ratio = 0.05;
+        if (all_cancelled) {
+                ratio = 1.0;
+        } else {
+                if (total_progress > 0) {
+                        ratio = MAX (0.05, elapsed_progress / total_progress);
+                } else {
+                        ratio = 0.05;
+                }
+        }
 
 
         width = gtk_widget_get_allocated_width (widget);
@@ -751,6 +791,7 @@ nautilus_toolbar_class_init (NautilusToolbarClass *klass)
        gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, forward_button);
 
         gtk_widget_class_bind_template_callback (widget_class, on_operations_icon_draw);
+        gtk_widget_class_bind_template_callback (widget_class, remove_finished_operations);
 }
 
 void



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