Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add option to permanently delete files and folders #359

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions i18n/de/cosmic_files.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ open-multiple-folders = Mehrere Ordner öffnen
save = Speichern
save-file = Datei speichern

## Dauerhaft Löschen Dialog
selected-items = {$items} gewählte {$items ->
[one] Objekt
*[other] Objekte
}
permanently-delete-question = {$target} dauerhaft löschen?
delete = Löschen
permanently-delete-warning = Dauerhaft gelöschte Objekte können nicht wiederhergestellt werden

# Umbenennen-Dialog
rename-file = Datei umbenennen
rename-folder = Ordner umbenennen
Expand Down Expand Up @@ -100,6 +109,14 @@ restored = {$items} {$items ->
} aus dem {trash} wiederhergestellt
undo = Rückgängig
unknown-folder = unbekannter Ordner
permanently-deleting = Lösche {$items} {$items ->
[one] Objekt
*[other] Objekte
} dauerhaft
permanently-deleted = {$items} {$items ->
[one] Objekt
*[other] Objekte
} dauerhaft gelöscht

## Öffnen mit
open-with = Öffnen mit
Expand All @@ -111,7 +128,10 @@ properties = Eigenschaften
## Einstellungen
settings = Einstellungen
settings-tab = Tab
settings-optional-context-menu-actions = Optionale Aktionen im Kontextmenü
settings-optional-context-menu-actions-description = Weitere Aktionen im Menü anzeigen. Tastenkürzel können auch für ausgeblendete Aktionen verwendet werden.
settings-show-hidden = Versteckte Dateien anzeigen
settings-show-delete-permanently = Dauerhaft löschen
default-view = Standardansicht
icon-size-list = Symbolgröße (Liste)
icon-size-grid = Symbolgröße (Raster)
Expand All @@ -133,6 +153,7 @@ new-file = Neue Datei
new-folder = Neuer Ordner
open-in-terminal = Im Terminal öffnen
move-to-trash = In den Papierkorb verschieben
permanently-delete = Dauerhaft löschen...
restore-from-trash = Aus dem Papierkorb wiederherstellen
remove-from-sidebar = Von der Seitenleiste entfernen
sort-by-name = Nach Name sortieren
Expand Down
21 changes: 21 additions & 0 deletions i18n/en/cosmic_files.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ open-multiple-folders = Open multiple folders
save = Save
save-file = Save file

## Permanently delete Dialog
selected-items = {$items} selected {$items ->
[one] item
*[other] items
}
permanently-delete-question = Permanently delete {$target}?
delete = Delete
permanently-delete-warning = Permanently deleted items can not be restored

