mirror of
https://code.blicky.net/yorhel/ncdu.git
synced 2026-01-14 09:48:40 -09:00
Support moving files/dirs to trash (fixes #215)
This commit is contained in:
parent
f37362af36
commit
1eac36a7ea
4 changed files with 60 additions and 4 deletions
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
src/main.zig
11
src/main.zig
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue