[Xfce4-commits] <thunar:master> Show transfer rate in transfer dialog (bug #8250).

Nick Schermer noreply at xfce.org
Sun Sep 30 18:34:01 CEST 2012


Updating branch refs/heads/master
         to f099302d60bbf463a8275d1be0547663b801a47d (commit)
       from 56d983d9387e3ea00feaaa3060a62383ccdf4ef9 (commit)

commit f099302d60bbf463a8275d1be0547663b801a47d
Author: Nick Schermer <nick at xfce.org>
Date:   Sun Sep 30 18:29:36 2012 +0200

    Show transfer rate in transfer dialog (bug #8250).
    
    Show transfer rate when copying/downloading a file. The
    rate is show after 10 seconds to be more reliable.
    
    The remaining time is also calculated with the transfer
    speed. This makes the time snappier when the transfer
    rate drops.

 thunar/thunar-pango-extensions.c |   20 ++++
 thunar/thunar-pango-extensions.h |    1 +
 thunar/thunar-progress-view.c    |   98 ++++++-----------
 thunar/thunar-transfer-job.c     |  222 +++++++++++++++++++++++++++-----------
 thunar/thunar-transfer-job.h     |    8 +-
 5 files changed, 218 insertions(+), 131 deletions(-)

diff --git a/thunar/thunar-pango-extensions.c b/thunar/thunar-pango-extensions.c
index 6d4f301..391c27c 100644
--- a/thunar/thunar-pango-extensions.c
+++ b/thunar/thunar-pango-extensions.c
@@ -159,6 +159,26 @@ thunar_pango_attr_list_small_italic (void)
 
 
 /**
+ * thunar_pango_attr_list_small:
+ *
+ * Returns a #PangoAttrList for rendering small text.
+ * The returned list is owned by the callee and must
+ * not be freed or modified by the caller.
+ *
+ * Return value: a #PangoAttrList for rendering small text.
+ **/
+PangoAttrList*
+thunar_pango_attr_list_small (void)
+{
+  static PangoAttrList *attr_list = NULL;
+  if (G_UNLIKELY (attr_list == NULL))
+    attr_list = thunar_pango_attr_list_wrap (pango_attr_scale_new (PANGO_SCALE_SMALL), NULL);
+  return attr_list;
+}
+
+
+
+/**
  * thunar_pango_attr_list_underline_single:
  *
  * Returns a #PangoAttrList for underlining text using a single line.
diff --git a/thunar/thunar-pango-extensions.h b/thunar/thunar-pango-extensions.h
index 7c91e21..300d9af 100644
--- a/thunar/thunar-pango-extensions.h
+++ b/thunar/thunar-pango-extensions.h
@@ -29,6 +29,7 @@ PangoAttrList *thunar_pango_attr_list_big_bold          (void) G_GNUC_CONST;
 PangoAttrList *thunar_pango_attr_list_bold              (void) G_GNUC_CONST;
 PangoAttrList *thunar_pango_attr_list_italic            (void) G_GNUC_CONST;
 PangoAttrList *thunar_pango_attr_list_small_italic      (void) G_GNUC_CONST;
+PangoAttrList *thunar_pango_attr_list_small             (void) G_GNUC_CONST;
 PangoAttrList *thunar_pango_attr_list_underline_single  (void) G_GNUC_CONST;
 
 G_END_DECLS;
diff --git a/thunar/thunar-progress-view.c b/thunar/thunar-progress-view.c
index 60ab64c..d3461eb 100644
--- a/thunar/thunar-progress-view.c
+++ b/thunar/thunar-progress-view.c
@@ -30,6 +30,7 @@
 #include <thunar/thunar-pango-extensions.h>
 #include <thunar/thunar-private.h>
 #include <thunar/thunar-util.h>
+#include <thunar/thunar-transfer-job.h>
 #include <thunar/thunar-progress-view.h>
 
 
@@ -91,11 +92,9 @@ struct _ThunarProgressView
 
   ThunarJob *job;
 
-  gint64     start_time;
-  gint64     last_update_time;
-
   GtkWidget *progress_bar;
   GtkWidget *progress_label;
+  GtkWidget *message_label;
 
   gchar     *icon_name;
   gchar     *title;
@@ -121,7 +120,7 @@ thunar_progress_view_class_init (ThunarProgressViewClass *klass)
   /**
    * ThunarProgressView:job:
    *
-   * The #ThunarJob, whose progress is displayed by this view, or 
+   * The #ThunarJob, whose progress is displayed by this view, or
    * %NULL if no job is set.
    **/
   g_object_class_install_property (gobject_class,
@@ -177,11 +176,9 @@ thunar_progress_view_init (ThunarProgressView *view)
   GtkWidget *button;
   GtkWidget *vbox;
   GtkWidget *vbox2;
+  GtkWidget *vbox3;
   GtkWidget *hbox;
 
-  /* remember the current time as start time */
-  view->start_time = g_get_real_time ();
-
   vbox = gtk_vbox_new (FALSE, 6);
   gtk_container_add (GTK_CONTAINER (view), vbox);
   gtk_widget_show (vbox);
@@ -205,27 +202,36 @@ thunar_progress_view_init (ThunarProgressView *view)
   gtk_box_pack_start (GTK_BOX (vbox2), label, TRUE, TRUE, 0);
   gtk_widget_show (label);
 
-  view->progress_label = g_object_new (GTK_TYPE_LABEL, "xalign", 0.0f, NULL);
-  gtk_label_set_ellipsize (GTK_LABEL (view->progress_label), PANGO_ELLIPSIZE_MIDDLE);
-  gtk_box_pack_start (GTK_BOX (vbox2), view->progress_label, TRUE, TRUE, 0);
-  gtk_widget_grab_focus (view->progress_label);
-  gtk_widget_show (view->progress_label);
+  view->message_label = g_object_new (GTK_TYPE_LABEL, "xalign", 0.0f, NULL);
+  gtk_label_set_ellipsize (GTK_LABEL (view->message_label), PANGO_ELLIPSIZE_MIDDLE);
+  gtk_box_pack_start (GTK_BOX (vbox2), view->message_label, TRUE, TRUE, 0);
+  gtk_widget_show (view->message_label);
 
   hbox = gtk_hbox_new (FALSE, 6);
-  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
   gtk_widget_show (hbox);
 
-  view->progress_bar = g_object_new (GTK_TYPE_PROGRESS_BAR, "text", "", NULL);
-  gtk_box_pack_start (GTK_BOX (hbox), view->progress_bar, TRUE, TRUE, 0);
+  vbox3 = gtk_vbox_new (FALSE, 3);
+  gtk_box_pack_start (GTK_BOX (hbox), vbox3, TRUE, TRUE, 0);
+  gtk_widget_show (vbox3);
+
+  view->progress_bar = gtk_progress_bar_new ();
+  gtk_box_pack_start (GTK_BOX (vbox3), view->progress_bar, TRUE, TRUE, 0);
   gtk_widget_show (view->progress_bar);
 
+  view->progress_label = g_object_new (GTK_TYPE_LABEL, "xalign", 0.0f, NULL);
+  gtk_label_set_ellipsize (GTK_LABEL (view->progress_label), PANGO_ELLIPSIZE_END);
+  gtk_label_set_attributes (GTK_LABEL (view->progress_label), thunar_pango_attr_list_small ());
+  gtk_box_pack_start (GTK_BOX (vbox3), view->progress_label, FALSE, TRUE, 0);
+  gtk_widget_show (view->progress_label);
+
   button = gtk_button_new ();
   g_signal_connect_swapped (button, "clicked", G_CALLBACK (thunar_progress_view_cancel_job), view);
   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
   gtk_widget_set_can_focus (button, FALSE);
   gtk_widget_show (button);
 
-  image = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_SMALL_TOOLBAR);
+  image = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
   gtk_container_add (GTK_CONTAINER (button), image);
   gtk_widget_show (image);
 
@@ -337,7 +343,7 @@ thunar_progress_view_cancel_job (ThunarProgressView *view)
       exo_job_cancel (EXO_JOB (view->job));
 
       /* don't listen to percentage updates any more */
-      g_signal_handlers_disconnect_matched (view->job, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, 
+      g_signal_handlers_disconnect_matched (view->job, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
                                             thunar_progress_view_percent, NULL);
 
       /* don't listen to info messages any more */
@@ -345,7 +351,7 @@ thunar_progress_view_cancel_job (ThunarProgressView *view)
                                             thunar_progress_view_info_message, NULL);
 
       /* update the progress bar text */
-      gtk_progress_bar_set_text (GTK_PROGRESS_BAR (view->progress_bar), 
+      gtk_progress_bar_set_text (GTK_PROGRESS_BAR (view->progress_bar),
                                  _("Cancelling..."));
     }
 }
@@ -372,7 +378,7 @@ thunar_progress_view_ask (ThunarProgressView *view,
   window = gtk_widget_get_toplevel (GTK_WIDGET (view));
 
   /* display the question view */
-  return thunar_dialogs_show_job_ask (window != NULL ? GTK_WINDOW (window) : NULL, 
+  return thunar_dialogs_show_job_ask (window != NULL ? GTK_WINDOW (window) : NULL,
                                       message, choices);
 }
 
@@ -399,7 +405,7 @@ thunar_progress_view_ask_replace (ThunarProgressView *view,
   window = gtk_widget_get_toplevel (GTK_WIDGET (view));
 
   /* display the question view */
-  return thunar_dialogs_show_job_ask_replace (window != NULL ? GTK_WINDOW (window) : NULL, 
+  return thunar_dialogs_show_job_ask_replace (window != NULL ? GTK_WINDOW (window) : NULL,
                                               src_file, dst_file);
 }
 
@@ -453,7 +459,7 @@ thunar_progress_view_info_message (ThunarProgressView *view,
   _thunar_return_if_fail (THUNAR_IS_JOB (job));
   _thunar_return_if_fail (view->job == THUNAR_JOB (job));
 
-  gtk_label_set_text (GTK_LABEL (view->progress_label), message);
+  gtk_label_set_text (GTK_LABEL (view->message_label), message);
 }
 
 
@@ -463,58 +469,20 @@ thunar_progress_view_percent (ThunarProgressView *view,
                               gdouble             percent,
                               ExoJob             *job)
 {
-  gint64 current_time;
-  gulong remaining_time;
-  gint64 elapsed_time;
-  gchar  text[512];
+  gchar *text;
 
   _thunar_return_if_fail (THUNAR_IS_PROGRESS_VIEW (view));
   _thunar_return_if_fail (percent >= 0.0 && percent <= 100.0);
   _thunar_return_if_fail (THUNAR_IS_JOB (job));
   _thunar_return_if_fail (view->job == THUNAR_JOB (job));
 
+  /* update progressbar */
   gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (view->progress_bar), percent / 100.0);
 
-  /* check if we should update the time display (every 400ms) */
-  current_time = g_get_real_time ();
-  if (current_time - view->last_update_time > (400 * 1000))
-    {
-      /* calculate the remaining time (in seconds) */
-      elapsed_time = (current_time - view->start_time) / 1000;
-      remaining_time = ((100 * elapsed_time) / percent - elapsed_time) / 1000;
-
-      /* setup the time label */
-      if (G_LIKELY (remaining_time > 0))
-        {
-          /* format the time text */
-          if (remaining_time > 60 * 60)
-            {
-              remaining_time = (gulong) (remaining_time / (60 * 60));
-              g_snprintf (text, sizeof (text), ngettext ("%lu hour remaining", "%lu hours remaining", remaining_time), remaining_time);
-            }
-          else if (remaining_time > 60)
-            {
-              remaining_time = (gulong) (remaining_time / 60);
-              g_snprintf (text, sizeof (text), ngettext ("%lu minute remaining", "%lu minutes remaining", remaining_time), remaining_time);
-            }
-          else
-            {
-              remaining_time = remaining_time;
-              g_snprintf (text, sizeof (text), ngettext ("%lu second remaining", "%lu seconds remaining", remaining_time), remaining_time);
-            }
-
-          /* apply the time text */
-          gtk_progress_bar_set_text (GTK_PROGRESS_BAR (view->progress_bar), text);
-        }
-      else
-        {
-          /* display an empty label */
-          gtk_progress_bar_set_text (GTK_PROGRESS_BAR (view->progress_bar), " ");
-        }
-
-      /* remember the current time as last update time */
-      view->last_update_time = current_time;
-    }
+  /* set progress text */
+  text = thunar_transfer_job_get_status (THUNAR_TRANSFER_JOB (job));
+  gtk_label_set_text (GTK_LABEL (view->progress_label), text);
+  g_free (text);
 }
 
 
diff --git a/thunar/thunar-transfer-job.c b/thunar/thunar-transfer-job.c
index 53eeeda..650d27f 100644
--- a/thunar/thunar-transfer-job.c
+++ b/thunar/thunar-transfer-job.c
@@ -3,18 +3,18 @@
  * Copyright (c) 2005-2007 Benedikt Meurer <benny at xfce.org>
  * Copyright (c) 2009-2011 Jannis Pohlmann <jannis at xfce.org>
  *
- * This program is free software; you can redistribute it and/or 
+ * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of 
+ * published by the Free Software Foundation; either version 2 of
  * the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public 
- * License along with this program; if not, write to the Free 
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */
@@ -36,6 +36,11 @@
 
 
 
+/* seconds before we show the transfer rate + remaining time */
+#define MINIMUM_TRANSFER_TIME (10 * G_USEC_PER_SEC) /* 10 seconds */
+
+
+
 typedef struct _ThunarTransferNode ThunarTransferNode;
 
 
@@ -60,11 +65,14 @@ struct _ThunarTransferJob
   GList                *source_node_list;
   GList                *target_file_list;
 
+  gint64                start_time;
+  gint64                last_update_time;
+  guint64               last_total_progress;
+
   guint64               total_size;
   guint64               total_progress;
   guint64               file_progress;
-
-  gdouble               previous_percentage;
+  guint64               transfer_rate;
 };
 
 struct _ThunarTransferNode
@@ -87,7 +95,7 @@ thunar_transfer_job_class_init (ThunarTransferJobClass *klass)
   ExoJobClass  *exojob_class;
 
   gobject_class = G_OBJECT_CLASS (klass);
-  gobject_class->finalize = thunar_transfer_job_finalize; 
+  gobject_class->finalize = thunar_transfer_job_finalize;
 
   exojob_class = EXO_JOB_CLASS (klass);
   exojob_class->execute = thunar_transfer_job_execute;
@@ -104,7 +112,10 @@ thunar_transfer_job_init (ThunarTransferJob *job)
   job->total_size = 0;
   job->total_progress = 0;
   job->file_progress = 0;
-  job->previous_percentage = 0.0;
+  job->last_update_time = 0;
+  job->last_total_progress = 0;
+  job->transfer_rate = 0;
+  job->start_time = 0;
 }
 
 
