diff --git a/build.zig b/build.zig index 7c67444..01edab7 100644 --- a/build.zig +++ b/build.zig @@ -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(); diff --git a/src/browser.zig b/src/browser.zig index 13e09eb..e9b132d 100644 --- a/src/browser.zig +++ b/src/browser.zig @@ -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), diff --git a/src/delete.zig b/src/delete.zig index 22c2706..4beef15 100644 --- a/src/delete.zig +++ b/src/delete.zig @@ -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; } diff --git a/src/main.zig b/src/main.zig index a04d96f..eb118b5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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;