From 75687f91eb030125c1e3b52cf9d050921c7e60c1 Mon Sep 17 00:00:00 2001 From: Cyrille Pontvieux Date: Wed, 8 Apr 2020 22:21:34 +0200 Subject: [PATCH] Freeze new copy where source or target device is shared with another running one preferences for freeze on same source/target device or not --- thunar/thunar-application.c | 17 ++- thunar/thunar-job.c | 100 +++++++++++++ thunar/thunar-job.h | 6 + thunar/thunar-preferences-dialog.c | 30 ++++ thunar/thunar-preferences.c | 28 ++++ thunar/thunar-progress-dialog.c | 40 ++++++ thunar/thunar-progress-dialog.h | 17 +-- thunar/thunar-progress-view.c | 78 ++++++++++- thunar/thunar-progress-view.h | 1 + thunar/thunar-transfer-job.c | 218 +++++++++++++++++++++++++++++ 10 files changed, 516 insertions(+), 19 deletions(-) diff --git a/thunar/thunar-application.c b/thunar/thunar-application.c index 8730107d..f63c4059 100644 --- a/thunar/thunar-application.c +++ b/thunar/thunar-application.c @@ -823,6 +823,7 @@ thunar_application_launch (ThunarApplication *application, GdkScreen *screen; ThunarJob *job; GList *parent_folder_list = NULL; + gboolean has_jobs; _thunar_return_if_fail (parent == NULL || GDK_IS_SCREEN (parent) || GTK_IS_WIDGET (parent)); @@ -853,21 +854,19 @@ thunar_application_launch (ThunarApplication *application, if (screen != NULL) gtk_window_set_screen (GTK_WINDOW (dialog), screen); - if (thunar_progress_dialog_has_jobs (THUNAR_PROGRESS_DIALOG (dialog))) - { - /* add the job to the dialog */ - thunar_progress_dialog_add_job (THUNAR_PROGRESS_DIALOG (dialog), - job, icon_name, title); + has_jobs = thunar_progress_dialog_has_jobs (THUNAR_PROGRESS_DIALOG (dialog)); + + /* add the job to the dialog */ + thunar_progress_dialog_add_job (THUNAR_PROGRESS_DIALOG (dialog), + job, icon_name, title); + if (has_jobs) + { /* show the dialog immediately */ thunar_application_show_dialogs (application); } else { - /* add the job to the dialog */ - thunar_progress_dialog_add_job (THUNAR_PROGRESS_DIALOG (dialog), - job, icon_name, title); - /* Set up a timer to show the dialog, to make sure we don't * just popup and destroy a dialog for a very short job. */ diff --git a/thunar/thunar-job.c b/thunar/thunar-job.c index 5eeffe27..dad0dcdf 100644 --- a/thunar/thunar-job.c +++ b/thunar/thunar-job.c @@ -43,8 +43,11 @@ enum { ASK, ASK_REPLACE, + ASK_JOBS, FILES_READY, NEW_FILES, + FROZEN, + UNFROZEN, LAST_SIGNAL, }; @@ -70,6 +73,7 @@ struct _ThunarJobPrivate guint n_total_files; gboolean pausable; gboolean paused; + gboolean frozen; }; @@ -199,6 +203,50 @@ thunar_job_class_init (ThunarJobClass *klass) G_SIGNAL_NO_HOOKS, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); + + /** + * ThunarJob::ask-jobs: + * @job : a #ThunarJob. + * + * Emitted to ask the running job list. + * + * Return value: GList* of running jobs. + **/ + job_signals[ASK_JOBS] = + g_signal_new (I_("ask-jobs"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_NO_HOOKS, 0, + NULL, NULL, + g_cclosure_marshal_generic, + G_TYPE_POINTER, 0); + + /** + * ThunarJob::frozen: + * @job : a #ThunarJob. + * + * This signal is emitted by the @job right after the @job is being frozen. + **/ + job_signals[FROZEN] = + g_signal_new (I_("frozen"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_NO_HOOKS, 0, + NULL, NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, 0); + + /** + * ThunarJob::unfrozen: + * @job : a #ThunarJob. + * + * This signal is emitted by the @job right after the @job is being unfrozen. + **/ + job_signals[UNFROZEN] = + g_signal_new (I_("unfrozen"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_NO_HOOKS, 0, + NULL, NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, 0); } @@ -214,6 +262,7 @@ thunar_job_init (ThunarJob *job) job->priv->n_total_files = 0; job->priv->pausable = FALSE; job->priv->paused = FALSE; + job->priv->frozen = FALSE; } @@ -647,6 +696,17 @@ thunar_job_new_files (ThunarJob *job, +GList * +thunar_job_ask_jobs (ThunarJob *job) +{ + GList* jobs = NULL; + + exo_job_emit (EXO_JOB (job), job_signals[ASK_JOBS], 0, &jobs); + return jobs; +} + + + void thunar_job_set_total_files (ThunarJob *job, GList *total_files) @@ -694,6 +754,38 @@ thunar_job_resume (ThunarJob *job) +void +thunar_job_freeze (ThunarJob *job) +{ + job->priv->frozen = TRUE; +} + + + +void +thunar_job_emit_frozen_signal (ThunarJob *job) +{ + exo_job_emit (EXO_JOB (job), job_signals[FROZEN], 0); +} + + + +void +thunar_job_unfreeze (ThunarJob *job) +{ + job->priv->frozen = FALSE; +} + + + +void +thunar_job_emit_unfrozen_signal (ThunarJob *job) +{ + exo_job_emit (EXO_JOB (job), job_signals[UNFROZEN], 0); +} + + + gboolean thunar_job_is_paused (ThunarJob *job) { @@ -702,6 +794,14 @@ thunar_job_is_paused (ThunarJob *job) +gboolean +thunar_job_is_frozen (ThunarJob *job) +{ + return job->priv->frozen; +} + + + void thunar_job_processing_file (ThunarJob *job, GList *current_file, diff --git a/thunar/thunar-job.h b/thunar/thunar-job.h index b9e7f26d..43063661 100644 --- a/thunar/thunar-job.h +++ b/thunar/thunar-job.h @@ -73,7 +73,12 @@ void thunar_job_set_pausable (ThunarJob *job, gboolean thunar_job_is_pausable (ThunarJob *job); void thunar_job_pause (ThunarJob *job); void thunar_job_resume (ThunarJob *job); +void thunar_job_freeze (ThunarJob *job); +void thunar_job_emit_frozen_signal (ThunarJob *job); +void thunar_job_unfreeze (ThunarJob *job); +void thunar_job_emit_unfrozen_signal (ThunarJob *job); gboolean thunar_job_is_paused (ThunarJob *job); +gboolean thunar_job_is_frozen (ThunarJob *job); void thunar_job_processing_file (ThunarJob *job, GList *current_file, guint n_processed); @@ -101,6 +106,7 @@ gboolean thunar_job_files_ready (ThunarJob *job, GList *file_list); void thunar_job_new_files (ThunarJob *job, const GList *file_list); +GList * thunar_job_ask_jobs (ThunarJob *job); G_END_DECLS diff --git a/thunar/thunar-preferences-dialog.c b/thunar/thunar-preferences-dialog.c index 1d2e48f5..f5db028a 100644 --- a/thunar/thunar-preferences-dialog.c +++ b/thunar/thunar-preferences-dialog.c @@ -701,6 +701,36 @@ thunar_preferences_dialog_init (ThunarPreferencesDialog *dialog) gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 1, 1); gtk_widget_show (button); + frame = g_object_new (GTK_TYPE_FRAME, "border-width", 0, "shadow-type", GTK_SHADOW_NONE, NULL); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0); + gtk_widget_show (frame); + + label = gtk_label_new (_("File transfer")); + gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ()); + gtk_frame_set_label_widget (GTK_FRAME (frame), label); + gtk_widget_show (label); + + grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (grid), 12); + gtk_grid_set_row_spacing (GTK_GRID (grid), 2); + gtk_container_set_border_width (GTK_CONTAINER (grid), 12); + gtk_container_add (GTK_CONTAINER (frame), grid); + gtk_widget_show (grid); + + button = gtk_check_button_new_with_mnemonic (_("One file transfer at a time per source device")); + exo_mutual_binding_new (G_OBJECT (dialog->preferences), "misc-freeze-transfer-on-same-source-device", G_OBJECT (button), "active"); + gtk_widget_set_tooltip_text (button, _("Select this option to have only one copy active at a time per source device")); + gtk_widget_set_hexpand (button, TRUE); + gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 1); + gtk_widget_show (button); + + button = gtk_check_button_new_with_mnemonic (_("One file transfer at a time per target device")); + exo_mutual_binding_new (G_OBJECT (dialog->preferences), "misc-freeze-transfer-on-same-target-device", G_OBJECT (button), "active"); + gtk_widget_set_tooltip_text (button, _("Select this option to have only one copy active at a time per target device")); + gtk_widget_set_hexpand (button, TRUE); + gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 1, 1); + gtk_widget_show (button); + if (thunar_g_vfs_is_uri_scheme_supported ("trash")) { frame = g_object_new (GTK_TYPE_FRAME, "border-width", 0, "shadow-type", GTK_SHADOW_NONE, NULL); diff --git a/thunar/thunar-preferences.c b/thunar/thunar-preferences.c index 732d9a2d..bb1ac539 100644 --- a/thunar/thunar-preferences.c +++ b/thunar/thunar-preferences.c @@ -97,6 +97,8 @@ enum PROP_MISC_THUMBNAIL_DRAW_FRAMES, PROP_MISC_FILE_SIZE_BINARY, PROP_MISC_CONFIRM_CLOSE_MULTIPLE_TABS, + PROP_MISC_FREEZE_TRANSFER_ON_SAME_SOURCE_DEVICE, + PROP_MISC_FREEZE_TRANSFER_ON_SAME_TARGET_DEVICE, PROP_MISC_WINDOW_ICON, PROP_SHORTCUTS_ICON_EMBLEMS, PROP_SHORTCUTS_ICON_SIZE, @@ -764,6 +766,32 @@ thunar_preferences_class_init (ThunarPreferencesClass *klass) TRUE, EXO_PARAM_READWRITE); + /** + * ThunarPreferences:misc-freeze-transfer-on-same-source-device: + * + * Freeze any copy/move jobs when another job is transfering from + * the same source device. + **/ + preferences_props[PROP_MISC_FREEZE_TRANSFER_ON_SAME_SOURCE_DEVICE] = + g_param_spec_boolean ("misc-freeze-transfer-on-same-source-device", + "MiscFreezeTransferOnSameSourceDevice", + NULL, + TRUE, + EXO_PARAM_READWRITE); + + /** + * ThunarPreferences:misc-freeze-transfer-on-same-target-device: + * + * Freeze any copy/move jobs when another job is transfering to + * the same target device. + **/ + preferences_props[PROP_MISC_FREEZE_TRANSFER_ON_SAME_TARGET_DEVICE] = + g_param_spec_boolean ("misc-freeze-transfer-on-same-target-device", + "MiscFreezeTransferOnSameTargetDevice", + NULL, + TRUE, + EXO_PARAM_READWRITE); + /** * ThunarPreferences:misc-change-window-icon: * diff --git a/thunar/thunar-progress-dialog.c b/thunar/thunar-progress-dialog.c index f4de6097..42ad7dcc 100644 --- a/thunar/thunar-progress-dialog.c +++ b/thunar/thunar-progress-dialog.c @@ -237,6 +237,21 @@ thunar_progress_dialog_view_needs_attention (ThunarProgressDialog *dialog, +static GList * +thunar_progress_dialog_ask_jobs (ThunarProgressDialog *dialog, + ThunarJob *job) +{ + GList *jobs = NULL; + + _thunar_return_val_if_fail (THUNAR_IS_PROGRESS_DIALOG (dialog), NULL); + _thunar_return_val_if_fail (THUNAR_IS_JOB (job), NULL); + jobs = thunar_progress_dialog_list_jobs (dialog); + + return jobs; +} + + + static void thunar_progress_dialog_job_finished (ThunarProgressDialog *dialog, ThunarProgressView *view) @@ -334,6 +349,28 @@ thunar_progress_dialog_new (void) +GList * +thunar_progress_dialog_list_jobs (ThunarProgressDialog *dialog) +{ + GList *jobs = NULL; + GList *l; + ThunarProgressView *view; + ThunarJob *job; + + for (l = dialog->views; l != NULL; l = l->next) + { + view = THUNAR_PROGRESS_VIEW (l->data); + job = thunar_progress_view_get_job (view); + if (job != NULL && !exo_job_is_cancelled (EXO_JOB (job))) + { + jobs = g_list_append (jobs, job); + } + } + return jobs; +} + + + void thunar_progress_dialog_add_job (ThunarProgressDialog *dialog, ThunarJob *job, @@ -393,6 +430,9 @@ thunar_progress_dialog_add_job (ThunarProgressDialog *dialog, g_signal_connect_swapped (view, "finished", G_CALLBACK (thunar_progress_dialog_job_finished), dialog); + g_signal_connect_swapped (job, "ask-jobs", + G_CALLBACK (thunar_progress_dialog_ask_jobs), dialog); + if (dialog->status_icon != NULL) thunar_progress_dialog_update_status_icon (dialog); } diff --git a/thunar/thunar-progress-dialog.h b/thunar/thunar-progress-dialog.h index 04e148de..29033cac 100644 --- a/thunar/thunar-progress-dialog.h +++ b/thunar/thunar-progress-dialog.h @@ -36,14 +36,15 @@ typedef struct _ThunarProgressDialog ThunarProgressDialog; #define THUNAR_IS_PROGRESS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_PROGRESS_DIALOG)) #define THUNAR_PROGRESS_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_PROGRESS_DIALOG, ThunarProgressDialogClass)) -GType thunar_progress_dialog_get_type (void) G_GNUC_CONST; - -GtkWidget *thunar_progress_dialog_new (void); -void thunar_progress_dialog_add_job (ThunarProgressDialog *dialog, - ThunarJob *job, - const gchar *icon_name, - const gchar *title); -gboolean thunar_progress_dialog_has_jobs (ThunarProgressDialog *dialog); +GType thunar_progress_dialog_get_type (void) G_GNUC_CONST; + +GtkWidget *thunar_progress_dialog_new (void); +GList *thunar_progress_dialog_list_jobs (ThunarProgressDialog *dialog); +void thunar_progress_dialog_add_job (ThunarProgressDialog *dialog, + ThunarJob *job, + const gchar *icon_name, + const gchar *title); +gboolean thunar_progress_dialog_has_jobs (ThunarProgressDialog *dialog); G_END_DECLS; diff --git a/thunar/thunar-progress-view.c b/thunar/thunar-progress-view.c index ace91fcb..b19265b7 100644 --- a/thunar/thunar-progress-view.c +++ b/thunar/thunar-progress-view.c @@ -57,6 +57,7 @@ static void thunar_progress_view_set_property (GObject * GParamSpec *pspec); static void thunar_progress_view_pause_job (ThunarProgressView *view); static void thunar_progress_view_unpause_job (ThunarProgressView *view); +static void thunar_progress_view_unfreeze_job (ThunarProgressView *view); static void thunar_progress_view_cancel_job (ThunarProgressView *view); static ThunarJobResponse thunar_progress_view_ask (ThunarProgressView *view, const gchar *message, @@ -77,7 +78,10 @@ static void thunar_progress_view_info_message (ThunarProgressView * static void thunar_progress_view_percent (ThunarProgressView *view, gdouble percent, ExoJob *job); -static ThunarJob *thunar_progress_view_get_job (ThunarProgressView *view); +static void thunar_progress_view_frozen (ThunarProgressView *view, + ExoJob *job); +static void thunar_progress_view_unfrozen (ThunarProgressView *view, + ExoJob *job); static void thunar_progress_view_set_job (ThunarProgressView *view, ThunarJob *job); @@ -99,6 +103,7 @@ struct _ThunarProgressView GtkWidget *message_label; GtkWidget *pause_button; GtkWidget *unpause_button; + GtkWidget *unfreeze_button; gchar *icon_name; gchar *title; @@ -246,6 +251,13 @@ thunar_progress_view_init (ThunarProgressView *view) gtk_widget_set_can_focus (view->unpause_button, FALSE); gtk_widget_hide (view->unpause_button); + view->unfreeze_button = gtk_button_new_from_icon_name ("media-playback-start", GTK_ICON_SIZE_BUTTON); + gtk_button_set_label (GTK_BUTTON (view->unfreeze_button), _("Force")); + g_signal_connect_swapped (view->unfreeze_button, "clicked", G_CALLBACK (thunar_progress_view_unfreeze_job), view); + gtk_box_pack_start (GTK_BOX (hbox), view->unfreeze_button, FALSE, FALSE, 0); + gtk_widget_set_can_focus (view->unfreeze_button, FALSE); + gtk_widget_hide (view->unfreeze_button); + cancel_button = gtk_button_new_from_icon_name ("process-stop", GTK_ICON_SIZE_BUTTON); gtk_button_set_label (GTK_BUTTON (cancel_button), _("Cancel")); g_signal_connect_swapped (cancel_button, "clicked", G_CALLBACK (thunar_progress_view_cancel_job), view); @@ -389,6 +401,22 @@ thunar_progress_view_unpause_job (ThunarProgressView *view) + static void +thunar_progress_view_unfreeze_job (ThunarProgressView *view) +{ + _thunar_return_if_fail (THUNAR_IS_PROGRESS_VIEW (view)); + _thunar_return_if_fail (THUNAR_IS_JOB (view->job)); + + if (view->job != NULL) + { + /* unfreeze the job */ + thunar_job_unfreeze(view->job); + /* the UI will be updated on a callback unfrozen event */ + } +} + + + static void thunar_progress_view_cancel_job (ThunarProgressView *view) { @@ -400,6 +428,12 @@ thunar_progress_view_cancel_job (ThunarProgressView *view) /* cancel the job */ exo_job_cancel (EXO_JOB (view->job)); + /* don't listen to frozen/unfrozen states updates any more */ + g_signal_handlers_disconnect_matched (view->job, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + thunar_progress_view_frozen, NULL); + g_signal_handlers_disconnect_matched (view->job, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + thunar_progress_view_unfrozen, NULL); + /* don't listen to percentage updates any more */ g_signal_handlers_disconnect_matched (view->job, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, thunar_progress_view_percent, NULL); @@ -548,6 +582,44 @@ thunar_progress_view_percent (ThunarProgressView *view, +static void +thunar_progress_view_frozen (ThunarProgressView *view, + ExoJob *job) +{ + _thunar_return_if_fail (THUNAR_IS_PROGRESS_VIEW (view)); + _thunar_return_if_fail (THUNAR_IS_JOB (job)); + _thunar_return_if_fail (view->job == THUNAR_JOB (job)); + + if (THUNAR_IS_TRANSFER_JOB (job)) + { + /* update the UI */ + gtk_widget_hide (view->pause_button); + gtk_widget_show (view->unfreeze_button); + gtk_label_set_text (GTK_LABEL (view->progress_label), _("Frozen by another job on same device")); + } +} + + + +static void +thunar_progress_view_unfrozen (ThunarProgressView *view, + ExoJob *job) +{ + _thunar_return_if_fail (THUNAR_IS_PROGRESS_VIEW (view)); + _thunar_return_if_fail (THUNAR_IS_JOB (job)); + _thunar_return_if_fail (view->job == THUNAR_JOB (job)); + + if (THUNAR_IS_TRANSFER_JOB (job)) + { + /* update the UI */ + gtk_widget_hide (view->unfreeze_button); + gtk_widget_show (view->pause_button); + gtk_label_set_text (GTK_LABEL (view->progress_label), _("Unfreezing...")); + } +} + + + /** * thunar_progress_view_new_with_job: * @job : a #ThunarJob or %NULL. @@ -574,7 +646,7 @@ thunar_progress_view_new_with_job (ThunarJob *job) * * Return value: the job associated with @view or %NULL. **/ -static ThunarJob * +ThunarJob * thunar_progress_view_get_job (ThunarProgressView *view) { _thunar_return_val_if_fail (THUNAR_IS_PROGRESS_VIEW (view), NULL); @@ -622,6 +694,8 @@ thunar_progress_view_set_job (ThunarProgressView *view, g_signal_connect_swapped (job, "finished", G_CALLBACK (thunar_progress_view_finished), view); g_signal_connect_swapped (job, "info-message", G_CALLBACK (thunar_progress_view_info_message), view); g_signal_connect_swapped (job, "percent", G_CALLBACK (thunar_progress_view_percent), view); + g_signal_connect_swapped (job, "frozen", G_CALLBACK (thunar_progress_view_frozen), view); + g_signal_connect_swapped (job, "unfrozen", G_CALLBACK (thunar_progress_view_unfrozen), view); if (thunar_job_is_pausable (job)) { gtk_widget_show (view->pause_button); diff --git a/thunar/thunar-progress-view.h b/thunar/thunar-progress-view.h index 96e58f30..cd81e857 100644 --- a/thunar/thunar-progress-view.h +++ b/thunar/thunar-progress-view.h @@ -45,6 +45,7 @@ void thunar_progress_view_set_icon_name (ThunarProgressView *view, const gchar *icon_name); void thunar_progress_view_set_title (ThunarProgressView *view, const gchar *title); +ThunarJob *thunar_progress_view_get_job (ThunarProgressView *view); G_END_DECLS; diff --git a/thunar/thunar-transfer-job.c b/thunar/thunar-transfer-job.c index 873cdc7c..623a3261 100644 --- a/thunar/thunar-transfer-job.c +++ b/thunar/thunar-transfer-job.c @@ -47,6 +47,8 @@ enum { PROP_0, PROP_FILE_SIZE_BINARY, + PROP_FREEZE_TRANSFER_ON_SAME_SOURCE_DEVICE, + PROP_FREEZE_TRANSFER_ON_SAME_TARGET_DEVICE, }; @@ -83,7 +85,9 @@ struct _ThunarTransferJob ThunarTransferJobType type; GList *source_node_list; + guint32 source_device_id; GList *target_file_list; + guint32 target_device_id; gint64 start_time; gint64 last_update_time; @@ -96,6 +100,8 @@ struct _ThunarTransferJob ThunarPreferences *preferences; gboolean file_size_binary; + gboolean freeze_on_same_source_device; + gboolean freeze_on_same_target_device; }; struct _ThunarTransferNode @@ -138,6 +144,34 @@ thunar_transfer_job_class_init (ThunarTransferJobClass *klass) NULL, TRUE, EXO_PARAM_READWRITE)); + + /** + * ThunarPropertiesDialog:freeze_on_same_source_device: + * + * Whether to freeze this job if another job is transfering + * from the same source device. + **/ + g_object_class_install_property (gobject_class, + PROP_FREEZE_TRANSFER_ON_SAME_SOURCE_DEVICE, + g_param_spec_boolean ("freeze-transfer-on-same-source-device", + "FreezeTransferOnSameSourceDevice", + NULL, + TRUE, + EXO_PARAM_READWRITE)); + + /** + * ThunarPropertiesDialog:freeze_on_same_target_device: + * + * Whether to freeze this job if another job is transfering + * to the same target device. + **/ + g_object_class_install_property (gobject_class, + PROP_FREEZE_TRANSFER_ON_SAME_TARGET_DEVICE, + g_param_spec_boolean ("freeze-transfer-on-same-target-device", + "FreezeTransferOnSameTargetDevice", + NULL, + TRUE, + EXO_PARAM_READWRITE)); } @@ -148,10 +182,16 @@ thunar_transfer_job_init (ThunarTransferJob *job) job->preferences = thunar_preferences_get (); exo_binding_new (G_OBJECT (job->preferences), "misc-file-size-binary", G_OBJECT (job), "file-size-binary"); + exo_binding_new (G_OBJECT (job->preferences), "misc-freeze-transfer-on-same-source-device", + G_OBJECT (job), "freeze-transfer-on-same-source-device"); + exo_binding_new (G_OBJECT (job->preferences), "misc-freeze-transfer-on-same-target-device", + G_OBJECT (job), "freeze-transfer-on-same-target-device"); job->type = 0; job->source_node_list = NULL; + job->source_device_id = 0; job->target_file_list = NULL; + job->target_device_id = 0; job->total_size = 0; job->total_progress = 0; job->file_progress = 0; @@ -192,6 +232,12 @@ thunar_transfer_job_get_property (GObject *object, case PROP_FILE_SIZE_BINARY: g_value_set_boolean (value, job->file_size_binary); break; + case PROP_FREEZE_TRANSFER_ON_SAME_SOURCE_DEVICE: + g_value_set_boolean (value, job->freeze_on_same_source_device); + break; + case PROP_FREEZE_TRANSFER_ON_SAME_TARGET_DEVICE: + g_value_set_boolean (value, job->freeze_on_same_target_device); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -214,6 +260,12 @@ thunar_transfer_job_set_property (GObject *object, case PROP_FILE_SIZE_BINARY: job->file_size_binary = g_value_get_boolean (value); break; + case PROP_FREEZE_TRANSFER_ON_SAME_SOURCE_DEVICE: + job->freeze_on_same_source_device = g_value_get_boolean (value); + break; + case PROP_FREEZE_TRANSFER_ON_SAME_TARGET_DEVICE: + job->freeze_on_same_target_device = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -880,6 +932,170 @@ thunar_transfer_job_verify_destination (ThunarTransferJob *transfer_job, +static GList * +_thunar_transfer_job_filter_running_jobs (GList *jobs, ThunarJob *own_job) +{ + ThunarJob *job; + GList *run_jobs = NULL; + + for (; jobs != NULL; jobs = jobs->next) + { + job = jobs->data; + if (job == own_job) + continue; + if (!exo_job_is_cancelled (EXO_JOB (job)) && !thunar_job_is_paused (job) && !thunar_job_is_frozen (job)) + { + run_jobs = g_list_append (run_jobs, job); + } + } + + return run_jobs; +} + + + +static gboolean +_thunar_transfer_job_device_id_in_job_source_list (guint32 device_id, GList *jobs) +{ + ThunarTransferJob *job; + + for (; jobs != NULL; jobs = jobs->next) + { + if (THUNAR_IS_TRANSFER_JOB (jobs->data)) + { + job = THUNAR_TRANSFER_JOB (jobs->data); + if (job->source_device_id != 0 && device_id == job->source_device_id) + return TRUE; + } + } + return FALSE; +} + + + +static gboolean +_thunar_transfer_job_device_id_in_job_target_list (guint32 device_id, GList *jobs) +{ + ThunarTransferJob *job; + + for (; device_id != 0 && jobs != NULL; jobs = jobs->next) + { + if (THUNAR_IS_TRANSFER_JOB (jobs->data)) + { + job = THUNAR_TRANSFER_JOB (jobs->data); + if (job->target_device_id != 0 && device_id == job->target_device_id) + return TRUE; + } + } + return FALSE; +} + + + +/** + * thunar_transfer_job_verify_devices: + * @job : a #ThunarTransferJob. + * + * Based on thunar setting, will block until all running jobs + * doing IO on the source files or target files devices are completed. + * The blocking could be forced by the user in the UI. + * + **/ +static void +thunar_transfer_job_verify_devices (ThunarTransferJob *transfer_job) +{ + ThunarTransferNode *node; + GFile *file; + GFileInfo *file_info; + guint32 src_device_id = 0; + guint32 tgt_device_id = 0; + GList *jobs; + GList *other_jobs; + gboolean been_frozen; + + _thunar_return_if_fail (THUNAR_IS_TRANSFER_JOB (transfer_job)); + + /* no source node list nor target file list */ + if (transfer_job->source_node_list == NULL || transfer_job->target_file_list == NULL) + return; + + /* first source file */ + node = transfer_job->source_node_list->data; + file = node->source_file; + /* query device id */ + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_UNIX_DEVICE, + G_FILE_QUERY_INFO_NONE, + exo_job_get_cancellable (EXO_JOB (transfer_job)), + NULL); + if (file_info != NULL) + { + src_device_id = g_file_info_get_attribute_uint32 (file_info, G_FILE_ATTRIBUTE_UNIX_DEVICE); + transfer_job->source_device_id = src_device_id; + g_object_unref (file_info); + } + + /* first target file */ + file = G_FILE (transfer_job->target_file_list->data); + /* query device id */ + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_UNIX_DEVICE, + G_FILE_QUERY_INFO_NONE, + exo_job_get_cancellable (EXO_JOB (transfer_job)), + NULL); + if (file_info != NULL) + { + tgt_device_id = g_file_info_get_attribute_uint32 (file_info, G_FILE_ATTRIBUTE_UNIX_DEVICE); + transfer_job->target_device_id = tgt_device_id; + g_object_unref (file_info); + } + + if (transfer_job->freeze_on_same_source_device || transfer_job->freeze_on_same_target_device) + { + jobs = thunar_job_ask_jobs (THUNAR_JOB (transfer_job)); + other_jobs = _thunar_transfer_job_filter_running_jobs (jobs, THUNAR_JOB (transfer_job)); + been_frozen = FALSE; + while ( + !exo_job_is_cancelled (EXO_JOB (transfer_job)) && + ( + (transfer_job->freeze_on_same_source_device && _thunar_transfer_job_device_id_in_job_source_list (src_device_id, other_jobs)) || + (transfer_job->freeze_on_same_target_device && _thunar_transfer_job_device_id_in_job_target_list (tgt_device_id, other_jobs)) + ) + ) + { + if (!thunar_job_is_frozen (THUNAR_JOB (transfer_job))) + { + if (been_frozen) + { + /* cannot re-freeze. It means that the user force to unfreeze */ + break; + } + else + { + /* first time here. The job needs to change to frozen state */ + been_frozen = TRUE; + thunar_job_freeze (THUNAR_JOB (transfer_job)); + thunar_job_emit_frozen_signal (THUNAR_JOB (transfer_job)); + } + } + g_usleep(500 * 1000); + g_list_free (g_steal_pointer (&jobs)); + g_list_free (g_steal_pointer (&other_jobs)); + jobs = thunar_job_ask_jobs (THUNAR_JOB (transfer_job)); + other_jobs = _thunar_transfer_job_filter_running_jobs (jobs, THUNAR_JOB (transfer_job)); + } + g_list_free (g_steal_pointer (&jobs)); + g_list_free (g_steal_pointer (&other_jobs)); + if (thunar_job_is_frozen (THUNAR_JOB (transfer_job))) + { + thunar_job_unfreeze (THUNAR_JOB (transfer_job)); + thunar_job_emit_unfrozen_signal (THUNAR_JOB (transfer_job)); + } + } +} + + + static gboolean thunar_transfer_job_execute (ExoJob *job, GError **error) @@ -1135,6 +1351,8 @@ thunar_transfer_job_execute (ExoJob *job, } } + thunar_transfer_job_verify_devices (transfer_job); + /* transfer starts now */ transfer_job->start_time = g_get_real_time (); -- 2.26.0