Support moving files/dirs to trash (fixes #215)

This commit is contained in:
Alex Saveau 2022-12-20 16:41:33 -08:00
parent f37362af36
commit 1eac36a7ea
No known key found for this signature in database
GPG key ID: 3F8D5B16EB169D48
4 changed files with 60 additions and 4 deletions

View file

@ -13,6 +13,7 @@ pub fn build(b: *std.build.Builder) void {
exe.addCSourceFile("src/ncurses_refs.c", &[_][]const u8{});
exe.linkLibC();
exe.linkSystemLibrary("ncursesw");
exe.linkSystemLibrary("gio-2.0");
exe.install();
const run_cmd = exe.run();

View file

@ -583,6 +583,7 @@ const help = struct {
"C", "Sort by items (ascending/descending)",
"M", "Sort by mtime (-e flag)",
"d", "Delete selected file or directory",
"D", "Move selected file or directory to trash",
"t", "Toggle dirs before files when sorting",
"g", "Show percentage and/or graph",
"u", "Show/hide hard link shared sizes",
@ -872,6 +873,17 @@ pub fn keyInput(ch: i32) void {
delete.setup(dir_parent, e, next);
}
},
'D' => {
if (dir_items.items.len == 0) {
} else if (dir_items.items[cursor_idx]) |e| {
main.state = .trash;
const next =
if (cursor_idx+1 < dir_items.items.len) dir_items.items[cursor_idx+1]
else if (cursor_idx == 0) null
else dir_items.items[cursor_idx-1];
delete.setupTrash(dir_parent, e, next);
}
},
// Sort & filter settings
'n' => sortToggle(.name, .asc),

View file

@ -7,6 +7,8 @@ const model = @import("model.zig");
const ui = @import("ui.zig");
const browser = @import("browser.zig");
const util = @import("util.zig");
const gio = @cImport(@cInclude("gio/gio.h"));
const gobject = @cImport(@cInclude("glib-object.h"));
var parent: *model.Dir = undefined;
var entry: *model.Entry = undefined;
@ -24,6 +26,14 @@ pub fn setup(p: *model.Dir, e: *model.Entry, n: ?*model.Entry) void {
confirm = .no;
}
pub fn setupTrash(p: *model.Dir, e: *model.Entry, n: ?*model.Entry) void {
parent = p;
entry = e;
next_sel = n;
state = .busy;
confirm = .no;
}
// Returns true to abort scanning.
fn err(e: anyerror) bool {
@ -68,6 +78,22 @@ fn deleteItem(dir: std.fs.Dir, path: [:0]const u8, ptr: *align(1) ?*model.Entry)
return false;
}
fn trashItem(_: std.fs.Dir, path: [:0]const u8, ptr: *align(1) ?*model.Entry) bool {
entry = ptr.*.?;
if (main.state != .trash)
return true;
const gfile = gio.g_file_new_for_path(path);
defer gobject.g_object_unref(gfile);
if (gio.g_file_trash(gfile, null, null) > 0) {
ptr.*.?.delStats(parent);
ptr.* = ptr.*.?.next;
}
return false; // Not used
}
// Returns the item that should be selected in the browser.
pub fn delete() ?*model.Entry {
while (main.state == .delete and state == .confirm)
@ -75,6 +101,18 @@ pub fn delete() ?*model.Entry {
if (main.state != .delete)
return entry;
return performOp(deleteItem);
}
// Returns the item that should be selected in the browser.
pub fn trash() ?*model.Entry {
if (main.state != .trash)
return entry;
return performOp(trashItem);
}
fn performOp(op: fn (std.fs.Dir, [:0]const u8, *align(1) ?*model.Entry) bool) ?*model.Entry {
// Find the pointer to this entry
const e = entry;
var it = &parent.sub;
@ -89,7 +127,7 @@ pub fn delete() ?*model.Entry {
path.append('/') catch unreachable;
path.appendSlice(entry.name()) catch unreachable;
_ = deleteItem(std.fs.cwd(), util.arrayListBufZ(&path), it);
_ = op(std.fs.cwd(), util.arrayListBufZ(&path), it);
model.inodes.addAllStats();
return if (it.* == e) e else next_sel;
}

View file

@ -76,7 +76,7 @@ pub const config = struct {
pub var ignore_delete_errors: bool = false;
};
pub var state: enum { scan, browse, refresh, shell, delete } = .scan;
pub var state: enum { scan, browse, refresh, shell, delete, trash } = .scan;
// Simple generic argument parser, supports getopt_long() style arguments.
const Args = struct {
@ -509,6 +509,11 @@ pub fn main() void {
state = .browse;
browser.loadDir(next);
},
.trash => {
const next = delete.trash();
state = .browse;
browser.loadDir(next);
},
else => handleEvent(true, false)
}
}
@ -524,7 +529,7 @@ pub fn handleEvent(block: bool, force_draw: bool) void {
switch (state) {
.scan, .refresh => scan.draw(),
.browse => browser.draw(),
.delete => delete.draw(),
.delete, .trash => delete.draw(),
.shell => unreachable,
}
if (ui.inited) _ = ui.c.refresh();
@ -543,7 +548,7 @@ pub fn handleEvent(block: bool, force_draw: bool) void {
switch (state) {
.scan, .refresh => scan.keyInput(ch),
.browse => browser.keyInput(ch),
.delete => delete.keyInput(ch),
.delete, .trash => delete.keyInput(ch),
.shell => unreachable,
}
firstblock = false;