@@ -128,12 +139,14 @@ thunar_transfer_job_progress (goffset  current_num_bytes,
                               goffset  total_num_bytes,
                               gpointer user_data)
 {
-  guint64 new_percentage;
-
   ThunarTransferJob *job = user_data;
+  guint64            new_percentage;
+  gint64             current_time;
+  gint64             expired_time;
+  guint64            transfer_rate;
 
   _thunar_return_if_fail (THUNAR_IS_TRANSFER_JOB (job));
-  
+
   if (G_LIKELY (job->total_size > 0))
     {
       /* update total progress */
@@ -145,15 +158,28 @@ thunar_transfer_job_progress (goffset  current_num_bytes,
       /* compute the new percentage after the progress we've made */
       new_percentage = (job->total_progress * 100.0) / job->total_size;
 
-      /* notify callers about the progress only if we have advanced by
-       * at least 0.01 percent since the last signal emission */
-      if (new_percentage >= (job->previous_percentage + 0.01))
+      /* get current time */
+      current_time = g_get_real_time ();
+      expired_time = current_time - job->last_update_time;
+
+      /* notify callers not more then every 500ms */
+      if (expired_time > (500 * 1000))
         {
+          /* calculate the transfer rate in the last expired time */
+          transfer_rate = (job->total_progress - job->last_total_progress) / ((gfloat) expired_time / G_USEC_PER_SEC);
+
+          /* take the average of the last 10 rates (5 sec), so the output is less jumpy */
+          if (job->transfer_rate > 0)
+            job->transfer_rate = ((job->transfer_rate * 10) + transfer_rate) / 11;
+          else
+            job->transfer_rate = transfer_rate;
+
           /* emit the percent signal */
           exo_job_percent (EXO_JOB (job), new_percentage);
 
-          /* remember the percentage */
-          job->previous_percentage = new_percentage;
+          /* update internals */
+          job->last_update_time = current_time;
+          job->last_total_progress = job->total_progress;
         }
     }
 }
@@ -178,7 +204,7 @@ thunar_transfer_job_collect_node (ThunarTransferJob  *job,
   if (exo_job_set_error_if_cancelled (EXO_JOB (job), error))
     return FALSE;
 
-  info = g_file_query_info (node->source_file, 
+  info = g_file_query_info (node->source_file,
                             G_FILE_ATTRIBUTE_STANDARD_SIZE ","
                             G_FILE_ATTRIBUTE_STANDARD_TYPE,
                             G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
@@ -212,7 +238,7 @@ thunar_transfer_job_collect_node (ThunarTransferJob  *job,
           /* collect the child node */
           thunar_transfer_job_collect_node (job, child_node, &err);
         }
-      
+
       /* release the child files */
       thunar_g_file_list_free (file_list);
     }
@@ -286,12 +312,12 @@ ttj_copy_file (ThunarTransferJob *job,
   /* check if there were errors */
   if (G_UNLIKELY (err != NULL && err->domain == G_IO_ERROR))
     {
-      if (err->code == G_IO_ERROR_WOULD_MERGE 
-          || (err->code == G_IO_ERROR_EXISTS 
+      if (err->code == G_IO_ERROR_WOULD_MERGE
+          || (err->code == G_IO_ERROR_EXISTS
               && source_type == G_FILE_TYPE_DIRECTORY
               && target_type == G_FILE_TYPE_DIRECTORY))
         {
-          /* we tried to overwrite a directory with a directory. this normally results 
+          /* we tried to overwrite a directory with a directory. this normally results
            * in a merge. ignore the error if we actually *want* to merge */
           if (merge_directories)
             g_clear_error (&err);
@@ -300,14 +326,14 @@ ttj_copy_file (ThunarTransferJob *job,
         {
           g_clear_error (&err);
 
-          /* we tried to copy a directory and either 
+          /* we tried to copy a directory and either
            *
-           * - the target did not exist which means we simple have to 
+           * - the target did not exist which means we simple have to
            *   create the target directory
            *
            * or
            *
-           * - the target is not a directory and we tried to overwrite it in 
+           * - the target is not a directory and we tried to overwrite it in
            *   which case we have to delete it first and then create the target
            *   directory
            */
@@ -322,8 +348,8 @@ ttj_copy_file (ThunarTransferJob *job,
               if (target_exists)
                 {
                   /* the target still exists and thus is not a directory. try to remove it */
-                  g_file_delete (target_file, 
-                                 exo_job_get_cancellable (EXO_JOB (job)), 
+                  g_file_delete (target_file,
+                                 exo_job_get_cancellable (EXO_JOB (job)),
                                  &err);
                 }
 
@@ -331,8 +357,8 @@ ttj_copy_file (ThunarTransferJob *job,
               if (err == NULL)
                 {
                   /* now try to create the directory */
-                  g_file_make_directory (target_file, 
-                                         exo_job_get_cancellable (EXO_JOB (job)), 
+                  g_file_make_directory (target_file,
+                                         exo_job_get_cancellable (EXO_JOB (job)),
                                          &err);
                 }
             }
@@ -362,16 +388,16 @@ ttj_copy_file (ThunarTransferJob *job,
  * Tries to copy @source_file to @target_file. The real destination is the
  * return value and may differ from @target_file (e.g. if you try to copy
  * the file "/foo/bar" into the same directory you'll end up with something
- * like "/foo/copy of bar" instead of "/foo/bar". 
+ * like "/foo/copy of bar" instead of "/foo/bar".
  *
  * The return value is guaranteed to be %NULL on errors and @error will
  * always be set in those cases. If the file is skipped, the return value
  * will be @source_file.
  *
- * Return value: the destination #GFile to which @source_file was copied 
- *               or linked. The caller is reposible to release it with 
- *               g_object_unref() if no longer needed. It points to 
- *               @source_file if the file was skipped and will be %NULL 
+ * Return value: the destination #GFile to which @source_file was copied
+ *               or linked. The caller is reposible to release it with
+ *               g_object_unref() if no longer needed. It points to
+ *               @source_file if the file was skipped and will be %NULL
  *               on error or cancellation.
  **/
 static GFile *
@@ -411,8 +437,8 @@ thunar_transfer_job_copy_file (ThunarTransferJob *job,
           for (n = 1; err == NULL; ++n)
             {
               GFile *duplicate_file = thunar_io_jobs_util_next_duplicate_file (THUNAR_JOB (job),
-                                                                               source_file, 
-                                                                               TRUE, n, 
+                                                                               source_file,
+                                                                               TRUE, n,
                                                                                &err);
 
               if (err == NULL)
@@ -423,7 +449,7 @@ thunar_transfer_job_copy_file (ThunarTransferJob *job,
                       /* return the real target file */
                       return duplicate_file;
                     }
-                  
+
                   g_object_unref (duplicate_file);
                 }
 
@@ -442,7 +468,7 @@ thunar_transfer_job_copy_file (ThunarTransferJob *job,
           g_clear_error (&err);
 
           /* ask the user whether to replace the target file */
-          response = thunar_job_ask_replace (THUNAR_JOB (job), source_file, 
+          response = thunar_job_ask_replace (THUNAR_JOB (job), source_file,
                                              target_file, &err);
 
           if (err != NULL)
@@ -459,7 +485,7 @@ thunar_transfer_job_copy_file (ThunarTransferJob *job,
               continue;
             }
 
-          /* tell the caller we skipped the file if the user 
+          /* tell the caller we skipped the file if the user
            * doesn't want to retry/overwrite */
           if (response == THUNAR_JOB_RESPONSE_NO)
             return g_object_ref (source_file);
@@ -537,7 +563,7 @@ thunar_transfer_job_copy_node (ThunarTransferJob  *job,
 
 retry_copy:
       /* copy the item specified by this node (not recursively) */
-      real_target_file = thunar_transfer_job_copy_file (job, node->source_file, 
+      real_target_file = thunar_transfer_job_copy_file (job, node->source_file,
                                                         target_file, &err);
       if (G_LIKELY (real_target_file != NULL))
         {
@@ -545,8 +571,8 @@ retry_copy:
           if (G_LIKELY (node->source_file != real_target_file))
             {
               /* notify the thumbnail cache of the copy operation */
-              thunar_thumbnail_cache_copy_file (thumbnail_cache, 
-                                                node->source_file, 
+              thunar_thumbnail_cache_copy_file (thumbnail_cache,
+                                                node->source_file,
                                                 real_target_file);
 
               /* check if we have children to copy */
@@ -572,8 +598,8 @@ retry_copy:
               /* add the real target file to the return list */
               if (G_LIKELY (target_file_list_return != NULL))
                 {
-                  *target_file_list_return = 
-                    thunar_g_file_list_prepend (*target_file_list_return, 
+                  *target_file_list_return =
+                    thunar_g_file_list_prepend (*target_file_list_return,
                                                 real_target_file);
                 }
 
@@ -581,18 +607,18 @@ retry_remove:
               /* try to remove the source directory if we are on copy+remove fallback for move */
               if (job->type == THUNAR_TRANSFER_JOB_MOVE)
                 {
-                  if (g_file_delete (node->source_file, 
-                                     exo_job_get_cancellable (EXO_JOB (job)), 
+                  if (g_file_delete (node->source_file,
+                                     exo_job_get_cancellable (EXO_JOB (job)),
                                      &err))
                     {
                       /* notify the thumbnail cache of the delete operation */
-                      thunar_thumbnail_cache_delete_file (thumbnail_cache, 
+                      thunar_thumbnail_cache_delete_file (thumbnail_cache,
                                                           node->source_file);
                     }
                   else
                     {
                       /* ask the user to retry */
-                      response = thunar_job_ask_skip (THUNAR_JOB (job), "%s", 
+                      response = thunar_job_ask_skip (THUNAR_JOB (job), "%s",
                                                       err->message);
 
                       /* reset the error */
@@ -608,9 +634,9 @@ retry_remove:
           g_object_unref (real_target_file);
         }
       else if (err != NULL)
-        { 
+        {
           /* we can only skip if there is space left on the device */
-          if (err->domain != G_IO_ERROR || err->code != G_IO_ERROR_NO_SPACE) 
+          if (err->domain != G_IO_ERROR || err->code != G_IO_ERROR_NO_SPACE)
             {
               /* ask the user to skip this node and all subnodes */
               response = thunar_job_ask_skip (THUNAR_JOB (job), "%s", err->message);
@@ -696,7 +722,7 @@ thunar_transfer_job_execute (ExoJob  *job,
         break;
 
       /* check if we are moving a file out of the trash */
-      if (transfer_job->type == THUNAR_TRANSFER_JOB_MOVE 
+      if (transfer_job->type == THUNAR_TRANSFER_JOB_MOVE
           && thunar_g_file_is_trashed (node->source_file))
         {
           /* update progress information */
@@ -731,7 +757,7 @@ thunar_transfer_job_execute (ExoJob  *job,
                                                 _("The folder \"%s\" does not exist anymore but is "
                                                   "required to restore the file \"%s\" from the "
                                                   "trash"),
-                                                parent_display_name, 
+                                                parent_display_name,
                                                 g_file_info_get_display_name (info));
 
               /* abort if cancelled */
@@ -743,7 +769,7 @@ thunar_transfer_job_execute (ExoJob  *job,
                 }
 
               /* try to create the parent directory */
-              if (!g_file_make_directory_with_parents (target_parent, 
+              if (!g_file_make_directory_with_parents (target_parent,
                                                        exo_job_get_cancellable (job),
                                                        &err))
                 {
@@ -753,7 +779,7 @@ thunar_transfer_job_execute (ExoJob  *job,
 
                       /* overwrite the internal GIO error with something more user-friendly */
                       g_set_error (&err, G_IO_ERROR, G_IO_ERROR_FAILED,
-                                   _("Failed to restore the folder \"%s\""), 
+                                   _("Failed to restore the folder \"%s\""),
                                    parent_display_name);
                     }
 
@@ -769,22 +795,22 @@ thunar_transfer_job_execute (ExoJob  *job,
           if (target_parent != NULL)
             g_object_unref (target_parent);
         }
-      
+
       if (transfer_job->type == THUNAR_TRANSFER_JOB_MOVE)
         {
           /* update progress information */
-          exo_job_info_message (job, _("Trying to move \"%s\""), 
+          exo_job_info_message (job, _("Trying to move \"%s\""),
                                 g_file_info_get_display_name (info));
 
-          if (g_file_move (node->source_file, tp->data, 
-                           G_FILE_COPY_NOFOLLOW_SYMLINKS 
+          if (g_file_move (node->source_file, tp->data,
+                           G_FILE_COPY_NOFOLLOW_SYMLINKS
                            | G_FILE_COPY_NO_FALLBACK_FOR_MOVE,
                            exo_job_get_cancellable (job),
                            NULL, NULL, &err))
             {
               /* notify the thumbnail cache of the move operation */
-              thunar_thumbnail_cache_move_file (thumbnail_cache, 
-                                                node->source_file, 
+              thunar_thumbnail_cache_move_file (thumbnail_cache,
+                                                node->source_file,
                                                 tp->data);
 
               /* add the target file to the new files list */
@@ -830,12 +856,15 @@ thunar_transfer_job_execute (ExoJob  *job,
   /* continue if there were no errors yet */
   if (G_LIKELY (err == NULL))
     {
+      /* transfer starts now */
+      transfer_job->start_time = g_get_real_time ();
+
       /* perform the copy recursively for all source transfer nodes */
       for (sp = transfer_job->source_node_list, tp = transfer_job->target_file_list;
            sp != NULL && tp != NULL && err == NULL;
            sp = sp->next, tp = tp->next)
         {
-          thunar_transfer_job_copy_node (transfer_job, sp->data, tp->data, NULL, 
+          thunar_transfer_job_copy_node (transfer_job, sp->data, tp->data, NULL,
                                          &new_files_list, &err);
         }
     }
@@ -902,8 +931,8 @@ thunar_transfer_job_new (GList                *source_node_list,
   job->type = type;
 
   /* add a transfer node for each source path and a matching target parent path */
-  for (sp = source_node_list, tp = target_file_list; 
-       sp != NULL; 
+  for (sp = source_node_list, tp = target_file_list;
+       sp != NULL;
        sp = sp->next, tp = tp->next)
     {
       /* make sure we don't transfer root directories. this should be prevented in the GUI */
@@ -928,3 +957,70 @@ thunar_transfer_job_new (GList                *source_node_list,
 
   return THUNAR_JOB (job);
 }
+
+
+
+gchar *
+thunar_transfer_job_get_status (ThunarTransferJob *job)
+{
+  gchar   *total_size_str;
+  gchar   *total_progress_str;
+  gchar   *transfer_rate_str;
+  GString *status;
+  gulong   remaining_time;
+
+  _thunar_return_val_if_fail (THUNAR_IS_TRANSFER_JOB (job), NULL);
+
+  status = g_string_sized_new (100);
+
+  /* transfer status like "22.6MB of 134.1MB" */
+  total_size_str = g_format_size (job->total_size);
+  total_progress_str = g_format_size (job->total_progress);
+  g_string_append_printf (status, _("%s of %s"), total_progress_str, total_size_str);
+  g_free (total_size_str);
+  g_free (total_progress_str);
+
+  /* show time and transfer rate after 10 seconds */
+  if (job->transfer_rate > 0
+      && (job->last_update_time - job->start_time) > MINIMUM_TRANSFER_TIME)
+    {
+      /* remaining time based on the transfer speed */
+      transfer_rate_str = g_format_size (job->transfer_rate);
+      remaining_time = (job->total_size - job->total_progress) / job->transfer_rate;
+
+      if (remaining_time > 0)
+        {
+          /* insert long dash */
+          g_string_append (status, " \xE2\x80\x94 ");
+
+          if (remaining_time > 60 * 60)
+            {
+              remaining_time = (gulong) (remaining_time / (60 * 60));
+              g_string_append_printf (status, ngettext ("%lu hour remaining (%s/sec)",
+                                                        "%lu hours remaining (%s/sec)",
+                                                        remaining_time),
+                                                        remaining_time, transfer_rate_str);
+            }
+          else if (remaining_time > 60)
+            {
+              remaining_time = (gulong) (remaining_time / 60);
+              g_string_append_printf (status, ngettext ("%lu minute remaining (%s/sec)",
+                                                        "%lu minutes remaining (%s/sec)",
+                                                        remaining_time),
+                                                        remaining_time, transfer_rate_str);
+            }
+          else
+            {
+              g_string_append_printf (status, ngettext ("%lu second remaining (%s/sec)",
+                                                        "%lu seconds remaining (%s/sec)",
+                                                        remaining_time),
+                                                        remaining_time, transfer_rate_str);
+            }
+        }
+
+      g_free (transfer_rate_str);
+    }
+
+  return g_string_free (status, FALSE);
+}
+
diff --git a/thunar/thunar-transfer-job.h b/thunar/thunar-transfer-job.h
index 49f94a5..b597167 100644
--- a/thunar/thunar-transfer-job.h
+++ b/thunar/thunar-transfer-job.h
@@ -51,9 +51,11 @@ typedef struct _ThunarTransferJob        ThunarTransferJob;
 
 GType      thunar_transfer_job_get_type (void) G_GNUC_CONST;
 
-ThunarJob *thunar_transfer_job_new      (GList                *source_file_list,
-                                         GList                *target_file_list,
-                                         ThunarTransferJobType type) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+ThunarJob *thunar_transfer_job_new        (GList                *source_file_list,
+                                           GList                *target_file_list,
+                                           ThunarTransferJobType type) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+
+gchar     *thunar_transfer_job_get_status (ThunarTransferJob    *job);
 
 G_END_DECLS
 


More information about the Xfce4-commits mailing list