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.addCSourceFile("src/ncurses_refs.c", &[_][]const u8{});
exe.linkLibC(); exe.linkLibC();
exe.linkSystemLibrary("ncursesw"); exe.linkSystemLibrary("ncursesw");
exe.linkSystemLibrary("gio-2.0");
exe.install(); exe.install();
const run_cmd = exe.run(); const run_cmd = exe.run();

View file

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

View file

@ -7,6 +7,8 @@ const model = @import("model.zig");
const ui = @import("ui.zig"); const ui = @import("ui.zig");
const browser = @import("browser.zig"); const browser = @import("browser.zig");
const util = @import("util.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 parent: *model.Dir = undefined;
var entry: *model.Entry = 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; 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. // Returns true to abort scanning.
fn err(e: anyerror) bool { 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; 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. // Returns the item that should be selected in the browser.
pub fn delete() ?*model.Entry { pub fn delete() ?*model.Entry {
while (main.state == .delete and state == .confirm) while (main.state == .delete and state == .confirm)
@ -75,6 +101,18 @@ pub fn delete() ?*model.Entry {
if (main.state != .delete) if (main.state != .delete)
return entry; 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 // Find the pointer to this entry
const e = entry; const e = entry;
var it = &parent.sub; var it = &parent.sub;
@ -89,7 +127,7 @@ pub fn delete() ?*model.Entry {
path.append('/') catch unreachable; path.append('/') catch unreachable;
path.appendSlice(entry.name()) 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(); model.inodes.addAllStats();
return if (it.* == e) e else next_sel; 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 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. // Simple generic argument parser, supports getopt_long() style arguments.
const Args = struct { const Args = struct {
@ -509,6 +509,11 @@ pub fn main() void {
state = .browse; state = .browse;
browser.loadDir(next); browser.loadDir(next);
}, },
.trash => {
const next = delete.trash();
state = .browse;
browser.loadDir(next);
},
else => handleEvent(true, false) else => handleEvent(true, false)
} }
} }
@ -524,7 +529,7 @@ pub fn handleEvent(block: bool, force_draw: bool) void {
switch (state) { switch (state) {
.scan, .refresh => scan.draw(), .scan, .refresh => scan.draw(),
.browse => browser.draw(), .browse => browser.draw(),
.delete => delete.draw(), .delete, .trash => delete.draw(),
.shell => unreachable, .shell => unreachable,
} }
if (ui.inited) _ = ui.c.refresh(); if (ui.inited) _ = ui.c.refresh();
@ -543,7 +548,7 @@ pub fn handleEvent(block: bool, force_draw: bool) void {
switch (state) { switch (state) {
.scan, .refresh => scan.keyInput(ch), .scan, .refresh => scan.keyInput(ch),
.browse => browser.keyInput(ch), .browse => browser.keyInput(ch),
.delete => delete.keyInput(ch), .delete, .trash => delete.keyInput(ch),
.shell => unreachable, .shell => unreachable,
} }
firstblock = false; firstblock = false;