From aa63a14f68cef631c01f6057106608c2c67b6e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= <41945903+qarmin@users.noreply.github.com> Date: Sat, 17 Oct 2020 12:14:25 +0200 Subject: [PATCH] Improve deleting files in Similar files in GUI (#75) --- czkawka_gui/src/main.rs | 119 ++++++++++++++++++++++++++++++++++------ 1 file changed, 102 insertions(+), 17 deletions(-) diff --git a/czkawka_gui/src/main.rs b/czkawka_gui/src/main.rs index e006ef5..6f46957 100644 --- a/czkawka_gui/src/main.rs +++ b/czkawka_gui/src/main.rs @@ -15,12 +15,11 @@ use chrono::NaiveDateTime; use crossbeam_channel::unbounded; use czkawka_core::big_file::BigFile; use czkawka_core::common_traits::SaveResults; -use czkawka_core::duplicate::CheckingMethod; +use czkawka_core::duplicate::{CheckingMethod, DuplicateFinder}; use czkawka_core::empty_files::EmptyFiles; use czkawka_core::empty_folder::EmptyFolder; use czkawka_core::similar_files::SimilarImages; use czkawka_core::temporary::Temporary; -use duplicate::DuplicateFinder; use gtk::prelude::*; use gtk::{Builder, SelectionMode, TreeIter, TreeView}; use std::cell::RefCell; @@ -425,11 +424,8 @@ fn main() { // Connect Notebook Tabs { let shared_buttons = shared_buttons.clone(); - let buttons_array = buttons_array.clone(); - let notebook_main_children_names = notebook_main_children_names.clone(); - let notebook_main_clone = notebook_main.clone(); notebook_main_clone.connect_switch_page(move |_, _, number| { @@ -684,6 +680,9 @@ fn main() { let selection = tree_view.get_selection(); let (selection_rows, tree_model) = selection.get_selected_rows(); + if selection_rows.is_empty() { + return; + } let list_store = tree_model.clone().downcast::().unwrap(); // let new_tree_model = TreeModel::new(); // TODO - maybe create new model when inserting a new data, because this seems to be not optimal when using thousands of rows @@ -711,6 +710,9 @@ fn main() { let selection = tree_view.get_selection(); let (selection_rows, tree_model) = selection.get_selected_rows(); + if selection_rows.is_empty() { + return; + } let list_store = tree_model.clone().downcast::().unwrap(); // let new_tree_model = TreeModel::new(); // TODO - maybe create new model when inserting a new data, because this seems to be not optimal when using thousands of rows @@ -791,6 +793,9 @@ fn main() { let selection = tree_view.get_selection(); let (selection_rows, tree_model) = selection.get_selected_rows(); + if selection_rows.is_empty() { + return; + } let list_store = tree_model.clone().downcast::().unwrap(); // let new_tree_model = TreeModel::new(); // TODO - maybe create new model when inserting a new data, because this seems to be not optimal when using thousands of rows @@ -818,6 +823,9 @@ fn main() { let selection = tree_view.get_selection(); let (selection_rows, tree_model) = selection.get_selected_rows(); + if selection_rows.is_empty() { + return; + } let list_store = tree_model.clone().downcast::().unwrap(); // let new_tree_model = TreeModel::new(); // TODO - maybe create new model when inserting a new data, because this seems to be not optimal when using thousands of rows @@ -845,6 +853,9 @@ fn main() { let selection = tree_view.get_selection(); let (selection_rows, tree_model) = selection.get_selected_rows(); + if selection_rows.is_empty() { + return; + } let list_store = tree_model.clone().downcast::().unwrap(); // let new_tree_model = TreeModel::new(); // TODO - maybe create new model when inserting a new data, because this seems to be not optimal when using thousands of rows @@ -872,31 +883,105 @@ fn main() { let selection = tree_view.get_selection(); let (selection_rows, tree_model) = selection.get_selected_rows(); + if selection_rows.is_empty() { + return; + } let list_store = tree_model.clone().downcast::().unwrap(); // let new_tree_model = TreeModel::new(); // TODO - maybe create new model when inserting a new data, because this seems to be not optimal when using thousands of rows let mut messages: String = "".to_string(); - // Must be deleted from end to start, because when deleting entries, TreePath(and also TreeIter) will points to invalid data + let mut vec_path_to_delete: Vec<(String, String)> = Vec::new(); + + // Just remove file, later must be deleted list entry with all occurencies for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsBigFiles::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsBigFiles::Path as i32).get::().unwrap().unwrap(); + let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSimilarImages::Name as i32).get::().unwrap().unwrap(); + let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSimilarImages::Path as i32).get::().unwrap().unwrap(); + + if fs::remove_file(format!("{}/{}", path, name)).is_err() { + messages += format!( + "Failed to remove file {}/{}. It is possible that you already deleted it, because similar images shows all possible file doesn't exists or you don't have permissions.\n", + path, name + ) + .as_str() + } + vec_path_to_delete.push((path, name)); + } + // Must be deleted from end to start, because when deleting entries, TreePath(and also TreeIter) will points to invalid data + for path_to_delete in vec_path_to_delete { + let mut vec_tree_path_to_delete: Vec = Vec::new(); + + let iter = list_store.get_iter_first().unwrap(); + let mut take_child_mode = false; // When original image is searched one, we must remove all occurences of its children + let mut prepared_for_delete; + loop { + prepared_for_delete = false; + if take_child_mode { + let color = tree_model.get_value(&iter, ColumnsSimilarImages::Color as i32).get::().unwrap().unwrap(); + if color == HEADER_ROW_COLOR { + take_child_mode = false; + } else { + prepared_for_delete = true; + } + } else { + let path = tree_model.get_value(&iter, ColumnsSimilarImages::Path as i32).get::().unwrap().unwrap(); + if path == path_to_delete.0 { + let name = tree_model.get_value(&iter, ColumnsSimilarImages::Name as i32).get::().unwrap().unwrap(); + if name == path_to_delete.1 { + let color = tree_model.get_value(&iter, ColumnsSimilarImages::Color as i32).get::().unwrap().unwrap(); + if color == HEADER_ROW_COLOR { + take_child_mode = true; + } + prepared_for_delete = true; + } + } + } - match fs::remove_file(format!("{}/{}", path, name)) { - Ok(_) => { - list_store.remove(&list_store.get_iter(tree_path).unwrap()); + if prepared_for_delete { + vec_tree_path_to_delete.push(list_store.get_path(&iter).unwrap()); + } + + if !list_store.iter_next(&iter) { + break; + } + } + + for tree_path in vec_tree_path_to_delete.iter().rev() { + list_store.remove(&list_store.get_iter(&tree_path).unwrap()); + } + } + // End run to remove single header rows(without duplicates) + if let Some(next_iter) = list_store.get_iter_first() { + let mut header_was_before = false; + let mut vec_tree_path_to_delete: Vec = Vec::new(); + let mut current_iter = next_iter.clone(); + loop { + let color = tree_model.get_value(&next_iter, ColumnsSimilarImages::Color as i32).get::().unwrap().unwrap(); + if color == HEADER_ROW_COLOR { + if header_was_before { + vec_tree_path_to_delete.push(list_store.get_path(¤t_iter).unwrap()); + } else { + header_was_before = true; + } + } else { + header_was_before = false; } - Err(_) => { - messages += format!( - "Failed to remove file {}/{}. It is possible that you already deleted it, because similar images shows all possible file doesn't exists or you don't have permissions.\n", - path, name - ) - .as_str() + + current_iter = next_iter.clone(); + if !list_store.iter_next(&next_iter) { + break; } } } + // Last step, remove orphan header if exists + if let Some(iter) = list_store.get_iter_first() { + if !list_store.iter_next(&iter) { + list_store.clear(); + } + } + text_view_errors.get_buffer().unwrap().set_text(messages.as_str()); selection.unselect_all(); }