Skip to content

Commit

Permalink
FileOperations: Continue to move some report functions to Vala (#2503)
Browse files Browse the repository at this point in the history
Co-authored-by: Jeremy Wootten <jeremywootten@gmail.com>
  • Loading branch information
tintou and jeremypw authored Oct 10, 2024
1 parent 2f0273b commit a1dd90f
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 373 deletions.
21 changes: 12 additions & 9 deletions libcore/FileOperations/CommonJob.vala
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,28 @@
*/

public class Files.FileOperations.CommonJob {
protected const int NSEC_PER_MSEC = 1000000;
protected const int SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE = 15;

[Compact]
[CCode (cname = "SourceInfo")]
protected class SourceInfo {
[CCode (has_target = false, cname = "CountProgressCallback")]
protected delegate void CountProgressCallback (Files.FileOperations.CommonJob job, Files.FileOperations.CommonJob.SourceInfo info);
internal delegate void CountProgressCallback (Files.FileOperations.CommonJob job, Files.FileOperations.CommonJob.SourceInfo info);

protected int num_files;
protected size_t num_bytes;
protected int num_files_since_progress;
protected weak CountProgressCallback count_callback;
internal int num_files;
internal size_t num_bytes;
internal int num_files_since_progress;
internal weak CountProgressCallback count_callback;
}

[Compact]
[CCode (cname = "TransferInfo")]
protected class TransferInfo {
protected int num_files;
protected size_t num_bytes;
protected uint64 last_report_time;
protected int last_reported_files_left;
internal int num_files;
internal size_t num_bytes;
internal uint64 last_report_time;
internal int last_reported_files_left;
}

protected unowned Gtk.Window? parent_window;
Expand Down
205 changes: 205 additions & 0 deletions libcore/FileOperations/CopyMoveJob.vala
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,209 @@ public class Files.FileOperations.CopyMoveJob : CommonJob {
this.destination = destination;
is_move = true;
}

protected void report_copy_move_count_progress (CommonJob.SourceInfo source_info) {
string s;
string num_bytes_format = GLib.format_size (source_info.num_bytes);

if (!is_move) {
/// TRANSLATORS: %'d is a placeholder for a number. It must not be translated or removed.
/// %s is a placeholder for a size like "2 bytes" or "3 MB". It must not be translated or removed.
/// So this represents something like "Preparing to copy 100 files (200 MB)"
/// The order in which %'d and %s appear can be changed by using the right positional specifier.
s = ngettext (
"Preparing to copy %'d file (%s)",
"Preparing to copy %'d files (%s)",
source_info.num_files
).printf (source_info.num_files, num_bytes_format);
} else {
/// TRANSLATORS: %'d is a placeholder for a number. It must not be translated or removed.
/// %s is a placeholder for a size like "2 bytes" or "3 MB". It must not be translated or removed.
/// So this represents something like "Preparing to move 100 files (200 MB)"
/// The order in which %'d and %s appear can be changed by using the right positional specifier.
s = ngettext (
"Preparing to move %'d file (%s)",
"Preparing to move %'d files (%s)",
source_info.num_files
).printf (source_info.num_files, num_bytes_format);
}

progress.take_details (s);
progress.pulse_progress ();
}

protected void report_link_progress (int total, int left) {
/// TRANSLATORS: '\"%s\"' is a placeholder for the quoted basename of a file. It may change position but must not be translated or removed
/// '\"' is an escaped quoted mark. This may be replaced with another suitable character (escaped if necessary)
var s = _("Creating links in \"%s\"").printf (destination.get_parse_name ());

progress.take_status (s);
progress.take_details (
ngettext (
"Making link to %'d file",
"Making links to %'d files",
left
).printf (left)
);

progress.update_progress (left, total);
}

protected void report_copy_progress (CommonJob.SourceInfo source_info, CommonJob.TransferInfo transfer_info) {
int64 now = GLib.get_monotonic_time () * 1000; // in ns

if (transfer_info.last_report_time != 0 &&
((int64)transfer_info.last_report_time - now).abs () < 100 * CommonJob.NSEC_PER_MSEC) {
return;
}

/* See https://github.com/elementary/files/issues/464. The job data may become invalid, possibly
* due to a race. */
if (files.data == null || destination == null) {
return;
}

var srcname = FileUtils.custom_basename_from_file (files.data);
var destname = FileUtils.custom_basename_from_file (destination);

transfer_info.last_report_time = now;

int files_left = source_info.num_files - transfer_info.num_files;

/* Races and whatnot could cause this to be negative... */
if (files_left < 0) {
return;
}

if (files_left != transfer_info.last_reported_files_left ||
transfer_info.last_reported_files_left == 0) {
string s;

/* 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 (destination != null) {
/// TRANSLATORS: \"%s\" is a placeholder for the quoted basename of a file. It may change position but must not be translated or removed.
/// \" is an escaped quotation mark. This may be replaced with another suitable character (escaped if necessary).
s = (is_move ? _("Moving \"%s\" to \"%s\"") : _("Copying \"%s\" to \"%s\"")).printf (srcname, destname);
} else {
/// TRANSLATORS: \"%s\" is a placeholder for the quoted basename of a file. It may change position but must not be translated or removed.
/// \" is an escaped quotation mark. This may be replaced with another suitable character (escaped if necessary).
s = _("Duplicating \"%s\"").printf (srcname);
}
} else if (files != null && files.next == null) {
if (destination != null) {
/// TRANSLATORS: \"%s\" is a placeholder for the quoted basename of a file. It may change position but must not be translated or removed.
/// \" is an escaped quotation mark. This may be replaced with another suitable character (escaped if necessary).
/// %'d is a placeholder for a number. It must not be translated or removed.
/// Placeholders must appear in the same order but otherwise may change position.
s = (is_move ?
ngettext (
"Moving %'d file (in \"%s\") to \"%s\"",
"Moving %'d files (in \"%s\") to \"%s\"",
files_left
) :
ngettext (
"Copying %'d file (in \"%s\") to \"%s\"",
"Copying %'d files (in \"%s\") to \"%s\"",
files_left
)
).printf (files_left, srcname, destname);
} else {
/// TRANSLATORS: \"%s\" is a placeholder for the quoted basename of a file. It may change position but must not be translated or removed.
/// \" is an escaped quotation mark. This may be replaced with another suitable character (escaped if necessary).
s = ngettext (
"Duplicating %'d file (in \"%s\")",
"Duplicating %'d files (in \"%s\")",
files_left
).printf (files_left, destname);
}
} else {
if (destination != null) {
/// TRANSLATORS: \"%s\" is a placeholder for the quoted basename of a file. It may change position but must not be translated or removed.
/// \" is an escaped quotation mark. This may be replaced with another suitable character (escaped if necessary).
/// %'d is a placeholder for a number. It must not be translated or removed.
/// Placeholders must appear in the same order but otherwise may change position.
s = (is_move ?
ngettext (
"Moving %'d file to \"%s\"",
"Moving %'d files to \"%s\"",
files_left
) :
ngettext (
"Copying %'d file to \"%s\"",
"Copying %'d files to \"%s\"",
files_left
)
).printf (files_left, destname);
} else {
s = ngettext (
"Duplicating %'d file",
"Duplicating %'d files",
files_left
).printf (files_left);
}
}

progress.take_status ((owned) s);
}

var total_size = size_t.max (source_info.num_bytes, transfer_info.num_bytes);

double elapsed = time.elapsed ();
double transfer_rate = 0;
if (elapsed > 0) {
transfer_rate = transfer_info.num_bytes / elapsed;
}

if (elapsed < CommonJob.SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE &&
transfer_rate > 0) {
var num_bytes_format = GLib.format_size (transfer_info.num_bytes);
var total_size_format = GLib.format_size (total_size);
/// TRANSLATORS: %s is a placeholder for a size like "2 bytes" or "3 MB". It must not be translated or removed. So this represents something like "4 kb of 4 MB".
progress.take_details (_("%s of %s").printf (num_bytes_format, total_size_format));
} else {
var num_bytes_format = GLib.format_size (transfer_info.num_bytes);
var total_size_format = GLib.format_size (total_size);
var transfer_rate_format = GLib.format_size ((uint64) transfer_rate);
int remaining_time = (int )((total_size - transfer_info.num_bytes) / transfer_rate);
int formated_time_unit;
var formated_remaining_time = FileUtils.format_time (remaining_time, out formated_time_unit);


/// TRANSLATORS: The two first %s and the last %s will expand to a size
/// like "2 bytes" or "3 MB", the third %s to a time duration like
/// "2 minutes". It must not be translated or removed.
/// 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 "%s left" part).
/// The order in which %s appear can be changed by using the right positional specifier.
var s = ngettext (
"%s of %s \xE2\x80\x94 %s left (%s/sec)",
"%s of %s \xE2\x80\x94 %s left (%s/sec)",
formated_time_unit
).printf (num_bytes_format, total_size_format, formated_remaining_time, transfer_rate_format); //FIXME Remove opaque hex
progress.take_details ((owned) s);
}

progress.update_progress (transfer_info.num_bytes, total_size);
}

protected void report_move_progress (int total, int left) {
var dest_basename = Files.FileUtils.custom_basename_from_file (destination);
/// TRANSLATORS: '\"%s\"' is a placeholder for the quoted basename of a file. It may change position but must not be translated or removed
/// '\"' is an escaped quoted mark. This may be replaced with another suitable character (escaped if necessary)
var s = _("Preparing to move to \"%s\"").printf (dest_basename);

progress.take_status (s);
progress.take_details (
ngettext (
"Preparing to move %'d file",
"Preparing to move %'d files",
left
).printf (left)
);

progress.pulse_progress ();
}
}
77 changes: 77 additions & 0 deletions libcore/FileOperations/DeleteJob.vala
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,81 @@ public class Files.FileOperations.DeleteJob : CommonJob {
false,
CANCEL, DELETE) == 1;
}

protected void report_delete_progress (CommonJob.SourceInfo source_info, CommonJob.TransferInfo transfer_info) {
int64 now = GLib.get_monotonic_time () * 1000; // in ns
if (transfer_info.last_report_time != 0 &&
((int64)transfer_info.last_report_time - now).abs () < 100 * CommonJob.NSEC_PER_MSEC) {
return;
}

transfer_info.last_report_time = now;

int 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;
}

string files_left_s = ngettext (
"%'d file left to delete",
"%'d files left to delete",
files_left
).printf (files_left);

progress.take_status (_("Deleting files"));

double elapsed = time.elapsed ();
if (elapsed < CommonJob.SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE) {
progress.take_details ((owned) files_left_s);
} else {
double transfer_rate = transfer_info.num_files / elapsed;
int remaining_time = (int) GLib.Math.floor (files_left / transfer_rate);
int formated_time_unit;
string formated_time = FileUtils.format_time (remaining_time, out formated_time_unit);

/// TRANSLATORS: %s will expand to a time like "2 minutes". It must not be translated or removed.
/// The singular/plural form will be used depending on the remaining time (i.e. the %s argument).
string time_left_s = ngettext ("%s left", "%s left", formated_time_unit).printf (formated_time);

string details = files_left_s.concat ("\xE2\x80\x94", time_left_s); //FIXME Remove opaque hex
progress.take_details ((owned) details);
}

if (source_info.num_files != 0) {
progress.update_progress (transfer_info.num_files, source_info.num_files);
}
}

protected void report_delete_count_progress (CommonJob.SourceInfo source_info) {
/// TRANSLATORS: %'d is a placeholder for a number. It must not be translated or removed.
/// %s is a placeholder for a size like "2 bytes" or "3 MB". It must not be translated or removed.
/// So this represents something like "Preparing to delete 100 files (200 MB)"
/// The order in which %'d and %s appear can be changed by using the right positional specifier.
var s = ngettext (
"Preparing to delete %'d file (%s)",
"Preparing to delete %'d files (%s)",
source_info.num_files
).printf (source_info.num_files, GLib.format_size (source_info.num_bytes));
progress.take_details (s);
progress.pulse_progress ();
}

protected void report_trash_progress (int files_trashed, int total_files) {
var files_left = total_files - files_trashed;

progress.take_status (_("Moving files to trash"));

var s = ngettext (
"%'d file left to trash",
"%'d files left to trash",
files_left
).printf (files_left);
progress.take_details (s);

if (total_files != 0) {
progress.update_progress (files_trashed, total_files);
}
}
}
Loading

0 comments on commit a1dd90f

Please sign in to comment.