diff --git a/czkawka_core/src/big_file.rs b/czkawka_core/src/big_file.rs index b1ba28f..921d0bc 100644 --- a/czkawka_core/src/big_file.rs +++ b/czkawka_core/src/big_file.rs @@ -73,9 +73,9 @@ impl BigFile { } } - pub fn find_big_files(&mut self, rx: Option<&Receiver<()>>) { + pub fn find_big_files(&mut self, stop_receiver: Option<&Receiver<()>>) { self.optimize_directories(); - if !self.look_for_big_files(rx) { + if !self.look_for_big_files(stop_receiver) { self.stopped_search = true; return; } @@ -111,7 +111,7 @@ impl BigFile { self.allowed_extensions.set_allowed_extensions(allowed_extensions, &mut self.text_messages); } - fn look_for_big_files(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn look_for_big_files(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -122,7 +122,7 @@ impl BigFile { self.information.number_of_checked_folders += folders_to_check.len(); while !folders_to_check.is_empty() { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let current_folder = folders_to_check.pop().unwrap(); diff --git a/czkawka_core/src/duplicate.rs b/czkawka_core/src/duplicate.rs index 37d61d1..3207307 100644 --- a/czkawka_core/src/duplicate.rs +++ b/czkawka_core/src/duplicate.rs @@ -114,28 +114,28 @@ impl DuplicateFinder { } } - pub fn find_duplicates(&mut self, rx: Option<&Receiver<()>>) { + pub fn find_duplicates(&mut self, stop_receiver: Option<&Receiver<()>>) { self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); match self.check_method { CheckingMethod::Name => { - if !self.check_files_name(rx) { + if !self.check_files_name(stop_receiver) { self.stopped_search = true; return; } } CheckingMethod::Size => { - if !self.check_files_size(rx) { + if !self.check_files_size(stop_receiver) { self.stopped_search = true; return; } } CheckingMethod::HashMB | CheckingMethod::Hash => { - if !self.check_files_size(rx) { + if !self.check_files_size(stop_receiver) { self.stopped_search = true; return; } - if !self.check_files_hash(rx) { + if !self.check_files_hash(stop_receiver) { self.stopped_search = true; return; } @@ -210,7 +210,7 @@ impl DuplicateFinder { self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages); } - fn check_files_name(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn check_files_name(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { // TODO maybe add multithreading checking files let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -222,7 +222,7 @@ impl DuplicateFinder { self.information.number_of_checked_folders += folders_to_check.len(); while !folders_to_check.is_empty() { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let current_folder = folders_to_check.pop().unwrap(); @@ -348,7 +348,7 @@ impl DuplicateFinder { /// Read file length and puts it to different boxes(each for different lengths) /// If in box is only 1 result, then it is removed - fn check_files_size(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn check_files_size(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { // TODO maybe add multithreading checking files let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -360,7 +360,7 @@ impl DuplicateFinder { self.information.number_of_checked_folders += folders_to_check.len(); while !folders_to_check.is_empty() { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let current_folder = folders_to_check.pop().unwrap(); @@ -486,7 +486,7 @@ impl DuplicateFinder { } /// The slowest checking type, which must be applied after checking for size - fn check_files_hash(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn check_files_hash(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { if self.hash_type != HashType::Blake3 { panic!(); // TODO Add more hash types } @@ -501,7 +501,7 @@ impl DuplicateFinder { hashmap_with_hash = Default::default(); for file_entry in vector { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } file_handler = match File::open(&file_entry.path) { @@ -547,7 +547,7 @@ impl DuplicateFinder { hashmap_with_hash = Default::default(); for file_entry in vector { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } file_handler = match File::open(&file_entry.path) { diff --git a/czkawka_core/src/empty_files.rs b/czkawka_core/src/empty_files.rs index 3103cf7..33fd331 100644 --- a/czkawka_core/src/empty_files.rs +++ b/czkawka_core/src/empty_files.rs @@ -70,9 +70,9 @@ impl EmptyFiles { } /// Finding empty files, save results to internal struct variables - pub fn find_empty_files(&mut self, rx: Option<&Receiver<()>>) { + pub fn find_empty_files(&mut self, stop_receiver: Option<&Receiver<()>>) { self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); - if !self.check_files(rx) { + if !self.check_files(stop_receiver) { self.stopped_search = true; return; } @@ -120,7 +120,7 @@ impl EmptyFiles { } /// Check files for any with size == 0 - fn check_files(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -131,7 +131,7 @@ impl EmptyFiles { self.information.number_of_checked_folders += folders_to_check.len(); while !folders_to_check.is_empty() { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let current_folder = folders_to_check.pop().unwrap(); diff --git a/czkawka_core/src/empty_folder.rs b/czkawka_core/src/empty_folder.rs index b2d0f62..93d804e 100644 --- a/czkawka_core/src/empty_folder.rs +++ b/czkawka_core/src/empty_folder.rs @@ -88,9 +88,9 @@ impl EmptyFolder { self.directories.set_excluded_directory(excluded_directory, &mut self.text_messages); } /// Public function used by CLI to search for empty folders - pub fn find_empty_folders(&mut self, rx: Option<&Receiver<()>>) { + pub fn find_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>) { self.directories.optimize_directories(true, &mut self.text_messages); - if !self.check_for_empty_folders(rx) { + if !self.check_for_empty_folders(stop_receiver) { self.stopped_search = true; return; } @@ -128,7 +128,7 @@ impl EmptyFolder { /// Function to check if folder are empty. /// Parameter initial_checking for second check before deleting to be sure that checked folder is still empty - fn check_for_empty_folders(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn check_for_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector let mut folders_checked: BTreeMap = Default::default(); @@ -147,7 +147,7 @@ impl EmptyFolder { } while !folders_to_check.is_empty() { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } self.information.number_of_checked_folders += 1; diff --git a/czkawka_core/src/same_music.rs b/czkawka_core/src/same_music.rs index 0a240df..dd473e5 100644 --- a/czkawka_core/src/same_music.rs +++ b/czkawka_core/src/same_music.rs @@ -101,13 +101,13 @@ impl SameMusic { } } - pub fn find_same_music(&mut self, rx: Option<&Receiver<()>>) { + pub fn find_same_music(&mut self, stop_receiver: Option<&Receiver<()>>) { self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); - if !self.check_files(rx) { + if !self.check_files(stop_receiver) { self.stopped_search = true; return; } - if !self.check_for_duplicates(rx) { + if !self.check_for_duplicates(stop_receiver) { self.stopped_search = true; return; } @@ -159,7 +159,7 @@ impl SameMusic { } /// Check files for any with size == 0 - fn check_files(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -170,7 +170,7 @@ impl SameMusic { self.information.number_of_checked_folders += folders_to_check.len(); while !folders_to_check.is_empty() { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let current_folder = folders_to_check.pop().unwrap(); @@ -289,7 +289,7 @@ impl SameMusic { true } - fn check_for_duplicates(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn check_for_duplicates(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { if MusicSimilarity::NONE == self.music_similarity { panic!("This can't be none"); } @@ -300,7 +300,7 @@ impl SameMusic { if (self.music_similarity & MusicSimilarity::TITLE) == MusicSimilarity::TITLE { for vec_file_entry in old_duplicates { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let mut hash_map: HashMap> = Default::default(); @@ -323,7 +323,7 @@ impl SameMusic { if (self.music_similarity & MusicSimilarity::ARTIST) == MusicSimilarity::ARTIST { for vec_file_entry in old_duplicates { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let mut hash_map: HashMap> = Default::default(); @@ -346,7 +346,7 @@ impl SameMusic { if (self.music_similarity & MusicSimilarity::ALBUM_TITLE) == MusicSimilarity::ALBUM_TITLE { for vec_file_entry in old_duplicates { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let mut hash_map: HashMap> = Default::default(); @@ -369,7 +369,7 @@ impl SameMusic { if (self.music_similarity & MusicSimilarity::ALBUM_ARTIST) == MusicSimilarity::ALBUM_ARTIST { for vec_file_entry in old_duplicates { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let mut hash_map: HashMap> = Default::default(); @@ -392,7 +392,7 @@ impl SameMusic { if (self.music_similarity & MusicSimilarity::YEAR) == MusicSimilarity::YEAR { for vec_file_entry in old_duplicates { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let mut hash_map: HashMap> = Default::default(); diff --git a/czkawka_core/src/similar_files.rs b/czkawka_core/src/similar_files.rs index 570a9d4..051254b 100644 --- a/czkawka_core/src/similar_files.rs +++ b/czkawka_core/src/similar_files.rs @@ -129,9 +129,9 @@ impl SimilarImages { } /// Public function used by CLI to search for empty folders - pub fn find_similar_images(&mut self, rx: Option<&Receiver<()>>) { + pub fn find_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>) { self.directories.optimize_directories(true, &mut self.text_messages); - if !self.check_for_similar_images(rx) { + if !self.check_for_similar_images(stop_receiver) { self.stopped_search = true; return; } @@ -147,7 +147,7 @@ impl SimilarImages { /// Function to check if folder are empty. /// Parameter initial_checking for second check before deleting to be sure that checked folder is still empty - fn check_for_similar_images(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn check_for_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -158,7 +158,7 @@ impl SimilarImages { self.information.number_of_checked_folders += folders_to_check.len(); while !folders_to_check.is_empty() { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let current_folder = folders_to_check.pop().unwrap(); @@ -280,7 +280,7 @@ impl SimilarImages { let mut new_vector: Vec = Vec::new(); for (hash, vec_file_entry) in &self.image_hashes { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let vector_with_found_similar_hashes = self.bktree.find(hash, 3).collect::>(); diff --git a/czkawka_core/src/temporary.rs b/czkawka_core/src/temporary.rs index 84cefc6..71d0ad9 100644 --- a/czkawka_core/src/temporary.rs +++ b/czkawka_core/src/temporary.rs @@ -67,9 +67,9 @@ impl Temporary { } /// Finding temporary files, save results to internal struct variables - pub fn find_temporary_files(&mut self, rx: Option<&Receiver<()>>) { + pub fn find_temporary_files(&mut self, stop_receiver: Option<&Receiver<()>>) { self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); - if !self.check_files(rx) { + if !self.check_files(stop_receiver) { self.stopped_search = true; return; } @@ -111,7 +111,7 @@ impl Temporary { self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages); } - fn check_files(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -122,7 +122,7 @@ impl Temporary { self.information.number_of_checked_folders += folders_to_check.len(); while !folders_to_check.is_empty() { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } diff --git a/czkawka_core/src/zeroed.rs b/czkawka_core/src/zeroed.rs index 9860701..aca8a30 100644 --- a/czkawka_core/src/zeroed.rs +++ b/czkawka_core/src/zeroed.rs @@ -72,9 +72,9 @@ impl ZeroedFiles { } } - pub fn find_zeroed_files(&mut self, rx: Option<&Receiver<()>>) { + pub fn find_zeroed_files(&mut self, stop_receiver: Option<&Receiver<()>>) { self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); - if !self.check_files(rx) { + if !self.check_files(stop_receiver) { self.stopped_search = true; return; } @@ -129,7 +129,7 @@ impl ZeroedFiles { } /// Check files for files which have 0 - fn check_files(&mut self, rx: Option<&Receiver<()>>) -> bool { + fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -140,7 +140,7 @@ impl ZeroedFiles { self.information.number_of_checked_folders += folders_to_check.len(); while !folders_to_check.is_empty() { - if rx.is_some() && rx.unwrap().try_recv().is_ok() { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { return false; } let current_folder = folders_to_check.pop().unwrap(); diff --git a/czkawka_gui/src/connect_button_delete.rs b/czkawka_gui/src/connect_button_delete.rs index 6dc8952..19c9ce5 100644 --- a/czkawka_gui/src/connect_button_delete.rs +++ b/czkawka_gui/src/connect_button_delete.rs @@ -2,14 +2,15 @@ extern crate gtk; use crate::gui_data::GuiData; use crate::help_functions::*; use gtk::prelude::*; +use std::collections::BTreeMap; use std::fs; use std::fs::Metadata; pub fn connect_button_delete(gui_data: &GuiData) { + let gui_data = gui_data.clone(); let buttons_delete = gui_data.buttons_delete.clone(); let shared_confirmation_dialog_delete_dialog_showing_state = gui_data.shared_confirmation_dialog_delete_dialog_showing_state.clone(); let scrolled_window_duplicate_finder = gui_data.scrolled_window_duplicate_finder.clone(); - let text_view_errors = gui_data.text_view_errors.clone(); let notebook_main_children_names = gui_data.notebook_main_children_names.clone(); let notebook_main = gui_data.notebook_main.clone(); let window_main = gui_data.window_main.clone(); @@ -55,380 +56,372 @@ pub fn connect_button_delete(gui_data: &GuiData) { match notebook_main_children_names.get(notebook_main.get_current_page().unwrap() as usize).unwrap().as_str() { "notebook_main_duplicate_finder_label" => { - let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - 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 - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsDuplicates::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsDuplicates::Path as i32).get::().unwrap().unwrap(); - - match fs::remove_file(format!("{}/{}", path, name)) { - Ok(_) => { - list_store.remove(&list_store.get_iter(tree_path).unwrap()); - } - Err(_) => messages += format!("Failed to remove file {}/{} because file doesn't exists or you don't have permissions.\n", path, name).as_str(), - } - } - - text_view_errors.get_buffer().unwrap().set_text(messages.as_str()); - selection.unselect_all(); + tree_remove( + scrolled_window_duplicate_finder.clone(), + ColumnsDuplicates::Name as i32, + ColumnsDuplicates::Path as i32, + ColumnsDuplicates::Color as i32, + &gui_data, + false, + false, + ); } "scrolled_window_main_empty_folder_finder" => { - let tree_view = scrolled_window_main_empty_folder_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let selection = tree_view.get_selection(); + empty_folder_remover(scrolled_window_main_empty_folder_finder.clone(), ColumnsEmptyFolders::Name as i32, ColumnsEmptyFolders::Path as i32, &gui_data); + } + "scrolled_window_main_empty_files_finder" => { + basic_remove(scrolled_window_main_empty_files_finder.clone(), ColumnsEmptyFiles::Name as i32, ColumnsEmptyFiles::Path as i32, &gui_data); + } + "scrolled_window_main_temporary_files_finder" => { + basic_remove(scrolled_window_main_temporary_files_finder.clone(), ColumnsTemporaryFiles::Name as i32, ColumnsTemporaryFiles::Path as i32, &gui_data); + } + "notebook_big_main_file_finder" => { + basic_remove(scrolled_window_big_files_finder.clone(), ColumnsBigFiles::Name as i32, ColumnsBigFiles::Path as i32, &gui_data); + } + "notebook_main_similar_images_finder_label" => { + tree_remove( + scrolled_window_similar_images_finder.clone(), + ColumnsSimilarImages::Name as i32, + ColumnsSimilarImages::Path as i32, + ColumnsSimilarImages::Color as i32, + &gui_data, + true, + true, + ); + } + "notebook_main_zeroed_files_finder" => { + basic_remove(scrolled_window_zeroed_files_finder.clone(), ColumnsZeroedFiles::Name as i32, ColumnsZeroedFiles::Path as i32, &gui_data); + } + "notebook_main_same_music_finder" => { + tree_remove( + scrolled_window_same_music_finder.clone(), + ColumnsSameMusic::Name as i32, + ColumnsSameMusic::Path as i32, + ColumnsSameMusic::Color as i32, + &gui_data, + false, + false, + ); + } + e => panic!("Not existent {}", e), + } + }); +} - let (selection_rows, tree_model) = selection.get_selected_rows(); - if selection_rows.is_empty() { - return; +fn empty_folder_remover(scrolled_window: gtk::ScrolledWindow, column_file_name: i32, column_path: i32, gui_data: &GuiData) { + let text_view_errors = gui_data.text_view_errors.clone(); + + let tree_view = scrolled_window.get_children().get(0).unwrap().clone().downcast::().unwrap(); + 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 + for tree_path in selection_rows.iter().rev() { + let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), column_file_name).get::().unwrap().unwrap(); + let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), column_path).get::().unwrap().unwrap(); + + // We must check if folder is really empty or contains only other empty folders + let mut error_happened = false; + let mut folders_to_check: Vec = vec![format!("{}/{}", path, name)]; + let mut current_folder: String; + let mut next_folder: String; + 'dir: while !folders_to_check.is_empty() { + current_folder = folders_to_check.pop().unwrap(); + let read_dir = match fs::read_dir(¤t_folder) { + Ok(t) => t, + Err(_) => { + error_happened = true; + break 'dir; } - 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 - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsEmptyFolders::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsEmptyFolders::Path as i32).get::().unwrap().unwrap(); - - // We must check if folder is really empty or contains only other empty folders - let mut error_happened = false; - let mut folders_to_check: Vec = vec![format!("{}/{}", path, name)]; - let mut current_folder: String; - let mut next_folder: String; - 'dir: while !folders_to_check.is_empty() { - current_folder = folders_to_check.pop().unwrap(); - let read_dir = match fs::read_dir(¤t_folder) { + for entry in read_dir { + let entry_data = match entry { + Ok(t) => t, + Err(_) => { + error_happened = true; + break 'dir; + } + }; + let metadata: Metadata = match entry_data.metadata() { + Ok(t) => t, + Err(_) => { + error_happened = true; + break 'dir; + } + }; + if metadata.is_dir() { + next_folder = "".to_owned() + + ¤t_folder + + "/" + + match &entry_data.file_name().into_string() { Ok(t) => t, Err(_) => { error_happened = true; break 'dir; } }; - - for entry in read_dir { - let entry_data = match entry { - Ok(t) => t, - Err(_) => { - error_happened = true; - break 'dir; - } - }; - let metadata: Metadata = match entry_data.metadata() { - Ok(t) => t, - Err(_) => { - error_happened = true; - break 'dir; - } - }; - if metadata.is_dir() { - next_folder = "".to_owned() - + ¤t_folder - + "/" - + match &entry_data.file_name().into_string() { - Ok(t) => t, - Err(_) => { - error_happened = true; - break 'dir; - } - }; - folders_to_check.push(next_folder.clone()); - } else { - error_happened = true; - } - } - } - - if !error_happened { - match fs::remove_dir_all(format!("{}/{}", path, name)) { - Ok(_) => { - list_store.remove(&list_store.get_iter(tree_path).unwrap()); - } - Err(_) => error_happened = true, - } - } - if error_happened { - messages += format!("Failed to remove folder {}/{} because folder doesn't exists, you don't have permissions or isn't empty.\n", path, name).as_str() - } + folders_to_check.push(next_folder.clone()); + } else { + error_happened = true; } - - text_view_errors.get_buffer().unwrap().set_text(messages.as_str()); - selection.unselect_all(); } - "scrolled_window_main_empty_files_finder" => { - let tree_view = scrolled_window_main_empty_files_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - 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 - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsEmptyFiles::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsEmptyFiles::Path as i32).get::().unwrap().unwrap(); - - match fs::remove_file(format!("{}/{}", path, name)) { - Ok(_) => { - list_store.remove(&list_store.get_iter(tree_path).unwrap()); - } - Err(_) => messages += format!("Failed to remove file {}/{} because file doesn't exists or you don't have permissions.\n", path, name).as_str(), - } - } - - text_view_errors.get_buffer().unwrap().set_text(messages.as_str()); - selection.unselect_all(); - } - "scrolled_window_main_temporary_files_finder" => { - let tree_view = scrolled_window_main_temporary_files_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - 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 - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsTemporaryFiles::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsTemporaryFiles::Path as i32).get::().unwrap().unwrap(); - - match fs::remove_file(format!("{}/{}", path, name)) { - Ok(_) => { - list_store.remove(&list_store.get_iter(tree_path).unwrap()); - } - Err(_) => messages += format!("Failed to remove file {}/{} because file doesn't exists or you don't have permissions.\n", path, name).as_str(), - } - } - - text_view_errors.get_buffer().unwrap().set_text(messages.as_str()); - selection.unselect_all(); - } - "notebook_big_main_file_finder" => { - let tree_view = scrolled_window_big_files_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - 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 - 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(); - - match fs::remove_file(format!("{}/{}", path, name)) { - Ok(_) => { - list_store.remove(&list_store.get_iter(tree_path).unwrap()); - } - Err(_) => messages += format!("Failed to remove file {}/{} because file doesn't exists or you don't have permissions.\n", path, name).as_str(), - } - } - - text_view_errors.get_buffer().unwrap().set_text(messages.as_str()); - selection.unselect_all(); - } - "notebook_main_similar_images_finder_label" => { - let tree_view = scrolled_window_similar_images_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - 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(); - - 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(), 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 = match list_store.get_iter_first() { - Some(t) => t, - None => break, - }; - 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; - } - } - } - - 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; - } - - 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(); - } - "notebook_main_zeroed_files_finder" => { - let tree_view = scrolled_window_zeroed_files_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - 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 - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsZeroedFiles::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsZeroedFiles::Path as i32).get::().unwrap().unwrap(); - - match fs::remove_file(format!("{}/{}", path, name)) { - Ok(_) => { - list_store.remove(&list_store.get_iter(tree_path).unwrap()); - } - Err(_) => messages += format!("Failed to remove file {}/{} because file doesn't exists or you don't have permissions.\n", path, name).as_str(), - } - } - - text_view_errors.get_buffer().unwrap().set_text(messages.as_str()); - selection.unselect_all(); - } - "notebook_main_same_music_finder" => { - let tree_view = scrolled_window_same_music_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - 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 - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSameMusic::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSameMusic::Path as i32).get::().unwrap().unwrap(); - - match fs::remove_file(format!("{}/{}", path, name)) { - Ok(_) => { - list_store.remove(&list_store.get_iter(tree_path).unwrap()); - } - Err(_) => messages += format!("Failed to remove file {}/{} because file doesn't exists or you don't have permissions.\n", path, name).as_str(), - } - } - - text_view_errors.get_buffer().unwrap().set_text(messages.as_str()); - selection.unselect_all(); - } - e => panic!("Not existent {}", e), } - }); + + if !error_happened { + match fs::remove_dir_all(format!("{}/{}", path, name)) { + Ok(_) => { + list_store.remove(&list_store.get_iter(tree_path).unwrap()); + } + Err(_) => error_happened = true, + } + } + if error_happened { + messages += format!("Failed to remove folder {}/{} because folder doesn't exists, you don't have permissions or isn't empty.\n", path, name).as_str() + } + } + + text_view_errors.get_buffer().unwrap().set_text(messages.as_str()); + selection.unselect_all(); +} + +fn basic_remove(scrolled_window: gtk::ScrolledWindow, column_file_name: i32, column_path: i32, gui_data: &GuiData) { + let text_view_errors = gui_data.text_view_errors.clone(); + + let tree_view = scrolled_window.get_children().get(0).unwrap().clone().downcast::().unwrap(); + 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 + for tree_path in selection_rows.iter().rev() { + let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), column_file_name).get::().unwrap().unwrap(); + let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), column_path).get::().unwrap().unwrap(); + + match fs::remove_file(format!("{}/{}", path, name)) { + Ok(_) => { + list_store.remove(&list_store.get_iter(tree_path).unwrap()); + } + Err(_) => messages += format!("Failed to remove file {}/{} because file doesn't exists or you don't have permissions.\n", path, name).as_str(), + } + } + + text_view_errors.get_buffer().unwrap().set_text(messages.as_str()); + selection.unselect_all(); +} // TODO, will replace simple remove of things + +// Remove all ocurences - remove every element which have same path and name as even non selected ones +// +fn tree_remove(scrolled_window: gtk::ScrolledWindow, column_file_name: i32, column_path: i32, column_color: i32, gui_data: &GuiData, remove_all_occurences: bool, header_can_have_one_child: bool) { + let text_view_errors = gui_data.text_view_errors.clone(); + + let tree_view = scrolled_window.get_children().get(0).unwrap().clone().downcast::().unwrap(); + 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(); + + let mut vec_path_to_delete: Vec<(String, String)> = Vec::new(); + let mut map_with_path_to_delete: BTreeMap> = Default::default(); // BTreeMap> + + // Save to variable paths of files, and remove it when not removing all ocureences. + for tree_path in selection_rows.iter().rev() { + let file_name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), column_file_name).get::().unwrap().unwrap(); + let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), column_path).get::().unwrap().unwrap(); + + if !remove_all_occurences { + list_store.remove(&list_store.get_iter(tree_path).unwrap()); + } + map_with_path_to_delete.entry(path.clone()).or_insert_with(Vec::new); + map_with_path_to_delete.get_mut(path.as_str()).unwrap().push(file_name); + // vec_path_to_delete.push((path, file_name)); + } + + // Delete duplicated entries, and remove real files + for (path, mut vec_file_name) in map_with_path_to_delete { + vec_file_name.sort(); + vec_file_name.dedup(); + for file_name in vec_file_name { + if fs::remove_file(format!("{}/{}", path.clone(), file_name.clone())).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, file_name + ) + .as_str() + } + vec_path_to_delete.push((path.clone(), file_name.clone())); + } + } + + if remove_all_occurences { + // 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 = match list_store.get_iter_first() { + Some(t) => t, + None => break, + }; + 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, column_color).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, column_path).get::().unwrap().unwrap(); + if path == path_to_delete.0 { + let name = tree_model.get_value(&iter, column_file_name).get::().unwrap().unwrap(); + if name == path_to_delete.1 { + let color = tree_model.get_value(&iter, column_color).get::().unwrap().unwrap(); + if color == HEADER_ROW_COLOR { + take_child_mode = true; + } + prepared_for_delete = true; + } + } + } + + 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()); + } + } + } + + // Remove only child from header + if !header_can_have_one_child { + if let Some(first_iter) = list_store.get_iter_first() { + let mut vec_tree_path_to_delete: Vec = Vec::new(); + let mut current_iter = first_iter; + if tree_model.get_value(¤t_iter, column_color).get::().unwrap().unwrap() != HEADER_ROW_COLOR { + panic!(); // First element should be header + }; + + let mut next_iter; + let mut next_next_iter; + 'main: loop { + if tree_model.get_value(¤t_iter, column_color).get::().unwrap().unwrap() != HEADER_ROW_COLOR { + panic!(); // First element should be header + }; + + next_iter = current_iter.clone(); + if !list_store.iter_next(&next_iter) { + // There is only single header + vec_tree_path_to_delete.push(list_store.get_path(¤t_iter).unwrap()); + break 'main; + } + + if tree_model.get_value(&next_iter, column_color).get::().unwrap().unwrap() == HEADER_ROW_COLOR { + // Only two headers + vec_tree_path_to_delete.push(list_store.get_path(¤t_iter).unwrap()); + current_iter = next_iter.clone(); + continue 'main; + } + + next_next_iter = next_iter.clone(); + if !list_store.iter_next(&next_next_iter) { + // There is only one child or two headers + vec_tree_path_to_delete.push(list_store.get_path(¤t_iter).unwrap()); + vec_tree_path_to_delete.push(list_store.get_path(&next_iter).unwrap()); + break 'main; + } + + if tree_model.get_value(&next_next_iter, column_color).get::().unwrap().unwrap() == HEADER_ROW_COLOR { + // Only one child or two headers - but still are later files + vec_tree_path_to_delete.push(list_store.get_path(¤t_iter).unwrap()); + vec_tree_path_to_delete.push(list_store.get_path(&next_iter).unwrap()); + current_iter = next_next_iter.clone(); + continue 'main; + } + + loop { + if !list_store.iter_next(&next_next_iter) { + break 'main; + } + if tree_model.get_value(&next_next_iter, column_color).get::().unwrap().unwrap() == HEADER_ROW_COLOR { + current_iter = next_next_iter.clone(); + continue 'main; + } + } + } + for tree_path in vec_tree_path_to_delete.iter().rev() { + list_store.remove(&list_store.get_iter(&tree_path).unwrap()); + } + } + } else { + // Remove header which doesn't have children + 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, column_color).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; + } + + current_iter = next_iter.clone(); + if !list_store.iter_next(&next_iter) { + break; + } + } + for tree_path in vec_tree_path_to_delete.iter().rev() { + list_store.remove(&list_store.get_iter(&tree_path).unwrap()); + } + } + } + + // 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(); } -// fn basic_remove(tree_view: gtk::TreeView, column_name: i32, column_path: i32) {} // TODO, will replace simple remove of things diff --git a/czkawka_gui/src/connect_button_save.rs b/czkawka_gui/src/connect_button_save.rs index d22a81b..4640bec 100644 --- a/czkawka_gui/src/connect_button_save.rs +++ b/czkawka_gui/src/connect_button_save.rs @@ -4,10 +4,8 @@ use czkawka_core::common_traits::SaveResults; use gtk::prelude::*; pub fn connect_button_save(gui_data: &GuiData) { - let shared_buttons = gui_data.shared_buttons.clone(); - let buttons_save_clone = gui_data.buttons_save.clone(); + let gui_data = gui_data.clone(); let buttons_save = gui_data.buttons_save.clone(); - let entry_info = gui_data.entry_info.clone(); let shared_duplication_state = gui_data.shared_duplication_state.clone(); let shared_empty_folders_state = gui_data.shared_empty_folders_state.clone(); let shared_big_files_state = gui_data.shared_big_files_state.clone(); @@ -18,111 +16,75 @@ pub fn connect_button_save(gui_data: &GuiData) { let shared_zeroed_files_state = gui_data.shared_zeroed_files_state.clone(); let notebook_main_children_names = gui_data.notebook_main_children_names.clone(); let notebook_main = gui_data.notebook_main.clone(); - buttons_save_clone.connect_clicked(move |_| match notebook_main_children_names.get(notebook_main.get_current_page().unwrap() as usize).unwrap().as_str() { + buttons_save.connect_clicked(move |_| match notebook_main_children_names.get(notebook_main.get_current_page().unwrap() as usize).unwrap().as_str() { "notebook_main_duplicate_finder_label" => { let file_name = "results_duplicates.txt"; - let mut df = shared_duplication_state.borrow_mut(); - df.save_results_to_file(file_name); + shared_duplication_state.borrow_mut().save_results_to_file(file_name); - entry_info.set_text(format!("Saved results to file {}", file_name).as_str()); - // Set state - { - buttons_save.hide(); - *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("save").unwrap() = false; - } + post_save_things(file_name, "duplicate", &gui_data); } "scrolled_window_main_empty_folder_finder" => { let file_name = "results_empty_folder.txt"; - let mut ef = shared_empty_folders_state.borrow_mut(); - ef.save_results_to_file(file_name); + shared_empty_folders_state.borrow_mut().save_results_to_file(file_name); - entry_info.set_text(format!("Saved results to file {}", file_name).as_str()); - // Set state - { - buttons_save.hide(); - *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap().get_mut("save").unwrap() = false; - } + post_save_things(file_name, "empty_folder", &gui_data); } "scrolled_window_main_empty_files_finder" => { let file_name = "results_empty_files.txt"; - let mut df = shared_empty_files_state.borrow_mut(); - df.save_results_to_file(file_name); + shared_empty_files_state.borrow_mut().save_results_to_file(file_name); - entry_info.set_text(format!("Saved results to file {}", file_name).as_str()); - // Set state - { - buttons_save.hide(); - *shared_buttons.borrow_mut().get_mut("empty_file").unwrap().get_mut("save").unwrap() = false; - } + post_save_things(file_name, "empty_file", &gui_data); } "scrolled_window_main_temporary_files_finder" => { let file_name = "results_temporary_files.txt"; - let mut df = shared_temporary_files_state.borrow_mut(); - df.save_results_to_file(file_name); + shared_temporary_files_state.borrow_mut().save_results_to_file(file_name); - entry_info.set_text(format!("Saved results to file {}", file_name).as_str()); - // Set state - { - buttons_save.hide(); - *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap().get_mut("save").unwrap() = false; - } + post_save_things(file_name, "temporary_file", &gui_data); } "notebook_big_main_file_finder" => { let file_name = "results_big_files.txt"; - let mut df = shared_big_files_state.borrow_mut(); - df.save_results_to_file(file_name); + shared_big_files_state.borrow_mut().save_results_to_file(file_name); - entry_info.set_text(format!("Saved results to file {}", file_name).as_str()); - // Set state - { - buttons_save.hide(); - *shared_buttons.borrow_mut().get_mut("big_file").unwrap().get_mut("save").unwrap() = false; - } + post_save_things(file_name, "big_file", &gui_data); } "notebook_main_similar_images_finder_label" => { let file_name = "results_similar_images.txt"; - let mut df = shared_similar_images_state.borrow_mut(); - df.save_results_to_file(file_name); + shared_similar_images_state.borrow_mut().save_results_to_file(file_name); - entry_info.set_text(format!("Saved results to file {}", file_name).as_str()); - // Set state - { - buttons_save.hide(); - *shared_buttons.borrow_mut().get_mut("similar_images").unwrap().get_mut("save").unwrap() = false; - } + post_save_things(file_name, "similar_images", &gui_data); } "notebook_main_zeroed_files_finder" => { let file_name = "results_zeroed_files.txt"; - let mut zf = shared_zeroed_files_state.borrow_mut(); - zf.save_results_to_file(file_name); + shared_zeroed_files_state.borrow_mut().save_results_to_file(file_name); - entry_info.set_text(format!("Saved results to file {}", file_name).as_str()); - // Set state - { - buttons_save.hide(); - *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("save").unwrap() = false; - } + post_save_things(file_name, "zeroed_files", &gui_data); } "notebook_main_same_music_finder" => { let file_name = "results_same_music.txt"; - let mut mf = shared_same_music_state.borrow_mut(); - mf.save_results_to_file(file_name); + shared_same_music_state.borrow_mut().save_results_to_file(file_name); - entry_info.set_text(format!("Saved results to file {}", file_name).as_str()); - // Set state - { - buttons_save.hide(); - *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("save").unwrap() = false; - } + post_save_things(file_name, "same_music", &gui_data); } e => panic!("Not existent {}", e), }); } +fn post_save_things(file_name: &str, type_of_tab: &str, gui_data: &GuiData) { + let entry_info = gui_data.entry_info.clone(); + let buttons_save = gui_data.buttons_save.clone(); + let shared_buttons = gui_data.shared_buttons.clone(); + + entry_info.set_text(format!("Saved results to file {}", file_name).as_str()); + // Set state + { + buttons_save.hide(); + *shared_buttons.borrow_mut().get_mut(type_of_tab).unwrap().get_mut("save").unwrap() = false; + } +} diff --git a/czkawka_gui/src/connect_button_search.rs b/czkawka_gui/src/connect_button_search.rs index a33b7e0..4638005 100644 --- a/czkawka_gui/src/connect_button_search.rs +++ b/czkawka_gui/src/connect_button_search.rs @@ -33,7 +33,7 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender) { let radio_button_hashmb = gui_data.radio_button_hashmb.clone(); let radio_button_hash = gui_data.radio_button_hash.clone(); let entry_duplicate_minimal_size = gui_data.entry_duplicate_minimal_size.clone(); - let rx = gui_data.rx.clone(); + let stop_receiver = gui_data.stop_receiver.clone(); let entry_big_files_number = gui_data.entry_big_files_number.clone(); let entry_similar_images_minimal_size = gui_data.entry_similar_images_minimal_size.clone(); let check_button_music_title: gtk::CheckButton = gui_data.check_button_music_title.clone(); @@ -75,10 +75,9 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender) { Ok(t) => t, Err(_) => 1024, // By default }; - let delete_method = duplicate::DeleteMethod::None; let sender = sender.clone(); - let receiver_stop = rx.clone(); + let receiver_stop = stop_receiver.clone(); // Find duplicates thread::spawn(move || { let mut df = DuplicateFinder::new(); @@ -89,26 +88,27 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender) { df.set_allowed_extensions(allowed_extensions); df.set_minimal_file_size(minimal_file_size); df.set_check_method(check_method); - df.set_delete_method(delete_method); df.find_duplicates(Option::from(&receiver_stop)); let _ = sender.send(Message::Duplicates(df)); }); } "scrolled_window_main_empty_folder_finder" => { let sender = sender.clone(); - let receiver_stop = rx.clone(); + let receiver_stop = stop_receiver.clone(); // Find empty folders thread::spawn(move || { let mut ef = EmptyFolder::new(); ef.set_included_directory(included_directories); + ef.set_excluded_directory(excluded_directories); + ef.set_excluded_items(excluded_items); ef.find_empty_folders(Option::from(&receiver_stop)); let _ = sender.send(Message::EmptyFolders(ef)); }); } "scrolled_window_main_empty_files_finder" => { let sender = sender.clone(); - let receiver_stop = rx.clone(); + let receiver_stop = stop_receiver.clone(); // Find empty files thread::spawn(move || { @@ -125,7 +125,7 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender) { } "scrolled_window_main_temporary_files_finder" => { let sender = sender.clone(); - let receiver_stop = rx.clone(); + let receiver_stop = stop_receiver.clone(); // Find temporary files thread::spawn(move || { @@ -146,7 +146,7 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender) { }; let sender = sender.clone(); - let receiver_stop = rx.clone(); + let receiver_stop = stop_receiver.clone(); // Find big files thread::spawn(move || { @@ -164,7 +164,7 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender) { "notebook_main_similar_images_finder_label" => { let sender = sender.clone(); - let receiver_stop = rx.clone(); + let receiver_stop = stop_receiver.clone(); let minimal_file_size = match entry_similar_images_minimal_size.get_text().as_str().parse::() { Ok(t) => t, @@ -186,7 +186,7 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender) { } "notebook_main_zeroed_files_finder" => { let sender = sender.clone(); - let receiver_stop = rx.clone(); + let receiver_stop = stop_receiver.clone(); // Find zeroed files thread::spawn(move || { @@ -226,7 +226,7 @@ pub fn connect_button_search(gui_data: &GuiData, sender: Sender) { if music_similarity != MusicSimilarity::NONE { let sender = sender.clone(); - let receiver_stop = rx.clone(); + let receiver_stop = stop_receiver.clone(); // Find temporary files thread::spawn(move || { diff --git a/czkawka_gui/src/connect_button_select.rs b/czkawka_gui/src/connect_button_select.rs index 97697a6..c4b68e5 100644 --- a/czkawka_gui/src/connect_button_select.rs +++ b/czkawka_gui/src/connect_button_select.rs @@ -14,6 +14,7 @@ pub fn connect_button_select(gui_data: &GuiData) { popover_select.set_relative_to(Some(&buttons_select)); popover_select.popup(); } + // TODO add for all basic selection popup e => panic!("Not existent {}", e), }); } diff --git a/czkawka_gui/src/connect_button_stop.rs b/czkawka_gui/src/connect_button_stop.rs index b76681a..ac62f7a 100644 --- a/czkawka_gui/src/connect_button_stop.rs +++ b/czkawka_gui/src/connect_button_stop.rs @@ -4,8 +4,8 @@ use gtk::prelude::*; pub fn connect_button_stop(gui_data: &GuiData) { let buttons_stop = gui_data.buttons_stop.clone(); - let sx = gui_data.sx.clone(); + let stop_sender = gui_data.stop_sender.clone(); buttons_stop.connect_clicked(move |_| { - sx.send(()).unwrap(); + stop_sender.send(()).unwrap(); }); } diff --git a/czkawka_gui/src/connect_notebook_tabs.rs b/czkawka_gui/src/connect_notebook_tabs.rs index cde3ce2..c2be347 100644 --- a/czkawka_gui/src/connect_notebook_tabs.rs +++ b/czkawka_gui/src/connect_notebook_tabs.rs @@ -18,12 +18,8 @@ pub fn connect_notebook_tabs(gui_data: &GuiData) { notebook_main_clone.connect_switch_page(move |_, _, number| { let page: &str; match notebook_main_children_names.get(number as usize).unwrap().as_str() { - "notebook_main_duplicate_finder_label" => { - page = "duplicate"; - } - "scrolled_window_main_empty_folder_finder" => { - page = "empty_folder"; - } + "notebook_main_duplicate_finder_label" => page = "duplicate", + "scrolled_window_main_empty_folder_finder" => page = "empty_folder", "scrolled_window_main_empty_files_finder" => page = "empty_file", "scrolled_window_main_temporary_files_finder" => page = "temporary_file", "notebook_big_main_file_finder" => page = "big_file", diff --git a/czkawka_gui/src/double_click_opening.rs b/czkawka_gui/src/double_click_opening.rs index 822de11..585928f 100644 --- a/czkawka_gui/src/double_click_opening.rs +++ b/czkawka_gui/src/double_click_opening.rs @@ -1,136 +1,70 @@ use crate::help_functions::*; use gtk::prelude::*; -pub fn opening_double_click_function_similar_images(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit { - if event.get_event_type() == gdk::EventType::DoubleButtonPress { - let selection = tree_view.get_selection(); - let (selection_rows, tree_model) = selection.get_selected_rows(); - - for tree_path in selection_rows.iter().rev() { - 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 open::that(format!("{}/{}", path, name)).is_err() { - println!("Failed to open {}/{}", path, name); - } - } - } - gtk::Inhibit(false) -} - pub fn opening_double_click_function_duplicates(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit { if event.get_event_type() == gdk::EventType::DoubleButtonPress { - let selection = tree_view.get_selection(); - let (selection_rows, tree_model) = selection.get_selected_rows(); - - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsDuplicates::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsDuplicates::Path as i32).get::().unwrap().unwrap(); - - if open::that(format!("{}/{}", path, name)).is_err() { - println!("Failed to open {}/{}", path, name); - } - } + common_open_function(tree_view, ColumnsDuplicates::Name as i32, ColumnsDuplicates::Path as i32); } gtk::Inhibit(false) } pub fn opening_double_click_function_empty_folders(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit { if event.get_event_type() == gdk::EventType::DoubleButtonPress { - let selection = tree_view.get_selection(); - let (selection_rows, tree_model) = selection.get_selected_rows(); - - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsEmptyFolders::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsEmptyFolders::Path as i32).get::().unwrap().unwrap(); - - if open::that(format!("{}/{}", path, name)).is_err() { - println!("Failed to open {}/{}", path, name); - } - } + common_open_function(tree_view, ColumnsEmptyFolders::Name as i32, ColumnsEmptyFolders::Path as i32); } gtk::Inhibit(false) } pub fn opening_double_click_function_empty_files(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit { if event.get_event_type() == gdk::EventType::DoubleButtonPress { - let selection = tree_view.get_selection(); - let (selection_rows, tree_model) = selection.get_selected_rows(); - - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsEmptyFiles::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsEmptyFiles::Path as i32).get::().unwrap().unwrap(); - - if open::that(format!("{}/{}", path, name)).is_err() { - println!("Failed to open {}/{}", path, name); - } - } + common_open_function(tree_view, ColumnsEmptyFiles::Name as i32, ColumnsEmptyFiles::Path as i32); } gtk::Inhibit(false) } pub fn opening_double_click_function_temporary_files(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit { if event.get_event_type() == gdk::EventType::DoubleButtonPress { - let selection = tree_view.get_selection(); - let (selection_rows, tree_model) = selection.get_selected_rows(); - - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsTemporaryFiles::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsTemporaryFiles::Path as i32).get::().unwrap().unwrap(); - - if open::that(format!("{}/{}", path, name)).is_err() { - println!("Failed to open {}/{}", path, name); - } - } + common_open_function(tree_view, ColumnsTemporaryFiles::Name as i32, ColumnsTemporaryFiles::Path as i32); } gtk::Inhibit(false) } pub fn opening_double_click_function_big_files(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit { if event.get_event_type() == gdk::EventType::DoubleButtonPress { - let selection = tree_view.get_selection(); - let (selection_rows, tree_model) = selection.get_selected_rows(); - - 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(); - - if open::that(format!("{}/{}", path, name)).is_err() { - println!("Failed to open {}/{}", path, name); - } - } + common_open_function(tree_view, ColumnsBigFiles::Name as i32, ColumnsBigFiles::Path as i32); } gtk::Inhibit(false) } pub fn opening_double_click_function_zeroed_files(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit { if event.get_event_type() == gdk::EventType::DoubleButtonPress { - let selection = tree_view.get_selection(); - let (selection_rows, tree_model) = selection.get_selected_rows(); - - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsZeroedFiles::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsZeroedFiles::Path as i32).get::().unwrap().unwrap(); - - if open::that(format!("{}/{}", path, name)).is_err() { - println!("Failed to open {}/{}", path, name); - } - } + common_open_function(tree_view, ColumnsZeroedFiles::Name as i32, ColumnsZeroedFiles::Path as i32); } gtk::Inhibit(false) } pub fn opening_double_click_function_same_music(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit { if event.get_event_type() == gdk::EventType::DoubleButtonPress { - let selection = tree_view.get_selection(); - let (selection_rows, tree_model) = selection.get_selected_rows(); - - for tree_path in selection_rows.iter().rev() { - let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSameMusic::Name as i32).get::().unwrap().unwrap(); - let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsSameMusic::Path as i32).get::().unwrap().unwrap(); - - if open::that(format!("{}/{}", path, name)).is_err() { - println!("Failed to open {}/{}", path, name); - } - } + common_open_function(tree_view, ColumnsSameMusic::Name as i32, ColumnsSameMusic::Path as i32); } gtk::Inhibit(false) } + +pub fn opening_double_click_function_similar_images(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit { + if event.get_event_type() == gdk::EventType::DoubleButtonPress { + common_open_function(tree_view, ColumnsSimilarImages::Name as i32, ColumnsSimilarImages::Path as i32); + } + gtk::Inhibit(false) +} + +pub fn common_open_function(tree_view: >k::TreeView, column_name: i32, column_path: i32) { + let selection = tree_view.get_selection(); + let (selection_rows, tree_model) = selection.get_selected_rows(); + + for tree_path in selection_rows.iter().rev() { + let name = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), column_name).get::().unwrap().unwrap(); + let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), column_path).get::().unwrap().unwrap(); + + if open::that(format!("{}/{}", path, name)).is_err() { + println!("Failed to open {}/{}", path, name); + } + } +} diff --git a/czkawka_gui/src/gui_data.rs b/czkawka_gui/src/gui_data.rs index b4814fc..8d2be38 100644 --- a/czkawka_gui/src/gui_data.rs +++ b/czkawka_gui/src/gui_data.rs @@ -129,8 +129,8 @@ pub struct GuiData { //// Threads // Used for sending stop signal to thread - pub sx: crossbeam_channel::Sender<()>, - pub rx: crossbeam_channel::Receiver<()>, + pub stop_sender: crossbeam_channel::Sender<()>, + pub stop_receiver: crossbeam_channel::Receiver<()>, } impl GuiData { @@ -310,7 +310,7 @@ impl GuiData { // Types of messages to send to main thread where gui can be draw. // Used for sending stop signal to thread - let (sx, rx): (crossbeam_channel::Sender<()>, crossbeam_channel::Receiver<()>) = unbounded(); + let (stop_sender, stop_receiver): (crossbeam_channel::Sender<()>, crossbeam_channel::Receiver<()>) = unbounded(); Self { glade_src, @@ -383,8 +383,8 @@ impl GuiData { scrolled_window_same_music_finder, scrolled_window_included_directories, scrolled_window_excluded_directories, - sx, - rx, + stop_sender, + stop_receiver, } } } diff --git a/czkawka_gui/src/startup_configuration.rs b/czkawka_gui/src/startup_configuration.rs index b28a0d2..bd0fb03 100644 --- a/czkawka_gui/src/startup_configuration.rs +++ b/czkawka_gui/src/startup_configuration.rs @@ -251,9 +251,11 @@ pub fn startup_configuration(gui_data: &GuiData) { let col_indices = [0, 1]; - for i in ["/proc", "/dev", "/sys", "/run", "/snap"].iter() { - let values: [&dyn ToValue; 2] = [&i, &(MAIN_ROW_COLOR.to_string())]; - list_store.set(&list_store.append(), &col_indices, &values); + if cfg!(target_family = "unix") { + for i in ["/proc", "/dev", "/sys", "/run", "/snap"].iter() { + let values: [&dyn ToValue; 2] = [&i, &(MAIN_ROW_COLOR.to_string())]; + list_store.set(&list_store.append(), &col_indices, &values); + } } scrolled_window_excluded_directories.add(&tree_view_excluded_directory);