## Rename Dialog
rename-file = Rename file
rename-folder = Rename folder
Expand Down Expand Up @@ -108,6 +117,14 @@ moved = Moved {$items} {$items ->
[one] item
*[other] items
} from {$from} to {$to}
permanently-deleting = Permanently deleting {$items} {$items ->
[one] item
*[other] items
}
permanently-deleted = Permanently deleted {$items} {$items ->
[one] item
*[other] items
}
renaming = Renaming {$from} to {$to}
renamed = Renamed {$from} to {$to}
restoring = Restoring {$items} {$items ->
Expand All @@ -131,7 +148,10 @@ show-details = Show details
## Settings
settings = Settings
settings-tab = Tab
settings-optional-context-menu-actions = Optional Context Menu Actions
settings-optional-context-menu-actions-description = Show more actions in the menus. Keyboard shortcuts can be used even if the actions are not shown.
settings-show-hidden = Show hidden files
settings-show-delete-permanently = Permanently delete
default-view = Default view
icon-size-list = Icon size (list)
icon-size-grid = Icon size (grid)
Expand All @@ -154,6 +174,7 @@ new-file = New file...
new-folder = New folder...
open-in-terminal = Open in terminal
move-to-trash = Move to trash
permanently-delete = Delete permanently...
restore-from-trash = Restore from trash
remove-from-sidebar = Remove from sidebar
sort-by-name = Sort by name
Expand Down
56 changes: 56 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub enum Action {
OpenTerminal,
OpenWith,
Paste,
PermanentlyDelete,
Properties,
Rename,
RestoreFromTrash,
Expand Down Expand Up @@ -138,6 +139,7 @@ impl Action {
Action::OpenTerminal => Message::OpenTerminal(entity_opt),
Action::OpenWith => Message::ToggleContextPage(ContextPage::OpenWith),
Action::Paste => Message::Paste(entity_opt),
Action::PermanentlyDelete => Message::PermanentlyDelete(entity_opt),
Action::Properties => Message::ToggleContextPage(ContextPage::Properties(None)),
Action::Rename => Message::Rename(entity_opt),
Action::RestoreFromTrash => Message::RestoreFromTrash(entity_opt),
Expand Down Expand Up @@ -243,6 +245,7 @@ pub enum Message {
PendingComplete(u64),
PendingError(u64, String),
PendingProgress(u64, f32),
PermanentlyDelete(Option<Entity>),
RescanTrash,
Rename(Option<Entity>),
ReplaceResult(ReplaceResult),
Expand Down Expand Up @@ -306,6 +309,9 @@ pub enum DialogPage {
name: String,
dir: bool,
},
PermanentlyDelete {
paths: Vec<PathBuf>,
},
RenameItem {
from: PathBuf,
parent: PathBuf,
Expand Down Expand Up @@ -969,6 +975,24 @@ impl App {
)
})
.into(),
widget::settings::view_section(fl!("settings-optional-context-menu-actions"))
.add(widget::text(fl!(
"settings-optional-context-menu-actions-description"
)))
.add({
let tab_config = self.config.tab.clone();
widget::settings::item::builder(fl!("settings-show-delete-permanently"))
.toggler(
tab_config.show_delete_permanently,
move |show_delete_permanently| {
Message::TabConfig(TabConfig {
show_delete_permanently,
..tab_config
})
},
)
})
.into(),
])
.into()
}
Expand Down Expand Up @@ -1305,6 +1329,9 @@ impl Application for App {
Operation::NewFile { path }
});
}
DialogPage::PermanentlyDelete { paths } => {
self.operation(Operation::PermanentlyDelete { paths });
}
DialogPage::RenameItem {
from, parent, name, ..
} => {
Expand Down Expand Up @@ -1712,6 +1739,13 @@ impl Application for App {
}
return self.update_notification();
}
Message::PermanentlyDelete(entity_opt) => {
let paths = self.selected_paths(entity_opt);
if !paths.is_empty() {
self.dialog_pages
.push_back(DialogPage::PermanentlyDelete { paths });
}
}
Message::RescanTrash => {
// Update trash icon if empty/full
let maybe_entity = self.nav_model.iter().find(|&entity| {
Expand Down Expand Up @@ -2401,6 +2435,28 @@ impl Application for App {
.spacing(space_xxs),
)
}
DialogPage::PermanentlyDelete { paths } => {
let target = if paths.len() == 1 {
format!(
"»{}«",
paths[0]
.file_name()
.map(std::ffi::OsStr::to_string_lossy)
.unwrap_or_else(|| paths[0].to_string_lossy())
)
} else {
fl!("selected-items", items = paths.len())
};
widget::dialog(fl!("permanently-delete-question", target = target))
.primary_action(
widget::button::destructive(fl!("delete"))
.on_press(Message::DialogComplete),
)
.secondary_action(
widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
)
.control(widget::text(fl!("permanently-delete-warning")))
}
DialogPage::RenameItem {
from,
parent,
Expand Down
3 changes: 3 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ pub struct TabConfig {
pub sort_direction: bool,
/// Icon zoom
pub icon_sizes: IconSizes,
/// Show delete permanently context menu item
pub show_delete_permanently: bool,
}

impl Default for TabConfig {
Expand All @@ -142,6 +144,7 @@ impl Default for TabConfig {
sort_name: HeadingOptions::Name,
sort_direction: true,
icon_sizes: IconSizes::default(),
show_delete_permanently: false,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/key_bind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub fn key_binds() -> HashMap<KeyBind, Action> {
bind!([Shift], Key::Named(Named::ArrowUp), ItemUp);
bind!([Alt], Key::Named(Named::ArrowUp), LocationUp);
bind!([], Key::Named(Named::Delete), MoveToTrash);
bind!([Shift], Key::Named(Named::Delete), PermanentlyDelete);
bind!([Ctrl, Shift], Key::Character("n".into()), NewFolder);
bind!([], Key::Named(Named::Enter), Open);
bind!([Ctrl], Key::Named(Named::Enter), OpenInNewTab);
Expand Down
6 changes: 6 additions & 0 deletions src/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub fn context_menu<'a>(
let TabConfig {
sort_name,
sort_direction,
show_delete_permanently,
..
} = tab.config;
let sort_item = |label, variant| {
Expand Down Expand Up @@ -141,6 +142,11 @@ pub fn context_menu<'a>(
children.push(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar).into());
children.push(container(horizontal_rule(1)).padding([0, 8]).into());
children.push(menu_item(fl!("move-to-trash"), Action::MoveToTrash).into());
if show_delete_permanently {
children.push(
menu_item(fl!("permanently-delete"), Action::PermanentlyDelete).into(),
);
}
} else {
//TODO: need better designs for menu with no selection
//TODO: have things like properties but they apply to the folder?
Expand Down
36 changes: 36 additions & 0 deletions src/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ pub enum Operation {
NewFolder {
path: PathBuf,
},
/// Permanently delete items, skipping the trash
PermanentlyDelete {
paths: Vec<PathBuf>,
},
Rename {
from: PathBuf,
to: PathBuf,
Expand Down Expand Up @@ -276,6 +280,7 @@ impl Operation {
name = file_name(path),
parent = parent_name(path)
),
Self::PermanentlyDelete { paths } => fl!("permanently-deleting", items = paths.len()),
Self::Rename { from, to } => {
fl!("renaming", from = file_name(from), to = file_name(to))
}
Expand Down Expand Up @@ -320,6 +325,7 @@ impl Operation {
name = file_name(path),
parent = parent_name(path)
),
Self::PermanentlyDelete { paths } => fl!("permanently-deleted", items = paths.len()),
Self::Rename { from, to } => fl!("renamed", from = file_name(from), to = file_name(to)),
Self::Restore { paths } => fl!("restored", items = paths.len()),
}
Expand Down Expand Up @@ -583,6 +589,36 @@ impl Operation {
.send(Message::PendingProgress(id, 100.0))
.await;
}
Self::PermanentlyDelete { paths } => {
let total = paths.len();
let mut count = 0;
for path in paths {
tokio::task::spawn_blocking(|| {
if path.is_symlink() || path.is_file() {
fs::remove_file(path)
} else if path.is_dir() {
fs::remove_dir_all(path)
} else {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"File to delete is not symlink, file or directory",
))
}
})
.await
.map_err(err_str)?
.map_err(err_str)?;
count += 1;
let _ = msg_tx
.lock()
.await
.send(Message::PendingProgress(
id,
100.0 * (count as f32) / (total as f32),
))
.await;
}
}
Self::Rename { from, to } => {
tokio::task::spawn_blocking(|| fs::rename(from, to))
.await
Expand Down