diff --git a/czkawka_gui/src/connect_button_delete.rs b/czkawka_gui/src/connect_button_delete.rs new file mode 100644 index 0000000..15288f1 --- /dev/null +++ b/czkawka_gui/src/connect_button_delete.rs @@ -0,0 +1,402 @@ +extern crate gtk; +use crate::gui_data::GuiData; +use crate::help_functions::*; +use gtk::prelude::*; +use std::fs; +use std::fs::Metadata; + +pub fn connect_button_delete(gui_data: &GuiData) { + 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(); + let scrolled_window_main_empty_folder_finder = gui_data.scrolled_window_main_empty_folder_finder.clone(); + let scrolled_window_big_files_finder = gui_data.scrolled_window_big_files_finder.clone(); + let scrolled_window_main_empty_files_finder = gui_data.scrolled_window_main_empty_files_finder.clone(); + let scrolled_window_main_temporary_files_finder = gui_data.scrolled_window_main_temporary_files_finder.clone(); + let scrolled_window_similar_images_finder = gui_data.scrolled_window_similar_images_finder.clone(); + let scrolled_window_zeroed_files_finder = gui_data.scrolled_window_zeroed_files_finder.clone(); + + buttons_delete.connect_clicked(move |_| { + if *shared_confirmation_dialog_delete_dialog_showing_state.borrow_mut() { + let confirmation_dialog_delete = gtk::Dialog::with_buttons( + Option::from("Delete confirmation"), + Option::from(&window_main), + gtk::DialogFlags::MODAL, + &[("Ok", gtk::ResponseType::Ok), ("Close", gtk::ResponseType::Cancel)], + ); + let label: gtk::Label = gtk::Label::new(Some("Are you sure that you want to delete files?")); + let check_button: gtk::CheckButton = gtk::CheckButton::with_label("Ask in future"); + check_button.set_active(true); + + for widgets in confirmation_dialog_delete.get_children() { + // By default GtkBox is child of dialog, so we can easily add other things to it + widgets.clone().downcast::().unwrap().add(&label); + widgets.downcast::().unwrap().add(&check_button); + } + + confirmation_dialog_delete.show_all(); + + let response_type = confirmation_dialog_delete.run(); + if response_type == gtk::ResponseType::Ok { + if !check_button.get_active() { + *shared_confirmation_dialog_delete_dialog_showing_state.borrow_mut() = false; + } + } else { + confirmation_dialog_delete.close(); + return; + } + confirmation_dialog_delete.close(); + } + + 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(); + } + "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(); + + 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(), 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) { + 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() + } + } + + 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(); + } + e => panic!("Not existent {}", e), + } + }); +} diff --git a/czkawka_gui/src/connect_button_save.rs b/czkawka_gui/src/connect_button_save.rs new file mode 100644 index 0000000..c6c21d5 --- /dev/null +++ b/czkawka_gui/src/connect_button_save.rs @@ -0,0 +1,114 @@ +extern crate gtk; +use crate::gui_data::GuiData; +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 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(); + let shared_temporary_files_state = gui_data.shared_temporary_files_state.clone(); + let shared_empty_files_state = gui_data.shared_empty_files_state.clone(); + let shared_similar_images_state = gui_data.shared_similar_images_state.clone(); + 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() { + "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); + + 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; + } + } + "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); + + 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; + } + } + "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); + + 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; + } + } + "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); + + 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; + } + } + "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); + + 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; + } + } + "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); + + 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; + } + } + "notebook_main_zeroed_files_finder_label" => { + let file_name = "results_zeroed_files.txt"; + + let mut zf = shared_zeroed_files_state.borrow_mut(); + zf.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; + } + } + e => panic!("Not existent {}", e), + }); +} diff --git a/czkawka_gui/src/connect_button_search.rs b/czkawka_gui/src/connect_button_search.rs new file mode 100644 index 0000000..40c6835 --- /dev/null +++ b/czkawka_gui/src/connect_button_search.rs @@ -0,0 +1,199 @@ +use czkawka_core::*; + +extern crate gtk; +use crate::gui_data::GuiData; +use crate::help_functions::*; +use czkawka_core::big_file::BigFile; +use czkawka_core::duplicate::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 czkawka_core::zeroed::ZeroedFiles; +use glib::Sender; +use gtk::prelude::*; +use std::thread; + +pub fn connect_button_search(gui_data: &GuiData, sender: Sender) { + let entry_info = gui_data.entry_info.clone(); + let notebook_main_children_names = gui_data.notebook_main_children_names.clone(); + let notebook_main = gui_data.notebook_main.clone(); + let scrolled_window_included_directories = gui_data.scrolled_window_included_directories.clone(); + let scrolled_window_excluded_directories = gui_data.scrolled_window_excluded_directories.clone(); + let buttons_search_clone = gui_data.buttons_search.clone(); + let buttons_array = gui_data.buttons_array.clone(); + let check_button_recursive = gui_data.check_button_recursive.clone(); + let entry_excluded_items = gui_data.entry_excluded_items.clone(); + let entry_allowed_extensions = gui_data.entry_allowed_extensions.clone(); + let buttons_names = gui_data.buttons_names.clone(); + let radio_button_name = gui_data.radio_button_name.clone(); + let radio_button_size = gui_data.radio_button_size.clone(); + 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 sender = gui_data.sender.clone(); + let rx = gui_data.rx.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(); + buttons_search_clone.connect_clicked(move |_| { + let included_directories = get_string_from_list_store(&scrolled_window_included_directories); + let excluded_directories = get_string_from_list_store(&scrolled_window_excluded_directories); + let recursive_search = check_button_recursive.get_active(); + let excluded_items = entry_excluded_items.get_text().as_str().to_string(); + let allowed_extensions = entry_allowed_extensions.get_text().as_str().to_string(); + + hide_all_buttons_except("stop", &buttons_array, &buttons_names); + + // Disable main notebook from any iteraction until search will end + notebook_main.set_sensitive(false); + + entry_info.set_text("Searching data, it may take a while, please wait..."); + + match notebook_main_children_names.get(notebook_main.get_current_page().unwrap() as usize).unwrap().as_str() { + "notebook_main_duplicate_finder_label" => { + let check_method; + if radio_button_name.get_active() { + check_method = duplicate::CheckingMethod::Name; + } else if radio_button_size.get_active() { + check_method = duplicate::CheckingMethod::Size; + } else if radio_button_hashmb.get_active() { + check_method = duplicate::CheckingMethod::HashMB; + } else if radio_button_hash.get_active() { + check_method = duplicate::CheckingMethod::Hash; + } else { + panic!("No radio button is pressed"); + } + let minimal_file_size = match entry_duplicate_minimal_size.get_text().as_str().parse::() { + Ok(t) => t, + Err(_) => 1024, // By default + }; + let delete_method = duplicate::DeleteMethod::None; + + let sender = sender.clone(); + let receiver_stop = rx.clone(); + // Find duplicates + thread::spawn(move || { + let mut df = DuplicateFinder::new(); + df.set_included_directory(included_directories); + df.set_excluded_directory(excluded_directories); + df.set_recursive_search(recursive_search); + df.set_excluded_items(excluded_items); + 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)); //&rc_stop_signal.borrow().1); + let _ = sender.send(Message::Duplicates(df)); + }); + } + "scrolled_window_main_empty_folder_finder" => { + let sender = sender.clone(); + let receiver_stop = rx.clone(); + + // Find empty folders + thread::spawn(move || { + let mut ef = EmptyFolder::new(); + ef.set_included_directory(included_directories); + ef.set_delete_folder(false); + 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(); + + // Find empty files + thread::spawn(move || { + let mut vf = EmptyFiles::new(); + + vf.set_included_directory(included_directories); + vf.set_excluded_directory(excluded_directories); + vf.set_recursive_search(recursive_search); + vf.set_excluded_items(excluded_items); + vf.set_allowed_extensions(allowed_extensions); + vf.find_empty_files(Option::from(&receiver_stop)); + let _ = sender.send(Message::EmptyFiles(vf)); + }); + } + "scrolled_window_main_temporary_files_finder" => { + let sender = sender.clone(); + let receiver_stop = rx.clone(); + + // Find temporary files + thread::spawn(move || { + let mut tf = Temporary::new(); + + tf.set_included_directory(included_directories); + tf.set_excluded_directory(excluded_directories); + tf.set_recursive_search(recursive_search); + tf.set_excluded_items(excluded_items); + tf.find_temporary_files(Option::from(&receiver_stop)); + let _ = sender.send(Message::Temporary(tf)); + }); + } + "notebook_big_main_file_finder" => { + let numbers_of_files_to_check = match entry_big_files_number.get_text().as_str().parse::() { + Ok(t) => t, + Err(_) => 50, // By default + }; + + let sender = sender.clone(); + let receiver_stop = rx.clone(); + + // Find big files + thread::spawn(move || { + let mut bf = BigFile::new(); + + bf.set_included_directory(included_directories); + bf.set_excluded_directory(excluded_directories); + bf.set_recursive_search(recursive_search); + bf.set_excluded_items(excluded_items); + bf.set_number_of_files_to_check(numbers_of_files_to_check); + bf.find_big_files(Option::from(&receiver_stop)); + let _ = sender.send(Message::BigFiles(bf)); + }); + } + + "notebook_main_similar_images_finder_label" => { + let sender = sender.clone(); + let receiver_stop = rx.clone(); + + let minimal_file_size = match entry_similar_images_minimal_size.get_text().as_str().parse::() { + Ok(t) => t, + Err(_) => 1024 * 16, // By default + }; + + // Find similar images + thread::spawn(move || { + let mut sf = SimilarImages::new(); + + sf.set_included_directory(included_directories); + sf.set_excluded_directory(excluded_directories); + sf.set_recursive_search(recursive_search); + sf.set_excluded_items(excluded_items); + sf.set_minimal_file_size(minimal_file_size); + sf.find_similar_images(Option::from(&receiver_stop)); + let _ = sender.send(Message::SimilarImages(sf)); + }); + } + "notebook_main_zeroed_files_finder" => { + let sender = sender.clone(); + let receiver_stop = rx.clone(); + + // Find temporary files + thread::spawn(move || { + let mut zf = ZeroedFiles::new(); + + zf.set_included_directory(included_directories); + zf.set_excluded_directory(excluded_directories); + zf.set_recursive_search(recursive_search); + zf.set_excluded_items(excluded_items); + zf.find_zeroed_files(Option::from(&receiver_stop)); + let _ = sender.send(Message::ZeroedFiles(zf)); + }); + } + e => panic!("Not existent {}", e), + } + }); +} diff --git a/czkawka_gui/src/connect_button_select.rs b/czkawka_gui/src/connect_button_select.rs new file mode 100644 index 0000000..97697a6 --- /dev/null +++ b/czkawka_gui/src/connect_button_select.rs @@ -0,0 +1,19 @@ +extern crate gtk; +use crate::gui_data::GuiData; +use gtk::prelude::*; + +pub fn connect_button_select(gui_data: &GuiData) { + let notebook_main_children_names = gui_data.notebook_main_children_names.clone(); + let notebook_main = gui_data.notebook_main.clone(); + let buttons_select_clone = gui_data.buttons_select.clone(); + let popover_select = gui_data.popover_select.clone(); + let buttons_select = gui_data.buttons_select.clone(); + buttons_select_clone.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" => { + // Only popup popup + popover_select.set_relative_to(Some(&buttons_select)); + popover_select.popup(); + } + e => panic!("Not existent {}", e), + }); +} diff --git a/czkawka_gui/src/connect_button_stop.rs b/czkawka_gui/src/connect_button_stop.rs new file mode 100644 index 0000000..b76681a --- /dev/null +++ b/czkawka_gui/src/connect_button_stop.rs @@ -0,0 +1,11 @@ +extern crate gtk; +use crate::gui_data::GuiData; +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(); + buttons_stop.connect_clicked(move |_| { + sx.send(()).unwrap(); + }); +} diff --git a/czkawka_gui/src/connect_compute_results.rs b/czkawka_gui/src/connect_compute_results.rs new file mode 100644 index 0000000..3dbe6c1 --- /dev/null +++ b/czkawka_gui/src/connect_compute_results.rs @@ -0,0 +1,666 @@ +use humansize::{file_size_opts as options, FileSize}; + +extern crate gtk; +use crate::gui_data::GuiData; +use crate::help_functions::*; +use chrono::NaiveDateTime; +use czkawka_core::duplicate::CheckingMethod; +use glib::Receiver; +use gtk::prelude::*; + +pub fn connect_compute_results(gui_data: &GuiData, receiver: Receiver) { + let buttons_search = gui_data.buttons_search.clone(); + let buttons_stop = gui_data.buttons_stop.clone(); + let notebook_main = gui_data.notebook_main.clone(); + let entry_info = gui_data.entry_info.clone(); + let scrolled_window_main_empty_folder_finder = gui_data.scrolled_window_main_empty_folder_finder.clone(); + let scrolled_window_main_empty_files_finder = gui_data.scrolled_window_main_empty_files_finder.clone(); + let scrolled_window_duplicate_finder = gui_data.scrolled_window_duplicate_finder.clone(); + let scrolled_window_similar_images_finder = gui_data.scrolled_window_similar_images_finder.clone(); + let buttons_array = gui_data.buttons_array.clone(); + let text_view_errors = gui_data.text_view_errors.clone(); + let shared_duplication_state = gui_data.shared_duplication_state.clone(); + let shared_buttons = gui_data.shared_buttons.clone(); + let scrolled_window_zeroed_files_finder = gui_data.scrolled_window_zeroed_files_finder.clone(); + let shared_empty_folders_state = gui_data.shared_empty_folders_state.clone(); + let shared_empty_files_state = gui_data.shared_empty_files_state.clone(); + let scrolled_window_big_files_finder = gui_data.scrolled_window_big_files_finder.clone(); + let shared_big_files_state = gui_data.shared_big_files_state.clone(); + let scrolled_window_main_temporary_files_finder = gui_data.scrolled_window_main_temporary_files_finder.clone(); + let shared_temporary_files_state = gui_data.shared_temporary_files_state.clone(); + let shared_similar_images_state = gui_data.shared_similar_images_state.clone(); + let shared_zeroed_files_state = gui_data.shared_zeroed_files_state.clone(); + let buttons_names = gui_data.buttons_names.clone(); + + receiver.attach(None, move |msg| { + buttons_search.show(); + buttons_stop.hide(); + + // Restore clickability to main notebook + notebook_main.set_sensitive(true); + + match msg { + Message::Duplicates(df) => { + if df.get_stopped_search() { + entry_info.set_text("Searching for duplicated was stopped by user"); + + //Also clear list + scrolled_window_duplicate_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap() + .clear(); + } else { + let information = df.get_information(); + let text_messages = df.get_text_messages(); + + let duplicates_number: usize; + let duplicates_size: u64; + let duplicates_group: usize; + + match df.get_check_method() { + CheckingMethod::Name => { + duplicates_number = information.number_of_duplicated_files_by_name; + duplicates_size = 0; + duplicates_group = information.number_of_groups_by_name; + entry_info.set_text(format!("Found {} files in {} groups which have same names.", duplicates_number, duplicates_group).as_str()); + } + CheckingMethod::Hash | CheckingMethod::HashMB => { + duplicates_number = information.number_of_duplicated_files_by_hash; + duplicates_size = information.lost_space_by_hash; + duplicates_group = information.number_of_groups_by_hash; + entry_info.set_text(format!("Found {} duplicates files in {} groups which took {}.", duplicates_number, duplicates_group, duplicates_size.file_size(options::BINARY).unwrap()).as_str()); + } + CheckingMethod::Size => { + duplicates_number = information.number_of_duplicated_files_by_size; + duplicates_size = information.lost_space_by_size; + duplicates_group = information.number_of_groups_by_size; + entry_info.set_text(format!("Found {} duplicates files in {} groups which took {}.", duplicates_number, duplicates_group, duplicates_size.file_size(options::BINARY).unwrap()).as_str()); + } + CheckingMethod::None => { + panic!(); + } + } + + entry_info.set_text(format!("Found {} duplicates files in {} groups which took {}.", duplicates_number, duplicates_group, duplicates_size.file_size(options::BINARY).unwrap()).as_str()); + + // Create GUI + { + let list_store = scrolled_window_duplicate_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap(); + list_store.clear(); + + let col_indices = [0, 1, 2, 3, 4, 5]; + + match df.get_check_method() { + CheckingMethod::Name => { + let btreemap = df.get_files_sorted_by_names(); + + for (name, vector) in btreemap.iter().rev() { + let values: [&dyn ToValue; 6] = [ + &name, + &(format!("{} results", vector.len())), + &"".to_string(), // No text in 3 column + &(0), // Not used here + &(HEADER_ROW_COLOR.to_string()), + &(TEXT_COLOR.to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + for entry in vector { + let (directory, file) = split_path(&entry.path); + let values: [&dyn ToValue; 6] = [ + &file, + &directory, + &(format!("{} - ({})", NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string(), entry.size.file_size(options::BINARY).unwrap())), + &(entry.modified_date), + &(MAIN_ROW_COLOR.to_string()), + &(TEXT_COLOR.to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + } + } + } + CheckingMethod::Hash | CheckingMethod::HashMB => { + let btreemap = df.get_files_sorted_by_hash(); + + for (size, vectors_vector) in btreemap.iter().rev() { + for vector in vectors_vector { + let values: [&dyn ToValue; 6] = [ + &(format!("{} x {} ({} bytes)", vector.len(), size.file_size(options::BINARY).unwrap(), size)), + &(format!("{} ({} bytes) lost", ((vector.len() - 1) as u64 * *size as u64).file_size(options::BINARY).unwrap(), (vector.len() - 1) as u64 * *size as u64)), + &"".to_string(), // No text in 3 column + &(0), // Not used here + &(HEADER_ROW_COLOR.to_string()), + &(TEXT_COLOR.to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + for entry in vector { + let (directory, file) = split_path(&entry.path); + let values: [&dyn ToValue; 6] = [ + &file, + &directory, + &(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string()), + &(entry.modified_date), + &(MAIN_ROW_COLOR.to_string()), + &(TEXT_COLOR.to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + } + } + } + } + CheckingMethod::Size => { + let btreemap = df.get_files_sorted_by_size(); + + for (size, vector) in btreemap.iter().rev() { + let values: [&dyn ToValue; 6] = [ + &(format!("{} x {} ({} bytes)", vector.len(), size.file_size(options::BINARY).unwrap(), size)), + &(format!("{} ({} bytes) lost", ((vector.len() - 1) as u64 * *size as u64).file_size(options::BINARY).unwrap(), (vector.len() - 1) as u64 * *size as u64)), + &"".to_string(), // No text in 3 column + &(0), // Not used here + &(HEADER_ROW_COLOR.to_string()), + &(TEXT_COLOR.to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + for entry in vector { + let (directory, file) = split_path(&entry.path); + let values: [&dyn ToValue; 6] = [ + &file, + &directory, + &(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string()), + &(entry.modified_date), + &(MAIN_ROW_COLOR.to_string()), + &(TEXT_COLOR.to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + } + } + } + CheckingMethod::None => { + panic!(); + } + } + + print_text_messages_to_text_view(text_messages, &text_view_errors); + } + + // Set state + { + *shared_duplication_state.borrow_mut() = df; + + if duplicates_number > 0 { + *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("save").unwrap() = true; + *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("delete").unwrap() = true; + *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("select").unwrap() = true; + } else { + *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("save").unwrap() = false; + *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("delete").unwrap() = false; + *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("select").unwrap() = false; + } + set_buttons(&mut *shared_buttons.borrow_mut().get_mut("duplicate").unwrap(), &buttons_array, &buttons_names); + } + } + } + Message::EmptyFolders(ef) => { + if ef.get_stopped_search() { + entry_info.set_text("Searching for empty folders was stopped by user"); + + //Also clear list + scrolled_window_main_empty_folder_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap() + .clear(); + } else { + let information = ef.get_information(); + let text_messages = ef.get_text_messages(); + + let empty_folder_number: usize = information.number_of_empty_folders; + + entry_info.set_text(format!("Found {} empty folders.", empty_folder_number).as_str()); + + // Create GUI + { + let list_store = scrolled_window_main_empty_folder_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap(); + list_store.clear(); + + let col_indices = [0, 1, 2]; + + let hashmap = ef.get_empty_folder_list(); + + for (path, entry) in hashmap { + let (directory, file) = split_path(path); + let values: [&dyn ToValue; 3] = [&file, &directory, &(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string())]; + list_store.set(&list_store.append(), &col_indices, &values); + } + print_text_messages_to_text_view(text_messages, &text_view_errors); + } + + // Set state + { + *shared_empty_folders_state.borrow_mut() = ef; + + if empty_folder_number > 0 { + *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap().get_mut("save").unwrap() = true; + *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap().get_mut("delete").unwrap() = true; + } else { + *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap().get_mut("save").unwrap() = false; + *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap().get_mut("delete").unwrap() = false; + } + set_buttons(&mut *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap(), &buttons_array, &buttons_names); + } + } + } + Message::EmptyFiles(vf) => { + if vf.get_stopped_search() { + entry_info.set_text("Searching for empty files was stopped by user"); + + //Also clear list + scrolled_window_main_empty_files_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap() + .clear(); + } else { + let information = vf.get_information(); + let text_messages = vf.get_text_messages(); + + let empty_files_number: usize = information.number_of_empty_files; + + entry_info.set_text(format!("Found {} empty files.", empty_files_number).as_str()); + + // Create GUI + { + let list_store = scrolled_window_main_empty_files_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap(); + list_store.clear(); + + let col_indices = [0, 1, 2]; + + let vector = vf.get_empty_files(); + + for file_entry in vector { + let (directory, file) = split_path(&file_entry.path); + let values: [&dyn ToValue; 3] = [&file, &directory, &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string())]; + list_store.set(&list_store.append(), &col_indices, &values); + } + print_text_messages_to_text_view(text_messages, &text_view_errors); + } + + // Set state + { + *shared_empty_files_state.borrow_mut() = vf; + + if empty_files_number > 0 { + *shared_buttons.borrow_mut().get_mut("empty_file").unwrap().get_mut("save").unwrap() = true; + *shared_buttons.borrow_mut().get_mut("empty_file").unwrap().get_mut("delete").unwrap() = true; + } else { + *shared_buttons.borrow_mut().get_mut("empty_file").unwrap().get_mut("save").unwrap() = false; + *shared_buttons.borrow_mut().get_mut("empty_file").unwrap().get_mut("delete").unwrap() = false; + } + set_buttons(&mut *shared_buttons.borrow_mut().get_mut("empty_file").unwrap(), &buttons_array, &buttons_names); + } + } + } + Message::BigFiles(bf) => { + if bf.get_stopped_search() { + entry_info.set_text("Searching for big files was stopped by user"); + + //Also clear list + scrolled_window_duplicate_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap() + .clear(); + } else { + let information = bf.get_information(); + let text_messages = bf.get_text_messages(); + + let biggest_files_number: usize = information.number_of_real_files; + + entry_info.set_text(format!("Found {} biggest files.", biggest_files_number).as_str()); + + // Create GUI + { + let list_store = scrolled_window_big_files_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap(); + list_store.clear(); + + let col_indices = [0, 1, 2, 3]; + + let btreemap = bf.get_big_files(); + + for (size, vector) in btreemap.iter().rev() { + for file_entry in vector { + let (directory, file) = split_path(&file_entry.path); + let values: [&dyn ToValue; 4] = [ + &(format!("{} ({} bytes)", size.file_size(options::BINARY).unwrap(), size)), + &file, + &directory, + &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + } + } + print_text_messages_to_text_view(text_messages, &text_view_errors); + } + + // Set state + { + *shared_big_files_state.borrow_mut() = bf; + + if biggest_files_number > 0 { + *shared_buttons.borrow_mut().get_mut("big_file").unwrap().get_mut("save").unwrap() = true; + *shared_buttons.borrow_mut().get_mut("big_file").unwrap().get_mut("delete").unwrap() = true; + } else { + *shared_buttons.borrow_mut().get_mut("big_file").unwrap().get_mut("save").unwrap() = false; + *shared_buttons.borrow_mut().get_mut("big_file").unwrap().get_mut("delete").unwrap() = false; + } + set_buttons(&mut *shared_buttons.borrow_mut().get_mut("big_file").unwrap(), &buttons_array, &buttons_names); + } + } + } + Message::Temporary(tf) => { + if tf.get_stopped_search() { + entry_info.set_text("Searching for temporary files was stopped by user"); + + //Also clear list + scrolled_window_duplicate_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap() + .clear(); + } else { + let information = tf.get_information(); + let text_messages = tf.get_text_messages(); + + let temporary_files_number: usize = information.number_of_temporary_files; + + entry_info.set_text(format!("Found {} temporary files.", temporary_files_number).as_str()); + + // Create GUI + { + let list_store = scrolled_window_main_temporary_files_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap(); + list_store.clear(); + + let col_indices = [0, 1, 2]; + + let vector = tf.get_temporary_files(); + + for file_entry in vector { + let (directory, file) = split_path(&file_entry.path); + let values: [&dyn ToValue; 3] = [&file, &directory, &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string())]; + list_store.set(&list_store.append(), &col_indices, &values); + } + print_text_messages_to_text_view(text_messages, &text_view_errors); + } + + // Set state + { + *shared_temporary_files_state.borrow_mut() = tf; + + if temporary_files_number > 0 { + *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap().get_mut("save").unwrap() = true; + *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap().get_mut("delete").unwrap() = true; + } else { + *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap().get_mut("save").unwrap() = false; + *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap().get_mut("delete").unwrap() = false; + } + set_buttons(&mut *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap(), &buttons_array, &buttons_names); + } + } + } + Message::SimilarImages(sf) => { + if sf.get_stopped_search() { + entry_info.set_text("Searching for duplicated was stopped by user"); + + //Also clear list + scrolled_window_similar_images_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap() + .clear(); + } else { + //let information = sf.get_information(); + let text_messages = sf.get_text_messages(); + + let base_images_size = sf.get_similar_images().len(); + + entry_info.set_text(format!("Found similar pictures for {} images.", base_images_size).as_str()); + + // Create GUI + { + let list_store = scrolled_window_similar_images_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap(); + list_store.clear(); + + let col_indices = [0, 1, 2, 3, 4, 5, 6, 7]; + + let vec_struct_similar = sf.get_similar_images(); + + for struct_similar in vec_struct_similar.iter() { + // Header + let (directory, file) = split_path(&struct_similar.base_image.path); + let values: [&dyn ToValue; 8] = [ + &(get_text_from_similarity(&struct_similar.base_image.similarity).to_string()), + &struct_similar.base_image.size.file_size(options::BINARY).unwrap(), + &struct_similar.base_image.dimensions, + &file, + &directory, + &(NaiveDateTime::from_timestamp(struct_similar.base_image.modified_date as i64, 0).to_string()), + &(HEADER_ROW_COLOR.to_string()), + &(TEXT_COLOR.to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + + // Meat + for similar_images in &struct_similar.similar_images { + let (directory, file) = split_path(&similar_images.path); + let values: [&dyn ToValue; 8] = [ + &(get_text_from_similarity(&similar_images.similarity).to_string()), + &similar_images.size.file_size(options::BINARY).unwrap(), + &similar_images.dimensions, + &file, + &directory, + &(NaiveDateTime::from_timestamp(similar_images.modified_date as i64, 0).to_string()), + &(MAIN_ROW_COLOR.to_string()), + &(TEXT_COLOR.to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + } + } + + print_text_messages_to_text_view(text_messages, &text_view_errors); + } + + // Set state + { + *shared_similar_images_state.borrow_mut() = sf; + + if base_images_size > 0 { + *shared_buttons.borrow_mut().get_mut("similar_images").unwrap().get_mut("save").unwrap() = true; + *shared_buttons.borrow_mut().get_mut("similar_images").unwrap().get_mut("delete").unwrap() = true; + } else { + *shared_buttons.borrow_mut().get_mut("similar_images").unwrap().get_mut("save").unwrap() = false; + *shared_buttons.borrow_mut().get_mut("similar_images").unwrap().get_mut("delete").unwrap() = false; + } + set_buttons(&mut *shared_buttons.borrow_mut().get_mut("similar_images").unwrap(), &buttons_array, &buttons_names); + } + } + } + + Message::ZeroedFiles(zf) => { + if zf.get_stopped_search() { + entry_info.set_text("Searching for zeroed files was stopped by user"); + + //Also clear list + scrolled_window_zeroed_files_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap() + .clear(); + } else { + let information = zf.get_information(); + let text_messages = zf.get_text_messages(); + + let zeroed_files_number: usize = information.number_of_zeroed_files; + + entry_info.set_text(format!("Found {} zeroed files.", zeroed_files_number).as_str()); + + // Create GUI + { + let list_store = scrolled_window_zeroed_files_finder + .get_children() + .get(0) + .unwrap() + .clone() + .downcast::() + .unwrap() + .get_model() + .unwrap() + .downcast::() + .unwrap(); + list_store.clear(); + + let col_indices = [0, 1, 2, 3]; + + let vector = zf.get_zeroed_files(); + + for file_entry in vector { + let (directory, file) = split_path(&file_entry.path); + let values: [&dyn ToValue; 4] = [ + &(file_entry.size.file_size(options::BINARY).unwrap()), + &file, + &directory, + &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + } + print_text_messages_to_text_view(text_messages, &text_view_errors); + } + + // Set state + { + *shared_zeroed_files_state.borrow_mut() = zf; + + if zeroed_files_number > 0 { + *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("save").unwrap() = true; + *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("delete").unwrap() = true; + } else { + *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("save").unwrap() = false; + *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("delete").unwrap() = false; + } + set_buttons(&mut *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap(), &buttons_array, &buttons_names); + } + } + } + } + // Returning false here would close the receiver and have senders fail + glib::Continue(true) + }); +} diff --git a/czkawka_gui/src/connect_notebook_tabs.rs b/czkawka_gui/src/connect_notebook_tabs.rs new file mode 100644 index 0000000..1f87ef6 --- /dev/null +++ b/czkawka_gui/src/connect_notebook_tabs.rs @@ -0,0 +1,59 @@ +extern crate gtk; +use crate::gui_data::GuiData; +use crate::help_functions::*; +use gtk::prelude::*; +use std::collections::HashMap; + +pub fn connect_notebook_tabs(gui_data: &GuiData) { + let shared_buttons = gui_data.shared_buttons.clone(); + let buttons_array = gui_data.buttons_array.clone(); + let notebook_main_children_names = gui_data.notebook_main_children_names.clone(); + let notebook_main_clone = gui_data.notebook_main.clone(); + let buttons_names = gui_data.buttons_names.clone(); + let upper_notebooks_labels = gui_data.upper_notebooks_labels.clone(); + let shared_upper_notebooks = gui_data.shared_upper_notebooks.clone(); + let notebook_upper = gui_data.notebook_upper.clone(); + let notebook_upper_children_names = gui_data.notebook_upper_children_names.clone(); + + 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"; + } + "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", + "notebook_main_similar_images_finder_label" => page = "similar_images", + "notebook_main_zeroed_files_finder" => page = "zeroed_files", + e => { + panic!("Not existent page {}", e); + } + }; + // Buttons + set_buttons(&mut *shared_buttons.borrow_mut().get_mut(page).unwrap(), &buttons_array, &buttons_names); + // Upper notebook + { + //let upper_notebooks_labels = [/*"general",*/"included_directories","excluded_directories","excluded_items","allowed_extensions"]; + let mut hashmap: HashMap<&str, &str> = Default::default(); + //hashmap.insert("notebook_upper_general","general"); + hashmap.insert("notebook_upper_included_directories", "included_directories"); + hashmap.insert("notebook_upper_excluded_directories", "excluded_directories"); + hashmap.insert("notebook_upper_excluded_items", "excluded_items"); + hashmap.insert("notebook_upper_allowed_extensions", "allowed_extensions"); + + for tab in ¬ebook_upper_children_names { + let name = hashmap.get(tab.as_str()).unwrap().to_string(); + let index = upper_notebooks_labels.iter().position(|x| *x == name).unwrap(); + if *shared_upper_notebooks.borrow_mut().get_mut(page).unwrap().get_mut(&name).unwrap() { + notebook_upper.get_children().get(index).unwrap().show(); + } else { + notebook_upper.get_children().get(index).unwrap().hide(); + } + } + } + }); +} diff --git a/czkawka_gui/src/connect_popover_duplicate.rs b/czkawka_gui/src/connect_popover_duplicate.rs new file mode 100644 index 0000000..5355b11 --- /dev/null +++ b/czkawka_gui/src/connect_popover_duplicate.rs @@ -0,0 +1,322 @@ +extern crate gtk; +use crate::gui_data::GuiData; +use crate::help_functions::*; +use gtk::prelude::*; +use gtk::TreeIter; + +pub fn connect_popover_duplicate(gui_data: &GuiData) { + // Select all button + { + let buttons_popover_select_all = gui_data.buttons_popover_select_all.clone(); + let scrolled_window_duplicate_finder = gui_data.scrolled_window_duplicate_finder.clone(); + let popover_select = gui_data.popover_select.clone(); + buttons_popover_select_all.connect_clicked(move |_| { + let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let selection = tree_view.get_selection(); + + selection.select_all(); + popover_select.popdown(); + }); + } + + // Unselect all button + { + let scrolled_window_duplicate_finder = gui_data.scrolled_window_duplicate_finder.clone(); + let buttons_popover_unselect_all = gui_data.buttons_popover_unselect_all.clone(); + let popover_select = gui_data.popover_select.clone(); + buttons_popover_unselect_all.connect_clicked(move |_| { + let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let selection = tree_view.get_selection(); + + selection.unselect_all(); + popover_select.popdown(); + }); + } + + // Reverse selection + { + let scrolled_window_duplicate_finder = gui_data.scrolled_window_duplicate_finder.clone(); + let popover_select = gui_data.popover_select.clone(); + let buttons_popover_reverse = gui_data.buttons_popover_reverse.clone(); + buttons_popover_reverse.connect_clicked(move |_| { + let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let selection = tree_view.get_selection(); + + let (vector_tree_path, tree_model) = selection.get_selected_rows(); + + if vector_tree_path.is_empty() { + selection.select_all(); + } else { + let tree_iter_all = tree_model.get_iter_first().unwrap(); // Never should be available button where there is no available records + + let mut current_path_index = 0; + let mut tree_iter_selected: TreeIter; + loop { + if current_path_index >= vector_tree_path.len() { + selection.select_iter(&tree_iter_all); + } else { + tree_iter_selected = tree_model.get_iter(vector_tree_path.get(current_path_index).unwrap()).unwrap(); + if tree_model.get_path(&tree_iter_all).unwrap() == tree_model.get_path(&tree_iter_selected).unwrap() { + selection.unselect_iter(&tree_iter_selected); + current_path_index += 1; + } else { + selection.select_iter(&tree_iter_all); + } + } + if !tree_model.iter_next(&tree_iter_all) { + break; + } + } + } + + popover_select.popdown(); + }); + } + + // All except oldest + { + let scrolled_window_duplicate_finder = gui_data.scrolled_window_duplicate_finder.clone(); + let popover_select = gui_data.popover_select.clone(); + let buttons_popover_select_all_except_oldest = gui_data.buttons_popover_select_all_except_oldest.clone(); + buttons_popover_select_all_except_oldest.connect_clicked(move |_| { + let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let selection = tree_view.get_selection(); + let tree_model = tree_view.get_model().unwrap(); + + let tree_iter_all = tree_model.get_iter_first().unwrap(); // Never should be available button where there is no available records + + let mut end: bool = false; + + loop { + let mut tree_iter_array: Vec = Vec::new(); + let mut oldest_index: Option = None; + let mut current_index: usize = 0; + let mut oldest_modification_time: u64 = u64::max_value(); + + loop { + let color = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::Color as i32).get::().unwrap().unwrap(); + if color == HEADER_ROW_COLOR { + if !tree_model.iter_next(&tree_iter_all) { + end = true; + } + break; + } + tree_iter_array.push(tree_iter_all.clone()); + let modification = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::ModificationAsSecs as i32).get::().unwrap().unwrap(); + if modification < oldest_modification_time { + oldest_modification_time = modification; + oldest_index = Some(current_index); + } + + current_index += 1; + + if !tree_model.iter_next(&tree_iter_all) { + end = true; + break; + } + } + if oldest_index == None { + continue; + } + for (index, tree_iter) in tree_iter_array.iter().enumerate() { + if index != oldest_index.unwrap() { + selection.select_iter(tree_iter); + } else { + selection.unselect_iter(tree_iter); + } + } + + if end { + break; + } + } + + popover_select.popdown(); + }); + } + + // All except newest + { + let scrolled_window_duplicate_finder = gui_data.scrolled_window_duplicate_finder.clone(); + let popover_select = gui_data.popover_select.clone(); + let buttons_popover_select_all_except_newest = gui_data.buttons_popover_select_all_except_newest.clone(); + buttons_popover_select_all_except_newest.connect_clicked(move |_| { + let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let selection = tree_view.get_selection(); + let tree_model = tree_view.get_model().unwrap(); + + let tree_iter_all = tree_model.get_iter_first().unwrap(); // Never should be available button where there is no available records + + let mut end: bool = false; + + loop { + let mut tree_iter_array: Vec = Vec::new(); + let mut newest_index: Option = None; + let mut current_index: usize = 0; + let mut newest_modification_time: u64 = 0; + + loop { + let color = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::Color as i32).get::().unwrap().unwrap(); + if color == HEADER_ROW_COLOR { + if !tree_model.iter_next(&tree_iter_all) { + end = true; + } + break; + } + tree_iter_array.push(tree_iter_all.clone()); + let modification = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::ModificationAsSecs as i32).get::().unwrap().unwrap(); + if modification > newest_modification_time { + newest_modification_time = modification; + newest_index = Some(current_index); + } + + current_index += 1; + + if !tree_model.iter_next(&tree_iter_all) { + end = true; + break; + } + } + if newest_index == None { + continue; + } + for (index, tree_iter) in tree_iter_array.iter().enumerate() { + if index != newest_index.unwrap() { + selection.select_iter(tree_iter); + } else { + selection.unselect_iter(tree_iter); + } + } + + if end { + break; + } + } + + popover_select.popdown(); + }); + } + + // All one oldest + { + let scrolled_window_duplicate_finder = gui_data.scrolled_window_duplicate_finder.clone(); + let popover_select = gui_data.popover_select.clone(); + let buttons_popover_select_one_oldest = gui_data.buttons_popover_select_one_oldest.clone(); + buttons_popover_select_one_oldest.connect_clicked(move |_| { + let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let selection = tree_view.get_selection(); + let tree_model = tree_view.get_model().unwrap(); + + let tree_iter_all = tree_model.get_iter_first().unwrap(); // Never should be available button where there is no available records + + let mut end: bool = false; + + loop { + let mut tree_iter_array: Vec = Vec::new(); + let mut oldest_index: Option = None; + let mut current_index: usize = 0; + let mut oldest_modification_time: u64 = u64::max_value(); + + loop { + let color = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::Color as i32).get::().unwrap().unwrap(); + if color == HEADER_ROW_COLOR { + if !tree_model.iter_next(&tree_iter_all) { + end = true; + } + break; + } + tree_iter_array.push(tree_iter_all.clone()); + let modification = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::ModificationAsSecs as i32).get::().unwrap().unwrap(); + if modification < oldest_modification_time { + oldest_modification_time = modification; + oldest_index = Some(current_index); + } + + current_index += 1; + + if !tree_model.iter_next(&tree_iter_all) { + end = true; + break; + } + } + if oldest_index == None { + continue; + } + for (index, tree_iter) in tree_iter_array.iter().enumerate() { + if index == oldest_index.unwrap() { + selection.select_iter(tree_iter); + } else { + selection.unselect_iter(tree_iter); + } + } + + if end { + break; + } + } + + popover_select.popdown(); + }); + } + // All one newest + { + let scrolled_window_duplicate_finder = gui_data.scrolled_window_duplicate_finder.clone(); + let buttons_popover_select_one_newest = gui_data.buttons_popover_select_one_newest.clone(); + let popover_select = gui_data.popover_select.clone(); + buttons_popover_select_one_newest.connect_clicked(move |_| { + let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let selection = tree_view.get_selection(); + let tree_model = tree_view.get_model().unwrap(); + + let tree_iter_all = tree_model.get_iter_first().unwrap(); // Never should be available button where there is no available records + + let mut end: bool = false; + + loop { + let mut tree_iter_array: Vec = Vec::new(); + let mut newest_index: Option = None; + let mut current_index: usize = 0; + let mut newest_modification_time: u64 = 0; + + loop { + let color = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::Color as i32).get::().unwrap().unwrap(); + if color == HEADER_ROW_COLOR { + if !tree_model.iter_next(&tree_iter_all) { + end = true; + } + break; + } + tree_iter_array.push(tree_iter_all.clone()); + let modification = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::ModificationAsSecs as i32).get::().unwrap().unwrap(); + if modification > newest_modification_time { + newest_modification_time = modification; + newest_index = Some(current_index); + } + + current_index += 1; + + if !tree_model.iter_next(&tree_iter_all) { + end = true; + break; + } + } + if newest_index == None { + continue; + } + for (index, tree_iter) in tree_iter_array.iter().enumerate() { + if index == newest_index.unwrap() { + selection.select_iter(tree_iter); + } else { + selection.unselect_iter(tree_iter); + } + } + + if end { + break; + } + } + + popover_select.popdown(); + }); + } +} diff --git a/czkawka_gui/src/connect_upper_notebook.rs b/czkawka_gui/src/connect_upper_notebook.rs new file mode 100644 index 0000000..0f042ac --- /dev/null +++ b/czkawka_gui/src/connect_upper_notebook.rs @@ -0,0 +1,98 @@ +extern crate gtk; +use crate::gui_data::GuiData; +use gtk::prelude::*; + +pub fn connect_upper_notebook(gui_data: &GuiData) { + // Add included directory + { + let scrolled_window_included_directories = gui_data.scrolled_window_included_directories.clone(); + let window_main = gui_data.window_main.clone(); + let buttons_add_included_directory = gui_data.buttons_add_included_directory.clone(); + buttons_add_included_directory.connect_clicked(move |_| { + let chooser = gtk::FileChooserDialog::with_buttons( + Option::from("Folders to include"), + Option::from(&window_main), + gtk::FileChooserAction::SelectFolder, + &[("Ok", gtk::ResponseType::Ok), ("Close", gtk::ResponseType::Cancel)], + ); + chooser.show_all(); + let response_type = chooser.run(); + if response_type == gtk::ResponseType::Ok { + let folder = chooser.get_filename().unwrap().to_str().unwrap().to_string(); + + let tree_view = scrolled_window_included_directories.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let list_store = tree_view.get_model().unwrap().downcast::().unwrap(); + + let col_indices = [0]; + + let values: [&dyn ToValue; 1] = [&folder]; + list_store.set(&list_store.append(), &col_indices, &values); + } + chooser.close(); + }); + } + // Add excluded directory + { + let scrolled_window_excluded_directories = gui_data.scrolled_window_excluded_directories.clone(); + let window_main = gui_data.window_main.clone(); + let buttons_add_excluded_directory = gui_data.buttons_add_excluded_directory.clone(); + buttons_add_excluded_directory.connect_clicked(move |_| { + let chooser = gtk::FileChooserDialog::with_buttons( + Option::from("Folders to exclude"), + Option::from(&window_main), + gtk::FileChooserAction::SelectFolder, + &[("Ok", gtk::ResponseType::Ok), ("Close", gtk::ResponseType::Cancel)], + ); + chooser.show_all(); + let response_type = chooser.run(); + if response_type == gtk::ResponseType::Ok { + let folder = chooser.get_filename().unwrap().to_str().unwrap().to_string(); + + let tree_view = scrolled_window_excluded_directories.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let list_store = tree_view.get_model().unwrap().downcast::().unwrap(); + + let col_indices = [0]; + + let values: [&dyn ToValue; 1] = [&folder]; + list_store.set(&list_store.append(), &col_indices, &values); + } + chooser.close(); + }); + } + // Remove Excluded Folder + { + let buttons_remove_excluded_directory = gui_data.buttons_remove_excluded_directory.clone(); + let scrolled_window_excluded_directories = gui_data.scrolled_window_excluded_directories.clone(); + buttons_remove_excluded_directory.connect_clicked(move |_| { + let tree_view = scrolled_window_excluded_directories.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let list_store = tree_view.get_model().unwrap().downcast::().unwrap(); + let selection = tree_view.get_selection(); + + let (_, tree_iter) = match selection.get_selected() { + Some(t) => t, + None => { + return; + } + }; + list_store.remove(&tree_iter); + }); + } + // Remove Included Folder + { + let buttons_remove_included_directory = gui_data.buttons_remove_included_directory.clone(); + let scrolled_window_included_directories = gui_data.scrolled_window_included_directories.clone(); + buttons_remove_included_directory.connect_clicked(move |_| { + let tree_view = scrolled_window_included_directories.get_children().get(0).unwrap().clone().downcast::().unwrap(); + let list_store = tree_view.get_model().unwrap().downcast::().unwrap(); + let selection = tree_view.get_selection(); + + let (_, tree_iter) = match selection.get_selected() { + Some(t) => t, + None => { + return; + } + }; + list_store.remove(&tree_iter); + }); + } +} diff --git a/czkawka_gui/src/gui_data.rs b/czkawka_gui/src/gui_data.rs new file mode 100644 index 0000000..745f830 --- /dev/null +++ b/czkawka_gui/src/gui_data.rs @@ -0,0 +1,358 @@ +extern crate gtk; +use crossbeam_channel::unbounded; +use czkawka_core::big_file::BigFile; +use czkawka_core::duplicate::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 czkawka_core::zeroed::ZeroedFiles; +use gtk::prelude::*; +use gtk::{Builder, Button}; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +#[derive(Clone)] +pub struct GuiData { + pub glade_src: String, + pub builder: Builder, + // Windows + pub window_main: gtk::Window, + // States + pub main_notebooks_labels: [String; 7], + pub upper_notebooks_labels: [String; 4], + pub buttons_labels: [String; 7], + // Buttons state + pub shared_buttons: Rc>>>, + + // Upper Notebook state + pub shared_upper_notebooks: Rc>>>, + + // State of search results + pub shared_duplication_state: Rc>, + pub shared_empty_folders_state: Rc>, + pub shared_empty_files_state: Rc>, + pub shared_temporary_files_state: Rc>, + pub shared_big_files_state: Rc>, + pub shared_similar_images_state: Rc>, + pub shared_zeroed_files_state: Rc>, + + // State of confirmation dialogs + pub shared_confirmation_dialog_delete_dialog_showing_state: Rc>, + + //// GUI Entry + pub entry_similar_images_minimal_size: gtk::Entry, + pub entry_duplicate_minimal_size: gtk::Entry, + pub entry_allowed_extensions: gtk::Entry, + pub entry_excluded_items: gtk::Entry, + pub entry_big_files_number: gtk::Entry, + + //// GUI Buttons + pub buttons_search: gtk::Button, + pub buttons_stop: gtk::Button, + pub buttons_resume: gtk::Button, + pub buttons_pause: gtk::Button, + pub buttons_select: gtk::Button, + pub buttons_delete: gtk::Button, + pub buttons_save: gtk::Button, + pub buttons_names: [String; 7], + pub buttons_array: [Button; 7], + pub buttons_add_included_directory: gtk::Button, + pub buttons_remove_included_directory: gtk::Button, + pub buttons_add_excluded_directory: gtk::Button, + pub buttons_remove_excluded_directory: gtk::Button, + + // Buttons search popover buttons + pub buttons_popover_select_all: gtk::Button, + pub buttons_popover_unselect_all: gtk::Button, + pub buttons_popover_reverse: gtk::Button, + pub buttons_popover_select_all_except_oldest: gtk::Button, + pub buttons_popover_select_all_except_newest: gtk::Button, + pub buttons_popover_select_one_oldest: gtk::Button, + pub buttons_popover_select_one_newest: gtk::Button, + + //// Popovers + pub popover_select: gtk::Popover, + + //// Check Buttons + pub check_button_recursive: gtk::CheckButton, + + //// Radio Buttons + pub radio_button_name: gtk::RadioButton, + pub radio_button_size: gtk::RadioButton, + pub radio_button_hashmb: gtk::RadioButton, + pub radio_button_hash: gtk::RadioButton, + + //// Notebooks + pub notebook_main: gtk::Notebook, + pub notebook_upper: gtk::Notebook, + + pub notebook_main_children_names: Vec, + pub notebook_upper_children_names: Vec, + + //// Entry + pub entry_info: gtk::Entry, // To show default + + //// Text View + pub text_view_errors: gtk::TextView, + + //// Scrolled windows + // Main notebook + pub scrolled_window_duplicate_finder: gtk::ScrolledWindow, + pub scrolled_window_main_empty_folder_finder: gtk::ScrolledWindow, + pub scrolled_window_main_empty_files_finder: gtk::ScrolledWindow, + pub scrolled_window_main_temporary_files_finder: gtk::ScrolledWindow, + pub scrolled_window_big_files_finder: gtk::ScrolledWindow, + pub scrolled_window_similar_images_finder: gtk::ScrolledWindow, + pub scrolled_window_zeroed_files_finder: gtk::ScrolledWindow, + + // Upper notebook + pub scrolled_window_included_directories: gtk::ScrolledWindow, + pub scrolled_window_excluded_directories: gtk::ScrolledWindow, + + //// Threads + + // Used for sending stop signal to thread + pub sx: crossbeam_channel::Sender<()>, + pub rx: crossbeam_channel::Receiver<()>, +} + +impl GuiData { + pub fn new() -> Self { + //// Loading glade file content and build with it help UI + let glade_src = include_str!("../czkawka.glade").to_string(); + let builder = Builder::from_string(glade_src.as_str()); + + //// Windows + let window_main: gtk::Window = builder.get_object("window_main").unwrap(); + window_main.show_all(); + window_main.set_title("Czkawka"); + + //////////////////////////////////////////////////////////////////////////////////////////////// + //// States + let main_notebooks_labels = [ + "duplicate".to_string(), + "empty_folder".to_string(), + "empty_file".to_string(), + "temporary_file".to_string(), + "big_file".to_string(), + "similar_images".to_string(), + "zeroed_files".to_string(), + ]; + let upper_notebooks_labels = [ + /*"general",*/ "included_directories".to_string(), + "excluded_directories".to_string(), + "excluded_items".to_string(), + "allowed_extensions".to_string(), + ]; + let buttons_labels = ["search".to_string(), "stop".to_string(), "resume".to_string(), "pause".to_string(), "select".to_string(), "delete".to_string(), "save".to_string()]; + + // Buttons State - to remember existence of different buttons on pages + + let shared_buttons: Rc> = Rc::new(RefCell::new(HashMap::>::new())); + shared_buttons.borrow_mut().clear(); + + // Show by default only search button + for i in main_notebooks_labels.iter() { + let mut temp_hashmap: HashMap = Default::default(); + for j in buttons_labels.iter() { + if *j == "search" { + temp_hashmap.insert(j.to_string(), true); + } else { + temp_hashmap.insert(j.to_string(), false); + } + } + shared_buttons.borrow_mut().insert(i.to_string(), temp_hashmap); + } + + // Upper Notebook state + let shared_upper_notebooks: Rc> = Rc::new(RefCell::new(HashMap::>::new())); + + for i in main_notebooks_labels.iter() { + let mut temp_hashmap: HashMap = Default::default(); + for j in upper_notebooks_labels.iter() { + temp_hashmap.insert(j.to_string(), true); + } + shared_upper_notebooks.borrow_mut().insert(i.to_string(), temp_hashmap); + } + // Some upper notebook tabs are disabled + *shared_upper_notebooks.borrow_mut().get_mut("empty_file").unwrap().get_mut("allowed_extensions").unwrap() = false; + *shared_upper_notebooks.borrow_mut().get_mut("temporary_file").unwrap().get_mut("allowed_extensions").unwrap() = false; + + // State of search results + + let shared_duplication_state: Rc> = Rc::new(RefCell::new(DuplicateFinder::new())); + let shared_empty_folders_state: Rc> = Rc::new(RefCell::new(EmptyFolder::new())); + let shared_empty_files_state: Rc> = Rc::new(RefCell::new(EmptyFiles::new())); + let shared_temporary_files_state: Rc> = Rc::new(RefCell::new(Temporary::new())); + let shared_big_files_state: Rc> = Rc::new(RefCell::new(BigFile::new())); + let shared_similar_images_state: Rc> = Rc::new(RefCell::new(SimilarImages::new())); + let shared_zeroed_files_state: Rc> = Rc::new(RefCell::new(ZeroedFiles::new())); + + // State of confirmation dialogs + let shared_confirmation_dialog_delete_dialog_showing_state: Rc> = Rc::new(RefCell::new(true)); + + //////////////////////////////////////////////////////////////////////////////////////////////// + + //// GUI Entry + let entry_similar_images_minimal_size: gtk::Entry = builder.get_object("entry_similar_images_minimal_size").unwrap(); + let entry_duplicate_minimal_size: gtk::Entry = builder.get_object("entry_duplicate_minimal_size").unwrap(); + let entry_allowed_extensions: gtk::Entry = builder.get_object("entry_allowed_extensions").unwrap(); + let entry_excluded_items: gtk::Entry = builder.get_object("entry_excluded_items").unwrap(); + let entry_big_files_number: gtk::Entry = builder.get_object("entry_big_files_number").unwrap(); + + //// GUI Buttons + let buttons_search: gtk::Button = builder.get_object("buttons_search").unwrap(); + let buttons_stop: gtk::Button = builder.get_object("buttons_stop").unwrap(); + let buttons_resume: gtk::Button = builder.get_object("buttons_resume").unwrap(); + let buttons_pause: gtk::Button = builder.get_object("buttons_pause").unwrap(); + let buttons_select: gtk::Button = builder.get_object("buttons_select").unwrap(); + let buttons_delete: gtk::Button = builder.get_object("buttons_delete").unwrap(); + let buttons_save: gtk::Button = builder.get_object("buttons_save").unwrap(); + + let buttons_names = ["search".to_string(), "stop".to_string(), "resume".to_string(), "pause".to_string(), "select".to_string(), "delete".to_string(), "save".to_string()]; + let buttons_array = [ + buttons_search.clone(), + buttons_stop.clone(), + buttons_resume.clone(), + buttons_pause.clone(), + buttons_select.clone(), + buttons_delete.clone(), + buttons_save.clone(), + ]; + + let buttons_add_included_directory: gtk::Button = builder.get_object("buttons_add_included_directory").unwrap(); + let buttons_remove_included_directory: gtk::Button = builder.get_object("buttons_remove_included_directory").unwrap(); + let buttons_add_excluded_directory: gtk::Button = builder.get_object("buttons_add_excluded_directory").unwrap(); + let buttons_remove_excluded_directory: gtk::Button = builder.get_object("buttons_remove_excluded_directory").unwrap(); + + // Buttons search popover buttons + let buttons_popover_select_all: gtk::Button = builder.get_object("buttons_popover_select_all").unwrap(); + let buttons_popover_unselect_all: gtk::Button = builder.get_object("buttons_popover_unselect_all").unwrap(); + let buttons_popover_reverse: gtk::Button = builder.get_object("buttons_popover_reverse").unwrap(); + let buttons_popover_select_all_except_oldest: gtk::Button = builder.get_object("buttons_popover_select_all_except_oldest").unwrap(); + let buttons_popover_select_all_except_newest: gtk::Button = builder.get_object("buttons_popover_select_all_except_newest").unwrap(); + let buttons_popover_select_one_oldest: gtk::Button = builder.get_object("buttons_popover_select_one_oldest").unwrap(); + let buttons_popover_select_one_newest: gtk::Button = builder.get_object("buttons_popover_select_one_newest").unwrap(); + + //// Popovers + let popover_select: gtk::Popover = builder.get_object("popover_select").unwrap(); + + //// Check Buttons + let check_button_recursive: gtk::CheckButton = builder.get_object("check_button_recursive").unwrap(); + + //// Radio Buttons + let radio_button_name: gtk::RadioButton = builder.get_object("radio_button_name").unwrap(); + let radio_button_size: gtk::RadioButton = builder.get_object("radio_button_size").unwrap(); + let radio_button_hashmb: gtk::RadioButton = builder.get_object("radio_button_hashmb").unwrap(); + let radio_button_hash: gtk::RadioButton = builder.get_object("radio_button_hash").unwrap(); + + //// Notebooks + let notebook_main: gtk::Notebook = builder.get_object("notebook_main").unwrap(); + let notebook_upper: gtk::Notebook = builder.get_object("notebook_upper").unwrap(); + + let mut notebook_main_children_names: Vec = Vec::new(); + let mut notebook_upper_children_names: Vec = Vec::new(); + + for i in notebook_main.get_children() { + notebook_main_children_names.push(i.get_buildable_name().unwrap().to_string()); + } + for i in notebook_upper.get_children() { + notebook_upper_children_names.push(i.get_buildable_name().unwrap().to_string()); + } + + //// Entry + let entry_info: gtk::Entry = builder.get_object("entry_info").unwrap(); // To show default + + //// Text View + let text_view_errors: gtk::TextView = builder.get_object("text_view_errors").unwrap(); + + //// Scrolled windows + // Main notebook + let scrolled_window_duplicate_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_duplicate_finder").unwrap(); + let scrolled_window_main_empty_folder_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_main_empty_folder_finder").unwrap(); + let scrolled_window_main_empty_files_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_main_empty_files_finder").unwrap(); + let scrolled_window_main_temporary_files_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_main_temporary_files_finder").unwrap(); + let scrolled_window_big_files_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_big_files_finder").unwrap(); + let scrolled_window_similar_images_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_similar_images_finder").unwrap(); + let scrolled_window_zeroed_files_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_zeroed_files_finder").unwrap(); + + // Upper notebook + let scrolled_window_included_directories: gtk::ScrolledWindow = builder.get_object("scrolled_window_included_directories").unwrap(); + let scrolled_window_excluded_directories: gtk::ScrolledWindow = builder.get_object("scrolled_window_excluded_directories").unwrap(); + + //// Threads + // 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(); + + Self { + glade_src, + builder, + window_main, + main_notebooks_labels, + upper_notebooks_labels, + buttons_labels, + shared_buttons, + shared_upper_notebooks, + shared_duplication_state, + shared_empty_folders_state, + shared_empty_files_state, + shared_temporary_files_state, + shared_big_files_state, + shared_similar_images_state, + shared_zeroed_files_state, + shared_confirmation_dialog_delete_dialog_showing_state, + entry_similar_images_minimal_size, + entry_duplicate_minimal_size, + entry_allowed_extensions, + entry_excluded_items, + entry_big_files_number, + buttons_search, + buttons_stop, + buttons_resume, + buttons_pause, + buttons_select, + buttons_delete, + buttons_save, + buttons_names, + buttons_array, + buttons_add_included_directory, + buttons_remove_included_directory, + buttons_add_excluded_directory, + buttons_remove_excluded_directory, + buttons_popover_select_all, + buttons_popover_unselect_all, + buttons_popover_reverse, + buttons_popover_select_all_except_oldest, + buttons_popover_select_all_except_newest, + buttons_popover_select_one_oldest, + buttons_popover_select_one_newest, + popover_select, + check_button_recursive, + radio_button_name, + radio_button_size, + radio_button_hashmb, + radio_button_hash, + notebook_main, + notebook_upper, + notebook_main_children_names, + notebook_upper_children_names, + entry_info, + text_view_errors, + scrolled_window_duplicate_finder, + scrolled_window_main_empty_folder_finder, + scrolled_window_main_empty_files_finder, + scrolled_window_main_temporary_files_finder, + scrolled_window_big_files_finder, + scrolled_window_similar_images_finder, + scrolled_window_zeroed_files_finder, + scrolled_window_included_directories, + scrolled_window_excluded_directories, + sx, + rx, + } + } +} diff --git a/czkawka_gui/src/help_functions.rs b/czkawka_gui/src/help_functions.rs index 6b549c6..8c02d53 100644 --- a/czkawka_gui/src/help_functions.rs +++ b/czkawka_gui/src/help_functions.rs @@ -1,7 +1,24 @@ +use czkawka_core::big_file::BigFile; use czkawka_core::common_messages::Messages; -use czkawka_core::similar_files::Similarity; +use czkawka_core::duplicate::DuplicateFinder; +use czkawka_core::empty_files::EmptyFiles; +use czkawka_core::empty_folder::EmptyFolder; +use czkawka_core::similar_files::{SimilarImages, Similarity}; +use czkawka_core::temporary::Temporary; +use czkawka_core::zeroed::ZeroedFiles; use gtk::prelude::*; use std::collections::HashMap; +use std::path::Path; + +pub enum Message { + Duplicates(DuplicateFinder), + EmptyFolders(EmptyFolder), + EmptyFiles(EmptyFiles), + BigFiles(BigFile), + Temporary(Temporary), + SimilarImages(SimilarImages), + ZeroedFiles(ZeroedFiles), +} pub enum ColumnsDuplicates { // Columns for duplicate treeview @@ -84,6 +101,13 @@ pub fn get_string_from_list_store(scrolled_window: >k::ScrolledWindow) -> Stri } } } +pub fn split_path(path: &Path) -> (String, String) { + match (path.parent(), path.file_name()) { + (Some(dir), Some(file)) => (dir.display().to_string(), file.to_string_lossy().into_owned()), + (Some(dir), None) => (dir.display().to_string(), String::new()), + (None, _) => (String::new(), String::new()), + } +} pub fn print_text_messages_to_text_view(text_messages: &Messages, text_view: >k::TextView) { let mut messages: String = String::from(""); @@ -134,9 +158,9 @@ pub fn select_function_duplicates(_tree_selection: >k::TreeSelection, tree_mod true } -pub fn set_buttons(hashmap: &mut HashMap, buttons_array: &[gtk::Button], button_names: &[&str]) { +pub fn set_buttons(hashmap: &mut HashMap, buttons_array: &[gtk::Button], button_names: &[String]) { for (index, button) in buttons_array.iter().enumerate() { - if *hashmap.get_mut(button_names[index]).unwrap() { + if *hashmap.get_mut(button_names[index].as_str()).unwrap() { button.show(); } else { button.hide(); @@ -149,7 +173,7 @@ pub fn set_buttons(hashmap: &mut HashMap, buttons_array: &[gtk::Bu // } // } -pub fn hide_all_buttons_except(except_name: &str, buttons_array: &[gtk::Button], button_names: &[&str]) { +pub fn hide_all_buttons_except(except_name: &str, buttons_array: &[gtk::Button], button_names: &[String]) { for (index, button) in buttons_array.iter().enumerate() { if except_name == button_names[index] { button.show(); diff --git a/czkawka_gui/src/main.rs b/czkawka_gui/src/main.rs index 9125657..1ab0013 100644 --- a/czkawka_gui/src/main.rs +++ b/czkawka_gui/src/main.rs @@ -1,42 +1,34 @@ +mod connect_button_delete; +mod connect_button_save; +mod connect_button_search; +mod connect_button_select; +mod connect_button_stop; +mod connect_compute_results; +mod connect_notebook_tabs; +mod connect_popover_duplicate; +mod connect_upper_notebook; mod create_tree_view; mod double_click_opening; +mod gui_data; mod help_functions; +mod startup_configuration; use czkawka_core::*; -use humansize::{file_size_opts as options, FileSize}; - -// TODO split it across multiple files extern crate gtk; -use crate::create_tree_view::*; -use crate::double_click_opening::*; -use crate::help_functions::*; -use chrono::NaiveDateTime; -use crossbeam_channel::unbounded; -use czkawka_core::big_file::BigFile; -use czkawka_core::common_traits::SaveResults; -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 czkawka_core::zeroed::ZeroedFiles; +use crate::connect_button_delete::*; +use crate::connect_button_save::*; +use crate::connect_button_search::*; +use crate::connect_button_select::*; +use crate::connect_button_stop::*; +use crate::connect_compute_results::*; +use crate::connect_notebook_tabs::*; +use crate::connect_popover_duplicate::*; +use crate::connect_upper_notebook::*; +use crate::gui_data::*; +use crate::startup_configuration::*; use gtk::prelude::*; -use gtk::{Builder, SelectionMode, TreeIter, TreeView}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::fs::Metadata; -use std::path::Path; -use std::rc::Rc; -use std::{env, fs, process, thread}; - -fn split_path(path: &Path) -> (String, String) { - match (path.parent(), path.file_name()) { - (Some(dir), Some(file)) => (dir.display().to_string(), file.to_string_lossy().into_owned()), - (Some(dir), None) => (dir.display().to_string(), String::new()), - (None, _) => (String::new(), String::new()), - } -} +use std::{env, process}; fn main() { let mut exit_program_after_initialization: bool = false; @@ -57,2176 +49,24 @@ fn main() { gtk::init().expect("Failed to initialize GTK."); - //// Loading glade file content and build with it help UI - let glade_src = include_str!("../czkawka.glade"); - let builder = Builder::from_string(glade_src); - - //// Windows - let window_main: gtk::Window = builder.get_object("window_main").unwrap(); - window_main.show_all(); - window_main.set_title("Czkawka"); - - //////////////////////////////////////////////////////////////////////////////////////////////// - //// States - let main_notebooks_labels = ["duplicate", "empty_folder", "empty_file", "temporary_file", "big_file", "similar_images", "zeroed_files"]; - let upper_notebooks_labels = [/*"general",*/ "included_directories", "excluded_directories", "excluded_items", "allowed_extensions"]; - let buttons_labels = ["search", "stop", "resume", "pause", "select", "delete", "save"]; - - // Buttons State - to remember existence of different buttons on pages - - let shared_buttons: Rc> = Rc::new(RefCell::new(HashMap::>::new())); - shared_buttons.borrow_mut().clear(); - - // Show by default only search button - for i in main_notebooks_labels.iter() { - let mut temp_hashmap: HashMap = Default::default(); - for j in buttons_labels.iter() { - if *j == "search" { - temp_hashmap.insert(j.to_string(), true); - } else { - temp_hashmap.insert(j.to_string(), false); - } - } - shared_buttons.borrow_mut().insert(i.to_string(), temp_hashmap); - } - - // Upper Notebook state - let shared_upper_notebooks: Rc> = Rc::new(RefCell::new(HashMap::>::new())); - - for i in main_notebooks_labels.iter() { - let mut temp_hashmap: HashMap = Default::default(); - for j in upper_notebooks_labels.iter() { - temp_hashmap.insert(j.to_string(), true); - } - shared_upper_notebooks.borrow_mut().insert(i.to_string(), temp_hashmap); - } - // Some upper notebook tabs are disabled - *shared_upper_notebooks.borrow_mut().get_mut("empty_file").unwrap().get_mut("allowed_extensions").unwrap() = false; - *shared_upper_notebooks.borrow_mut().get_mut("temporary_file").unwrap().get_mut("allowed_extensions").unwrap() = false; - - // State of search results - - let shared_duplication_state: Rc> = Rc::new(RefCell::new(DuplicateFinder::new())); - let shared_empty_folders_state: Rc> = Rc::new(RefCell::new(EmptyFolder::new())); - let shared_empty_files_state: Rc> = Rc::new(RefCell::new(EmptyFiles::new())); - let shared_temporary_files_state: Rc> = Rc::new(RefCell::new(Temporary::new())); - let shared_big_files_state: Rc> = Rc::new(RefCell::new(BigFile::new())); - let shared_similar_images_state: Rc> = Rc::new(RefCell::new(SimilarImages::new())); - let shared_zeroed_files_state: Rc> = Rc::new(RefCell::new(ZeroedFiles::new())); - - // State of confirmation dialogs - let shared_confirmation_dialog_delete_dialog_showing_state: Rc> = Rc::new(RefCell::new(true)); - - //////////////////////////////////////////////////////////////////////////////////////////////// - - //// GUI Entry - let entry_similar_images_minimal_size: gtk::Entry = builder.get_object("entry_similar_images_minimal_size").unwrap(); - let entry_duplicate_minimal_size: gtk::Entry = builder.get_object("entry_duplicate_minimal_size").unwrap(); - let entry_allowed_extensions: gtk::Entry = builder.get_object("entry_allowed_extensions").unwrap(); - let entry_excluded_items: gtk::Entry = builder.get_object("entry_excluded_items").unwrap(); - let entry_big_files_number: gtk::Entry = builder.get_object("entry_big_files_number").unwrap(); - - //// GUI Buttons - let buttons_search: gtk::Button = builder.get_object("buttons_search").unwrap(); - let buttons_stop: gtk::Button = builder.get_object("buttons_stop").unwrap(); - let buttons_resume: gtk::Button = builder.get_object("buttons_resume").unwrap(); - let buttons_pause: gtk::Button = builder.get_object("buttons_pause").unwrap(); - let buttons_select: gtk::Button = builder.get_object("buttons_select").unwrap(); - let buttons_delete: gtk::Button = builder.get_object("buttons_delete").unwrap(); - let buttons_save: gtk::Button = builder.get_object("buttons_save").unwrap(); - - let buttons_names = ["search", "stop", "resume", "pause", "select", "delete", "save"]; - let buttons_array = [ - buttons_search.clone(), - buttons_stop.clone(), - buttons_resume.clone(), - buttons_pause.clone(), - buttons_select.clone(), - buttons_delete.clone(), - buttons_save.clone(), - ]; - - let buttons_add_included_directory: gtk::Button = builder.get_object("buttons_add_included_directory").unwrap(); - let buttons_remove_included_directory: gtk::Button = builder.get_object("buttons_remove_included_directory").unwrap(); - let buttons_add_excluded_directory: gtk::Button = builder.get_object("buttons_add_excluded_directory").unwrap(); - let buttons_remove_excluded_directory: gtk::Button = builder.get_object("buttons_remove_excluded_directory").unwrap(); - - // Buttons search popover buttons - let buttons_popover_select_all: gtk::Button = builder.get_object("buttons_popover_select_all").unwrap(); - let buttons_popover_unselect_all: gtk::Button = builder.get_object("buttons_popover_unselect_all").unwrap(); - let buttons_popover_reverse: gtk::Button = builder.get_object("buttons_popover_reverse").unwrap(); - let buttons_popover_select_all_except_oldest: gtk::Button = builder.get_object("buttons_popover_select_all_except_oldest").unwrap(); - let buttons_popover_select_all_except_newest: gtk::Button = builder.get_object("buttons_popover_select_all_except_newest").unwrap(); - let buttons_popover_select_one_oldest: gtk::Button = builder.get_object("buttons_popover_select_one_oldest").unwrap(); - let buttons_popover_select_one_newest: gtk::Button = builder.get_object("buttons_popover_select_one_newest").unwrap(); - - //// Popovers - let popover_select: gtk::Popover = builder.get_object("popover_select").unwrap(); - - //// Check Buttons - let check_button_recursive: gtk::CheckButton = builder.get_object("check_button_recursive").unwrap(); - - //// Radio Buttons - let radio_button_name: gtk::RadioButton = builder.get_object("radio_button_name").unwrap(); - let radio_button_size: gtk::RadioButton = builder.get_object("radio_button_size").unwrap(); - let radio_button_hashmb: gtk::RadioButton = builder.get_object("radio_button_hashmb").unwrap(); - let radio_button_hash: gtk::RadioButton = builder.get_object("radio_button_hash").unwrap(); - - //// Notebooks - let notebook_main: gtk::Notebook = builder.get_object("notebook_main").unwrap(); - let notebook_upper: gtk::Notebook = builder.get_object("notebook_upper").unwrap(); - - let mut notebook_main_children_names: Vec = Vec::new(); - let mut notebook_upper_children_names: Vec = Vec::new(); - - for i in notebook_main.get_children() { - notebook_main_children_names.push(i.get_buildable_name().unwrap().to_string()); - } - for i in notebook_upper.get_children() { - notebook_upper_children_names.push(i.get_buildable_name().unwrap().to_string()); - } - - //// Entry - let entry_info: gtk::Entry = builder.get_object("entry_info").unwrap(); // To show default - - //// Text View - let text_view_errors: gtk::TextView = builder.get_object("text_view_errors").unwrap(); - - //// Scrolled windows - // Main notebook - let scrolled_window_duplicate_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_duplicate_finder").unwrap(); - let scrolled_window_main_empty_folder_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_main_empty_folder_finder").unwrap(); - let scrolled_window_main_empty_files_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_main_empty_files_finder").unwrap(); - let scrolled_window_main_temporary_files_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_main_temporary_files_finder").unwrap(); - let scrolled_window_big_files_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_big_files_finder").unwrap(); - let scrolled_window_similar_images_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_similar_images_finder").unwrap(); - let scrolled_window_zeroed_files_finder: gtk::ScrolledWindow = builder.get_object("scrolled_window_zeroed_files_finder").unwrap(); - - // Upper notebook - let scrolled_window_included_directories: gtk::ScrolledWindow = builder.get_object("scrolled_window_included_directories").unwrap(); - let scrolled_window_excluded_directories: gtk::ScrolledWindow = builder.get_object("scrolled_window_excluded_directories").unwrap(); - - //// Threads - // Types of messages to send to main thread where gui can be draw. - enum Message { - Duplicates(DuplicateFinder), - EmptyFolders(EmptyFolder), - EmptyFiles(EmptyFiles), - BigFiles(BigFile), - Temporary(Temporary), - SimilarImages(SimilarImages), - ZeroedFiles(ZeroedFiles), - } + let gui_data: GuiData = GuiData::new(); // Used for getting data from thread let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); - // Used for sending stop signal to thread - let (sx, rx): (crossbeam_channel::Sender<()>, crossbeam_channel::Receiver<()>) = unbounded(); - - //// Setup default look(duplicate finder) - { - entry_info.set_text("Duplicated Files"); - - // Disable and show buttons - buttons_search.show(); - buttons_save.hide(); - buttons_delete.hide(); - buttons_stop.hide(); - buttons_resume.hide(); - buttons_pause.hide(); - buttons_select.hide(); - - // Set Main Scrolled Window Treeviews - { - // Duplicate Files - { - let col_types: [glib::types::Type; 6] = [ - glib::types::Type::String, - glib::types::Type::String, - glib::types::Type::String, - glib::types::Type::U64, - glib::types::Type::String, - glib::types::Type::String, - ]; - let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); - - let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); - - tree_view.get_selection().set_mode(SelectionMode::Multiple); - tree_view.get_selection().set_select_function(Some(Box::new(select_function_duplicates))); - - create_tree_view_duplicates(&mut tree_view); - - tree_view.connect_button_press_event(opening_double_click_function_duplicates); - - scrolled_window_duplicate_finder.add(&tree_view); - scrolled_window_duplicate_finder.show_all(); - } - // Empty Folders - { - let col_types: [glib::types::Type; 3] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; - let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); - - let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); - - tree_view.get_selection().set_mode(SelectionMode::Multiple); - - create_tree_view_empty_folders(&mut tree_view); - - tree_view.connect_button_press_event(opening_double_click_function_empty_folders); - - scrolled_window_main_empty_folder_finder.add(&tree_view); - scrolled_window_main_empty_folder_finder.show_all(); - } - // Empty Files - { - let col_types: [glib::types::Type; 3] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; - let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); - - let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); - - tree_view.get_selection().set_mode(SelectionMode::Multiple); - - create_tree_view_empty_files(&mut tree_view); - - tree_view.connect_button_press_event(opening_double_click_function_empty_files); - - scrolled_window_main_empty_files_finder.add(&tree_view); - scrolled_window_main_empty_files_finder.show_all(); - } - // Temporary Files - { - let col_types: [glib::types::Type; 3] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; - let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); - - let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); - - tree_view.get_selection().set_mode(SelectionMode::Multiple); - - create_tree_view_temporary_files(&mut tree_view); - - tree_view.connect_button_press_event(opening_double_click_function_temporary_files); - - scrolled_window_main_temporary_files_finder.add(&tree_view); - scrolled_window_main_temporary_files_finder.show_all(); - } - // Big Files - { - let col_types: [glib::types::Type; 4] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; - let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); - - let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); - - tree_view.get_selection().set_mode(SelectionMode::Multiple); - - create_tree_view_big_files(&mut tree_view); - - tree_view.connect_button_press_event(opening_double_click_function_big_files); - - scrolled_window_big_files_finder.add(&tree_view); - scrolled_window_big_files_finder.show_all(); - } - // Similar Images - { - // TODO create maybe open button to support opening multiple files at once - let col_types: [glib::types::Type; 8] = [ - glib::types::Type::String, - glib::types::Type::String, - glib::types::Type::String, - glib::types::Type::String, - glib::types::Type::String, - glib::types::Type::String, - glib::types::Type::String, - glib::types::Type::String, - ]; - let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); - - let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); - - tree_view.get_selection().set_mode(SelectionMode::Multiple); - - create_tree_view_similar_images(&mut tree_view); - - tree_view.connect_button_press_event(opening_double_click_function_similar_images); - - scrolled_window_similar_images_finder.add(&tree_view); - scrolled_window_similar_images_finder.show_all(); - } - // Zeroed Files - { - let col_types: [glib::types::Type; 4] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; - let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); - - let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); - - tree_view.get_selection().set_mode(SelectionMode::Multiple); - - create_tree_view_zeroed_files(&mut tree_view); - - tree_view.connect_button_press_event(opening_double_click_function_zeroed_files); - - scrolled_window_zeroed_files_finder.add(&tree_view); - scrolled_window_zeroed_files_finder.show_all(); - } - } - - // Set Included Directory - { - let col_types: [glib::types::Type; 2] = [glib::types::Type::String, glib::types::Type::String]; - let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); - - let mut tree_view_included_directory: gtk::TreeView = TreeView::with_model(&list_store); - - tree_view_included_directory.get_selection().set_mode(SelectionMode::Single); - - create_tree_view_directories(&mut tree_view_included_directory); - - let col_indices = [0, 1]; - - let current_dir: String = match env::current_dir() { - Ok(t) => t.to_str().unwrap().to_string(), - Err(_) => { - if cfg!(target_family = "unix") { - println!("Failed to read current directory, setting /home instead"); - "/home".to_string() - } else if cfg!(target_family = "windows") { - println!("Failed to read current directory, setting C:\\ instead"); - "C:\\".to_string() - } else { - "".to_string() - } - } - }; - - let values: [&dyn ToValue; 2] = [¤t_dir, &(MAIN_ROW_COLOR.to_string())]; - list_store.set(&list_store.append(), &col_indices, &values); - - scrolled_window_included_directories.add(&tree_view_included_directory); - scrolled_window_included_directories.show_all(); - } - // Set Excluded Directory - { - let col_types: [glib::types::Type; 2] = [glib::types::Type::String, glib::types::Type::String]; - let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); - - let mut tree_view_excluded_directory: gtk::TreeView = TreeView::with_model(&list_store); - - tree_view_excluded_directory.get_selection().set_mode(SelectionMode::Single); - - create_tree_view_directories(&mut tree_view_excluded_directory); - - 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); - } - - scrolled_window_excluded_directories.add(&tree_view_excluded_directory); - scrolled_window_excluded_directories.show_all(); - } - // Set Excluded Items - { - if cfg!(target_family = "unix") { - entry_excluded_items.set_text("*/.git/*,*/node_modules/*,*/lost+found/*"); - } - if cfg!(target_family = "windows") { - entry_excluded_items.set_text("*/.git/*,*/node_modules/*,*/lost+found/*,*:/windows/*"); - } - } - } - - // Connecting events - { - // 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| { - 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"; - } - "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", - "notebook_main_similar_images_finder_label" => page = "similar_images", - "notebook_main_zeroed_files_finder" => page = "zeroed_files", - e => { - panic!("Not existent page {}", e); - } - }; - // Buttons - set_buttons(&mut *shared_buttons.borrow_mut().get_mut(page).unwrap(), &buttons_array, &buttons_names); - // Upper notebook - { - //let upper_notebooks_labels = [/*"general",*/"included_directories","excluded_directories","excluded_items","allowed_extensions"]; - let mut hashmap: HashMap<&str, &str> = Default::default(); - //hashmap.insert("notebook_upper_general","general"); - hashmap.insert("notebook_upper_included_directories", "included_directories"); - hashmap.insert("notebook_upper_excluded_directories", "excluded_directories"); - hashmap.insert("notebook_upper_excluded_items", "excluded_items"); - hashmap.insert("notebook_upper_allowed_extensions", "allowed_extensions"); - - for tab in ¬ebook_upper_children_names { - let name = hashmap.get(tab.as_str()).unwrap().to_string(); - let index = upper_notebooks_labels.iter().position(|&x| x == name).unwrap(); - if *shared_upper_notebooks.borrow_mut().get_mut(page).unwrap().get_mut(&name).unwrap() { - notebook_upper.get_children().get(index).unwrap().show(); - } else { - notebook_upper.get_children().get(index).unwrap().hide(); - } - } - } - }); - } - - // Connect double click - - //// Connect Buttons - - // Main buttons - { - // Search button - { - let entry_info = entry_info.clone(); - let notebook_main_children_names = notebook_main_children_names.clone(); - let notebook_main = notebook_main.clone(); - let scrolled_window_included_directories = scrolled_window_included_directories.clone(); - let scrolled_window_excluded_directories = scrolled_window_excluded_directories.clone(); - let buttons_search_clone = buttons_search.clone(); - let buttons_array = buttons_array.clone(); - buttons_search_clone.connect_clicked(move |_| { - let included_directories = get_string_from_list_store(&scrolled_window_included_directories); - let excluded_directories = get_string_from_list_store(&scrolled_window_excluded_directories); - let recursive_search = check_button_recursive.get_active(); - let excluded_items = entry_excluded_items.get_text().as_str().to_string(); - let allowed_extensions = entry_allowed_extensions.get_text().as_str().to_string(); - - hide_all_buttons_except("stop", &buttons_array, &buttons_names); - - // Disable main notebook from any iteraction until search will end - notebook_main.set_sensitive(false); - - entry_info.set_text("Searching data, it may take a while, please wait..."); - - match notebook_main_children_names.get(notebook_main.get_current_page().unwrap() as usize).unwrap().as_str() { - "notebook_main_duplicate_finder_label" => { - let check_method; - if radio_button_name.get_active() { - check_method = duplicate::CheckingMethod::Name; - } else if radio_button_size.get_active() { - check_method = duplicate::CheckingMethod::Size; - } else if radio_button_hashmb.get_active() { - check_method = duplicate::CheckingMethod::HashMB; - } else if radio_button_hash.get_active() { - check_method = duplicate::CheckingMethod::Hash; - } else { - panic!("No radio button is pressed"); - } - let minimal_file_size = match entry_duplicate_minimal_size.get_text().as_str().parse::() { - Ok(t) => t, - Err(_) => 1024, // By default - }; - let delete_method = duplicate::DeleteMethod::None; - - let sender = sender.clone(); - let receiver_stop = rx.clone(); - // Find duplicates - thread::spawn(move || { - let mut df = DuplicateFinder::new(); - df.set_included_directory(included_directories); - df.set_excluded_directory(excluded_directories); - df.set_recursive_search(recursive_search); - df.set_excluded_items(excluded_items); - 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)); //&rc_stop_signal.borrow().1); - let _ = sender.send(Message::Duplicates(df)); - }); - } - "scrolled_window_main_empty_folder_finder" => { - let sender = sender.clone(); - let receiver_stop = rx.clone(); - - // Find empty folders - thread::spawn(move || { - let mut ef = EmptyFolder::new(); - ef.set_included_directory(included_directories); - ef.set_delete_folder(false); - 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(); - - // Find empty files - thread::spawn(move || { - let mut vf = EmptyFiles::new(); - - vf.set_included_directory(included_directories); - vf.set_excluded_directory(excluded_directories); - vf.set_recursive_search(recursive_search); - vf.set_excluded_items(excluded_items); - vf.set_allowed_extensions(allowed_extensions); - vf.find_empty_files(Option::from(&receiver_stop)); - let _ = sender.send(Message::EmptyFiles(vf)); - }); - } - "scrolled_window_main_temporary_files_finder" => { - let sender = sender.clone(); - let receiver_stop = rx.clone(); - - // Find temporary files - thread::spawn(move || { - let mut tf = Temporary::new(); - - tf.set_included_directory(included_directories); - tf.set_excluded_directory(excluded_directories); - tf.set_recursive_search(recursive_search); - tf.set_excluded_items(excluded_items); - tf.find_temporary_files(Option::from(&receiver_stop)); - let _ = sender.send(Message::Temporary(tf)); - }); - } - "notebook_big_main_file_finder" => { - let numbers_of_files_to_check = match entry_big_files_number.get_text().as_str().parse::() { - Ok(t) => t, - Err(_) => 50, // By default - }; - - let sender = sender.clone(); - let receiver_stop = rx.clone(); - - // Find big files - thread::spawn(move || { - let mut bf = BigFile::new(); - - bf.set_included_directory(included_directories); - bf.set_excluded_directory(excluded_directories); - bf.set_recursive_search(recursive_search); - bf.set_excluded_items(excluded_items); - bf.set_number_of_files_to_check(numbers_of_files_to_check); - bf.find_big_files(Option::from(&receiver_stop)); - let _ = sender.send(Message::BigFiles(bf)); - }); - } - - "notebook_main_similar_images_finder_label" => { - let sender = sender.clone(); - let receiver_stop = rx.clone(); - - let minimal_file_size = match entry_similar_images_minimal_size.get_text().as_str().parse::() { - Ok(t) => t, - Err(_) => 1024 * 16, // By default - }; - - // Find similar images - thread::spawn(move || { - let mut sf = SimilarImages::new(); - - sf.set_included_directory(included_directories); - sf.set_excluded_directory(excluded_directories); - sf.set_recursive_search(recursive_search); - sf.set_excluded_items(excluded_items); - sf.set_minimal_file_size(minimal_file_size); - sf.find_similar_images(Option::from(&receiver_stop)); - let _ = sender.send(Message::SimilarImages(sf)); - }); - } - "notebook_main_zeroed_files_finder" => { - let sender = sender.clone(); - let receiver_stop = rx.clone(); - - // Find temporary files - thread::spawn(move || { - let mut zf = ZeroedFiles::new(); - - zf.set_included_directory(included_directories); - zf.set_excluded_directory(excluded_directories); - zf.set_recursive_search(recursive_search); - zf.set_excluded_items(excluded_items); - zf.find_zeroed_files(Option::from(&receiver_stop)); - let _ = sender.send(Message::ZeroedFiles(zf)); - }); - } - e => panic!("Not existent {}", e), - } - }); - } - // Delete button - { - let scrolled_window_duplicate_finder = scrolled_window_duplicate_finder.clone(); - let text_view_errors = text_view_errors.clone(); - let notebook_main_children_names = notebook_main_children_names.clone(); - let notebook_main = notebook_main.clone(); - let window_main = window_main.clone(); - let scrolled_window_main_empty_folder_finder = scrolled_window_main_empty_folder_finder.clone(); - let scrolled_window_big_files_finder = scrolled_window_big_files_finder.clone(); - let scrolled_window_main_empty_files_finder = scrolled_window_main_empty_files_finder.clone(); - let scrolled_window_main_temporary_files_finder = scrolled_window_main_temporary_files_finder.clone(); - let scrolled_window_similar_images_finder = scrolled_window_similar_images_finder.clone(); - let scrolled_window_zeroed_files_finder = scrolled_window_zeroed_files_finder.clone(); - - buttons_delete.connect_clicked(move |_| { - if *shared_confirmation_dialog_delete_dialog_showing_state.borrow_mut() { - let confirmation_dialog_delete = gtk::Dialog::with_buttons( - Option::from("Delete confirmation"), - Option::from(&window_main), - gtk::DialogFlags::MODAL, - &[("Ok", gtk::ResponseType::Ok), ("Close", gtk::ResponseType::Cancel)], - ); - let label: gtk::Label = gtk::Label::new(Some("Are you sure that you want to delete files?")); - let check_button: gtk::CheckButton = gtk::CheckButton::with_label("Ask in future"); - check_button.set_active(true); - - for widgets in confirmation_dialog_delete.get_children() { - // By default GtkBox is child of dialog, so we can easily add other things to it - widgets.clone().downcast::().unwrap().add(&label); - widgets.downcast::().unwrap().add(&check_button); - } - - confirmation_dialog_delete.show_all(); - - let response_type = confirmation_dialog_delete.run(); - if response_type == gtk::ResponseType::Ok { - if !check_button.get_active() { - *shared_confirmation_dialog_delete_dialog_showing_state.borrow_mut() = false; - } - } else { - confirmation_dialog_delete.close(); - return; - } - confirmation_dialog_delete.close(); - } - - 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(); - } - "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(); - - 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(), 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) { - 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() - } - } - - 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(); - } - e => panic!("Not existent {}", e), - } - }); - } - // Select button - { - let notebook_main_children_names = notebook_main_children_names.clone(); - let notebook_main = notebook_main.clone(); - let buttons_select_clone = buttons_select.clone(); - let popover_select = popover_select.clone(); - buttons_select_clone.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" => { - // Only popup popup - popover_select.set_relative_to(Some(&buttons_select)); - popover_select.popup(); - } - e => panic!("Not existent {}", e), - }); - } - // Save button - { - let shared_buttons = shared_buttons.clone(); - let buttons_save_clone = buttons_save.clone(); - let entry_info = entry_info.clone(); - let shared_duplication_state = shared_duplication_state.clone(); - let shared_empty_folders_state = shared_empty_folders_state.clone(); - let shared_big_files_state = shared_big_files_state.clone(); - let shared_temporary_files_state = shared_temporary_files_state.clone(); - let shared_empty_files_state = shared_empty_files_state.clone(); - let shared_similar_images_state = shared_similar_images_state.clone(); - let shared_zeroed_files_state = shared_zeroed_files_state.clone(); - let notebook_main = 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() { - "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); - - 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; - } - } - "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); - - 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; - } - } - "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); - - 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; - } - } - "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); - - 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; - } - } - "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); - - 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; - } - } - "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); - - 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; - } - } - "notebook_main_zeroed_files_finder_label" => { - let file_name = "results_zeroed_files.txt"; - - let mut zf = shared_zeroed_files_state.borrow_mut(); - zf.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; - } - } - e => panic!("Not existent {}", e), - }); - } - // Stop button - { - buttons_stop.connect_clicked(move |_| { - sx.send(()).unwrap(); - }); - } - } - // Popover Buttons - { - // Select all button - { - let scrolled_window_duplicate_finder = scrolled_window_duplicate_finder.clone(); - let popover_select = popover_select.clone(); - buttons_popover_select_all.connect_clicked(move |_| { - let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let selection = tree_view.get_selection(); - - selection.select_all(); - popover_select.popdown(); - }); - } - - // Unselect all button - { - let scrolled_window_duplicate_finder = scrolled_window_duplicate_finder.clone(); - let popover_select = popover_select.clone(); - buttons_popover_unselect_all.connect_clicked(move |_| { - let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let selection = tree_view.get_selection(); - - selection.unselect_all(); - popover_select.popdown(); - }); - } - - // Reverse selection - { - let scrolled_window_duplicate_finder = scrolled_window_duplicate_finder.clone(); - let popover_select = popover_select.clone(); - buttons_popover_reverse.connect_clicked(move |_| { - let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let selection = tree_view.get_selection(); - - let (vector_tree_path, tree_model) = selection.get_selected_rows(); - - if vector_tree_path.is_empty() { - selection.select_all(); - } else { - let tree_iter_all = tree_model.get_iter_first().unwrap(); // Never should be available button where there is no available records - - let mut current_path_index = 0; - let mut tree_iter_selected: TreeIter; - loop { - if current_path_index >= vector_tree_path.len() { - selection.select_iter(&tree_iter_all); - } else { - tree_iter_selected = tree_model.get_iter(vector_tree_path.get(current_path_index).unwrap()).unwrap(); - if tree_model.get_path(&tree_iter_all).unwrap() == tree_model.get_path(&tree_iter_selected).unwrap() { - selection.unselect_iter(&tree_iter_selected); - current_path_index += 1; - } else { - selection.select_iter(&tree_iter_all); - } - } - if !tree_model.iter_next(&tree_iter_all) { - break; - } - } - } - - popover_select.popdown(); - }); - } - - // All except oldest - { - let scrolled_window_duplicate_finder = scrolled_window_duplicate_finder.clone(); - let popover_select = popover_select.clone(); - buttons_popover_select_all_except_oldest.connect_clicked(move |_| { - let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let selection = tree_view.get_selection(); - let tree_model = tree_view.get_model().unwrap(); - - let tree_iter_all = tree_model.get_iter_first().unwrap(); // Never should be available button where there is no available records - - let mut end: bool = false; - - loop { - let mut tree_iter_array: Vec = Vec::new(); - let mut oldest_index: Option = None; - let mut current_index: usize = 0; - let mut oldest_modification_time: u64 = u64::max_value(); - - loop { - let color = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::Color as i32).get::().unwrap().unwrap(); - if color == HEADER_ROW_COLOR { - if !tree_model.iter_next(&tree_iter_all) { - end = true; - } - break; - } - tree_iter_array.push(tree_iter_all.clone()); - let modification = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::ModificationAsSecs as i32).get::().unwrap().unwrap(); - if modification < oldest_modification_time { - oldest_modification_time = modification; - oldest_index = Some(current_index); - } - - current_index += 1; - - if !tree_model.iter_next(&tree_iter_all) { - end = true; - break; - } - } - if oldest_index == None { - continue; - } - for (index, tree_iter) in tree_iter_array.iter().enumerate() { - if index != oldest_index.unwrap() { - selection.select_iter(tree_iter); - } else { - selection.unselect_iter(tree_iter); - } - } - - if end { - break; - } - } - - popover_select.popdown(); - }); - } - - // All except newest - { - let scrolled_window_duplicate_finder = scrolled_window_duplicate_finder.clone(); - let popover_select = popover_select.clone(); - buttons_popover_select_all_except_newest.connect_clicked(move |_| { - let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let selection = tree_view.get_selection(); - let tree_model = tree_view.get_model().unwrap(); - - let tree_iter_all = tree_model.get_iter_first().unwrap(); // Never should be available button where there is no available records - - let mut end: bool = false; - - loop { - let mut tree_iter_array: Vec = Vec::new(); - let mut newest_index: Option = None; - let mut current_index: usize = 0; - let mut newest_modification_time: u64 = 0; - - loop { - let color = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::Color as i32).get::().unwrap().unwrap(); - if color == HEADER_ROW_COLOR { - if !tree_model.iter_next(&tree_iter_all) { - end = true; - } - break; - } - tree_iter_array.push(tree_iter_all.clone()); - let modification = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::ModificationAsSecs as i32).get::().unwrap().unwrap(); - if modification > newest_modification_time { - newest_modification_time = modification; - newest_index = Some(current_index); - } - - current_index += 1; - - if !tree_model.iter_next(&tree_iter_all) { - end = true; - break; - } - } - if newest_index == None { - continue; - } - for (index, tree_iter) in tree_iter_array.iter().enumerate() { - if index != newest_index.unwrap() { - selection.select_iter(tree_iter); - } else { - selection.unselect_iter(tree_iter); - } - } - - if end { - break; - } - } - - popover_select.popdown(); - }); - } - - // All one oldest - { - let scrolled_window_duplicate_finder = scrolled_window_duplicate_finder.clone(); - let popover_select = popover_select.clone(); - buttons_popover_select_one_oldest.connect_clicked(move |_| { - let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let selection = tree_view.get_selection(); - let tree_model = tree_view.get_model().unwrap(); - - let tree_iter_all = tree_model.get_iter_first().unwrap(); // Never should be available button where there is no available records - - let mut end: bool = false; - - loop { - let mut tree_iter_array: Vec = Vec::new(); - let mut oldest_index: Option = None; - let mut current_index: usize = 0; - let mut oldest_modification_time: u64 = u64::max_value(); - - loop { - let color = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::Color as i32).get::().unwrap().unwrap(); - if color == HEADER_ROW_COLOR { - if !tree_model.iter_next(&tree_iter_all) { - end = true; - } - break; - } - tree_iter_array.push(tree_iter_all.clone()); - let modification = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::ModificationAsSecs as i32).get::().unwrap().unwrap(); - if modification < oldest_modification_time { - oldest_modification_time = modification; - oldest_index = Some(current_index); - } - - current_index += 1; - - if !tree_model.iter_next(&tree_iter_all) { - end = true; - break; - } - } - if oldest_index == None { - continue; - } - for (index, tree_iter) in tree_iter_array.iter().enumerate() { - if index == oldest_index.unwrap() { - selection.select_iter(tree_iter); - } else { - selection.unselect_iter(tree_iter); - } - } - - if end { - break; - } - } - - popover_select.popdown(); - }); - } - // All one newest - { - let scrolled_window_duplicate_finder = scrolled_window_duplicate_finder.clone(); - buttons_popover_select_one_newest.connect_clicked(move |_| { - let tree_view = scrolled_window_duplicate_finder.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let selection = tree_view.get_selection(); - let tree_model = tree_view.get_model().unwrap(); - - let tree_iter_all = tree_model.get_iter_first().unwrap(); // Never should be available button where there is no available records - - let mut end: bool = false; - - loop { - let mut tree_iter_array: Vec = Vec::new(); - let mut newest_index: Option = None; - let mut current_index: usize = 0; - let mut newest_modification_time: u64 = 0; - - loop { - let color = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::Color as i32).get::().unwrap().unwrap(); - if color == HEADER_ROW_COLOR { - if !tree_model.iter_next(&tree_iter_all) { - end = true; - } - break; - } - tree_iter_array.push(tree_iter_all.clone()); - let modification = tree_model.get_value(&tree_iter_all, ColumnsDuplicates::ModificationAsSecs as i32).get::().unwrap().unwrap(); - if modification > newest_modification_time { - newest_modification_time = modification; - newest_index = Some(current_index); - } - - current_index += 1; - - if !tree_model.iter_next(&tree_iter_all) { - end = true; - break; - } - } - if newest_index == None { - continue; - } - for (index, tree_iter) in tree_iter_array.iter().enumerate() { - if index == newest_index.unwrap() { - selection.select_iter(tree_iter); - } else { - selection.unselect_iter(tree_iter); - } - } - - if end { - break; - } - } - - popover_select.popdown(); - }); - } - } - // Upper Notebook - { - // Add included directory - { - let scrolled_window_included_directories = scrolled_window_included_directories.clone(); - let window_main = window_main.clone(); - buttons_add_included_directory.connect_clicked(move |_| { - let chooser = gtk::FileChooserDialog::with_buttons( - Option::from("Folders to include"), - Option::from(&window_main), - gtk::FileChooserAction::SelectFolder, - &[("Ok", gtk::ResponseType::Ok), ("Close", gtk::ResponseType::Cancel)], - ); - chooser.show_all(); - let response_type = chooser.run(); - if response_type == gtk::ResponseType::Ok { - let folder = chooser.get_filename().unwrap().to_str().unwrap().to_string(); - - let tree_view = scrolled_window_included_directories.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let list_store = tree_view.get_model().unwrap().downcast::().unwrap(); - - let col_indices = [0]; - - let values: [&dyn ToValue; 1] = [&folder]; - list_store.set(&list_store.append(), &col_indices, &values); - } - chooser.close(); - }); - } - // Add excluded directory - { - let scrolled_window_excluded_directories = scrolled_window_excluded_directories.clone(); - let window_main = window_main.clone(); - buttons_add_excluded_directory.connect_clicked(move |_| { - let chooser = gtk::FileChooserDialog::with_buttons( - Option::from("Folders to exclude"), - Option::from(&window_main), - gtk::FileChooserAction::SelectFolder, - &[("Ok", gtk::ResponseType::Ok), ("Close", gtk::ResponseType::Cancel)], - ); - chooser.show_all(); - let response_type = chooser.run(); - if response_type == gtk::ResponseType::Ok { - let folder = chooser.get_filename().unwrap().to_str().unwrap().to_string(); - - let tree_view = scrolled_window_excluded_directories.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let list_store = tree_view.get_model().unwrap().downcast::().unwrap(); - - let col_indices = [0]; - - let values: [&dyn ToValue; 1] = [&folder]; - list_store.set(&list_store.append(), &col_indices, &values); - } - chooser.close(); - }); - } - // Remove Excluded Folder - { - buttons_remove_excluded_directory.connect_clicked(move |_| { - let tree_view = scrolled_window_excluded_directories.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let list_store = tree_view.get_model().unwrap().downcast::().unwrap(); - let selection = tree_view.get_selection(); - - let (_, tree_iter) = match selection.get_selected() { - Some(t) => t, - None => { - return; - } - }; - list_store.remove(&tree_iter); - }); - } - // Remove Included Folder - { - buttons_remove_included_directory.connect_clicked(move |_| { - let tree_view = scrolled_window_included_directories.get_children().get(0).unwrap().clone().downcast::().unwrap(); - let list_store = tree_view.get_model().unwrap().downcast::().unwrap(); - let selection = tree_view.get_selection(); - - let (_, tree_iter) = match selection.get_selected() { - Some(t) => t, - None => { - return; - } - }; - list_store.remove(&tree_iter); - }); - } - } - } - - // Wait for ending of search: - // Unblock left notebook bar - // Show proper buttons - receiver.attach(None, move |msg| { - buttons_search.show(); - buttons_stop.hide(); - - // Restore clickability to main notebook - notebook_main.set_sensitive(true); - - match msg { - Message::Duplicates(df) => { - if df.get_stopped_search() { - entry_info.set_text("Searching for duplicated was stopped by user"); - - //Also clear list - scrolled_window_duplicate_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap() - .clear(); - } else { - let information = df.get_information(); - let text_messages = df.get_text_messages(); - - let duplicates_number: usize; - let duplicates_size: u64; - let duplicates_group: usize; - - match df.get_check_method() { - CheckingMethod::Name => { - duplicates_number = information.number_of_duplicated_files_by_name; - duplicates_size = 0; - duplicates_group = information.number_of_groups_by_name; - entry_info.set_text(format!("Found {} files in {} groups which have same names.", duplicates_number, duplicates_group).as_str()); - } - CheckingMethod::Hash | CheckingMethod::HashMB => { - duplicates_number = information.number_of_duplicated_files_by_hash; - duplicates_size = information.lost_space_by_hash; - duplicates_group = information.number_of_groups_by_hash; - entry_info.set_text(format!("Found {} duplicates files in {} groups which took {}.", duplicates_number, duplicates_group, duplicates_size.file_size(options::BINARY).unwrap()).as_str()); - } - CheckingMethod::Size => { - duplicates_number = information.number_of_duplicated_files_by_size; - duplicates_size = information.lost_space_by_size; - duplicates_group = information.number_of_groups_by_size; - entry_info.set_text(format!("Found {} duplicates files in {} groups which took {}.", duplicates_number, duplicates_group, duplicates_size.file_size(options::BINARY).unwrap()).as_str()); - } - CheckingMethod::None => { - panic!(); - } - } - - entry_info.set_text(format!("Found {} duplicates files in {} groups which took {}.", duplicates_number, duplicates_group, duplicates_size.file_size(options::BINARY).unwrap()).as_str()); - - // Create GUI - { - let list_store = scrolled_window_duplicate_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap(); - list_store.clear(); - - let col_indices = [0, 1, 2, 3, 4, 5]; - - match df.get_check_method() { - CheckingMethod::Name => { - let btreemap = df.get_files_sorted_by_names(); - - for (name, vector) in btreemap.iter().rev() { - let values: [&dyn ToValue; 6] = [ - &name, - &(format!("{} results", vector.len())), - &"".to_string(), // No text in 3 column - &(0), // Not used here - &(HEADER_ROW_COLOR.to_string()), - &(TEXT_COLOR.to_string()), - ]; - list_store.set(&list_store.append(), &col_indices, &values); - for entry in vector { - let (directory, file) = split_path(&entry.path); - let values: [&dyn ToValue; 6] = [ - &file, - &directory, - &(format!("{} - ({})", NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string(), entry.size.file_size(options::BINARY).unwrap())), - &(entry.modified_date), - &(MAIN_ROW_COLOR.to_string()), - &(TEXT_COLOR.to_string()), - ]; - list_store.set(&list_store.append(), &col_indices, &values); - } - } - } - CheckingMethod::Hash | CheckingMethod::HashMB => { - let btreemap = df.get_files_sorted_by_hash(); - - for (size, vectors_vector) in btreemap.iter().rev() { - for vector in vectors_vector { - let values: [&dyn ToValue; 6] = [ - &(format!("{} x {} ({} bytes)", vector.len(), size.file_size(options::BINARY).unwrap(), size)), - &(format!("{} ({} bytes) lost", ((vector.len() - 1) as u64 * *size as u64).file_size(options::BINARY).unwrap(), (vector.len() - 1) as u64 * *size as u64)), - &"".to_string(), // No text in 3 column - &(0), // Not used here - &(HEADER_ROW_COLOR.to_string()), - &(TEXT_COLOR.to_string()), - ]; - list_store.set(&list_store.append(), &col_indices, &values); - for entry in vector { - let (directory, file) = split_path(&entry.path); - let values: [&dyn ToValue; 6] = [ - &file, - &directory, - &(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string()), - &(entry.modified_date), - &(MAIN_ROW_COLOR.to_string()), - &(TEXT_COLOR.to_string()), - ]; - list_store.set(&list_store.append(), &col_indices, &values); - } - } - } - } - CheckingMethod::Size => { - let btreemap = df.get_files_sorted_by_size(); - - for (size, vector) in btreemap.iter().rev() { - let values: [&dyn ToValue; 6] = [ - &(format!("{} x {} ({} bytes)", vector.len(), size.file_size(options::BINARY).unwrap(), size)), - &(format!("{} ({} bytes) lost", ((vector.len() - 1) as u64 * *size as u64).file_size(options::BINARY).unwrap(), (vector.len() - 1) as u64 * *size as u64)), - &"".to_string(), // No text in 3 column - &(0), // Not used here - &(HEADER_ROW_COLOR.to_string()), - &(TEXT_COLOR.to_string()), - ]; - list_store.set(&list_store.append(), &col_indices, &values); - for entry in vector { - let (directory, file) = split_path(&entry.path); - let values: [&dyn ToValue; 6] = [ - &file, - &directory, - &(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string()), - &(entry.modified_date), - &(MAIN_ROW_COLOR.to_string()), - &(TEXT_COLOR.to_string()), - ]; - list_store.set(&list_store.append(), &col_indices, &values); - } - } - } - CheckingMethod::None => { - panic!(); - } - } - - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_duplication_state.borrow_mut() = df; - - if duplicates_number > 0 { - *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("save").unwrap() = true; - *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("delete").unwrap() = true; - *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("select").unwrap() = true; - } else { - *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("save").unwrap() = false; - *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("delete").unwrap() = false; - *shared_buttons.borrow_mut().get_mut("duplicate").unwrap().get_mut("select").unwrap() = false; - } - set_buttons(&mut *shared_buttons.borrow_mut().get_mut("duplicate").unwrap(), &buttons_array, &buttons_names); - } - } - } - Message::EmptyFolders(ef) => { - if ef.get_stopped_search() { - entry_info.set_text("Searching for empty folders was stopped by user"); - - //Also clear list - scrolled_window_main_empty_folder_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap() - .clear(); - } else { - let information = ef.get_information(); - let text_messages = ef.get_text_messages(); - - let empty_folder_number: usize = information.number_of_empty_folders; - - entry_info.set_text(format!("Found {} empty folders.", empty_folder_number).as_str()); - - // Create GUI - { - let list_store = scrolled_window_main_empty_folder_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap(); - list_store.clear(); - - let col_indices = [0, 1, 2]; - - let hashmap = ef.get_empty_folder_list(); - - for (path, entry) in hashmap { - let (directory, file) = split_path(path); - let values: [&dyn ToValue; 3] = [&file, &directory, &(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string())]; - list_store.set(&list_store.append(), &col_indices, &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_empty_folders_state.borrow_mut() = ef; - - if empty_folder_number > 0 { - *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap().get_mut("save").unwrap() = true; - *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap().get_mut("delete").unwrap() = true; - } else { - *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap().get_mut("save").unwrap() = false; - *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap().get_mut("delete").unwrap() = false; - } - set_buttons(&mut *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap(), &buttons_array, &buttons_names); - } - } - } - Message::EmptyFiles(vf) => { - if vf.get_stopped_search() { - entry_info.set_text("Searching for empty files was stopped by user"); - - //Also clear list - scrolled_window_main_empty_files_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap() - .clear(); - } else { - let information = vf.get_information(); - let text_messages = vf.get_text_messages(); - - let empty_files_number: usize = information.number_of_empty_files; - - entry_info.set_text(format!("Found {} empty files.", empty_files_number).as_str()); - - // Create GUI - { - let list_store = scrolled_window_main_empty_files_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap(); - list_store.clear(); - - let col_indices = [0, 1, 2]; - - let vector = vf.get_empty_files(); - - for file_entry in vector { - let (directory, file) = split_path(&file_entry.path); - let values: [&dyn ToValue; 3] = [&file, &directory, &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string())]; - list_store.set(&list_store.append(), &col_indices, &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_empty_files_state.borrow_mut() = vf; - - if empty_files_number > 0 { - *shared_buttons.borrow_mut().get_mut("empty_file").unwrap().get_mut("save").unwrap() = true; - *shared_buttons.borrow_mut().get_mut("empty_file").unwrap().get_mut("delete").unwrap() = true; - } else { - *shared_buttons.borrow_mut().get_mut("empty_file").unwrap().get_mut("save").unwrap() = false; - *shared_buttons.borrow_mut().get_mut("empty_file").unwrap().get_mut("delete").unwrap() = false; - } - set_buttons(&mut *shared_buttons.borrow_mut().get_mut("empty_file").unwrap(), &buttons_array, &buttons_names); - } - } - } - Message::BigFiles(bf) => { - if bf.get_stopped_search() { - entry_info.set_text("Searching for big files was stopped by user"); - - //Also clear list - scrolled_window_duplicate_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap() - .clear(); - } else { - let information = bf.get_information(); - let text_messages = bf.get_text_messages(); - - let biggest_files_number: usize = information.number_of_real_files; - - entry_info.set_text(format!("Found {} biggest files.", biggest_files_number).as_str()); - - // Create GUI - { - let list_store = scrolled_window_big_files_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap(); - list_store.clear(); - - let col_indices = [0, 1, 2, 3]; - - let btreemap = bf.get_big_files(); - - for (size, vector) in btreemap.iter().rev() { - for file_entry in vector { - let (directory, file) = split_path(&file_entry.path); - let values: [&dyn ToValue; 4] = [ - &(format!("{} ({} bytes)", size.file_size(options::BINARY).unwrap(), size)), - &file, - &directory, - &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()), - ]; - list_store.set(&list_store.append(), &col_indices, &values); - } - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_big_files_state.borrow_mut() = bf; - - if biggest_files_number > 0 { - *shared_buttons.borrow_mut().get_mut("big_file").unwrap().get_mut("save").unwrap() = true; - *shared_buttons.borrow_mut().get_mut("big_file").unwrap().get_mut("delete").unwrap() = true; - } else { - *shared_buttons.borrow_mut().get_mut("big_file").unwrap().get_mut("save").unwrap() = false; - *shared_buttons.borrow_mut().get_mut("big_file").unwrap().get_mut("delete").unwrap() = false; - } - set_buttons(&mut *shared_buttons.borrow_mut().get_mut("big_file").unwrap(), &buttons_array, &buttons_names); - } - } - } - Message::Temporary(tf) => { - if tf.get_stopped_search() { - entry_info.set_text("Searching for temporary files was stopped by user"); - - //Also clear list - scrolled_window_duplicate_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap() - .clear(); - } else { - let information = tf.get_information(); - let text_messages = tf.get_text_messages(); - - let temporary_files_number: usize = information.number_of_temporary_files; - - entry_info.set_text(format!("Found {} temporary files.", temporary_files_number).as_str()); - - // Create GUI - { - let list_store = scrolled_window_main_temporary_files_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap(); - list_store.clear(); - - let col_indices = [0, 1, 2]; - - let vector = tf.get_temporary_files(); - - for file_entry in vector { - let (directory, file) = split_path(&file_entry.path); - let values: [&dyn ToValue; 3] = [&file, &directory, &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string())]; - list_store.set(&list_store.append(), &col_indices, &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_temporary_files_state.borrow_mut() = tf; - - if temporary_files_number > 0 { - *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap().get_mut("save").unwrap() = true; - *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap().get_mut("delete").unwrap() = true; - } else { - *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap().get_mut("save").unwrap() = false; - *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap().get_mut("delete").unwrap() = false; - } - set_buttons(&mut *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap(), &buttons_array, &buttons_names); - } - } - } - Message::SimilarImages(sf) => { - if sf.get_stopped_search() { - entry_info.set_text("Searching for duplicated was stopped by user"); - - //Also clear list - scrolled_window_duplicate_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap() - .clear(); - } else { - //let information = sf.get_information(); - let text_messages = sf.get_text_messages(); - - let base_images_size = sf.get_similar_images().len(); - - entry_info.set_text(format!("Found similar pictures for {} images.", base_images_size).as_str()); - - // Create GUI - { - let list_store = scrolled_window_similar_images_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap(); - list_store.clear(); - - let col_indices = [0, 1, 2, 3, 4, 5, 6, 7]; - - let vec_struct_similar = sf.get_similar_images(); - - for struct_similar in vec_struct_similar.iter() { - // Header - let (directory, file) = split_path(&struct_similar.base_image.path); - let values: [&dyn ToValue; 8] = [ - &(get_text_from_similarity(&struct_similar.base_image.similarity).to_string()), - &struct_similar.base_image.size.file_size(options::BINARY).unwrap(), - &struct_similar.base_image.dimensions, - &file, - &directory, - &(NaiveDateTime::from_timestamp(struct_similar.base_image.modified_date as i64, 0).to_string()), - &(HEADER_ROW_COLOR.to_string()), - &(TEXT_COLOR.to_string()), - ]; - list_store.set(&list_store.append(), &col_indices, &values); - - // Meat - for similar_images in &struct_similar.similar_images { - let (directory, file) = split_path(&similar_images.path); - let values: [&dyn ToValue; 8] = [ - &(get_text_from_similarity(&similar_images.similarity).to_string()), - &similar_images.size.file_size(options::BINARY).unwrap(), - &similar_images.dimensions, - &file, - &directory, - &(NaiveDateTime::from_timestamp(similar_images.modified_date as i64, 0).to_string()), - &(MAIN_ROW_COLOR.to_string()), - &(TEXT_COLOR.to_string()), - ]; - list_store.set(&list_store.append(), &col_indices, &values); - } - } - - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_similar_images_state.borrow_mut() = sf; - - if base_images_size > 0 { - *shared_buttons.borrow_mut().get_mut("similar_images").unwrap().get_mut("save").unwrap() = true; - *shared_buttons.borrow_mut().get_mut("similar_images").unwrap().get_mut("delete").unwrap() = true; - } else { - *shared_buttons.borrow_mut().get_mut("similar_images").unwrap().get_mut("save").unwrap() = false; - *shared_buttons.borrow_mut().get_mut("similar_images").unwrap().get_mut("delete").unwrap() = false; - } - set_buttons(&mut *shared_buttons.borrow_mut().get_mut("similar_images").unwrap(), &buttons_array, &buttons_names); - } - } - } - - Message::ZeroedFiles(zf) => { - if zf.get_stopped_search() { - entry_info.set_text("Searching for zeroed files was stopped by user"); - - //Also clear list - scrolled_window_zeroed_files_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap() - .clear(); - } else { - let information = zf.get_information(); - let text_messages = zf.get_text_messages(); - - let zeroed_files_number: usize = information.number_of_zeroed_files; - - entry_info.set_text(format!("Found {} zeroed files.", zeroed_files_number).as_str()); - - // Create GUI - { - let list_store = scrolled_window_zeroed_files_finder - .get_children() - .get(0) - .unwrap() - .clone() - .downcast::() - .unwrap() - .get_model() - .unwrap() - .downcast::() - .unwrap(); - list_store.clear(); - - let col_indices = [0, 1, 2, 3]; - - let vector = zf.get_zeroed_files(); - - for file_entry in vector { - let (directory, file) = split_path(&file_entry.path); - let values: [&dyn ToValue; 4] = [ - &(file_entry.size.file_size(options::BINARY).unwrap()), - &file, - &directory, - &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()), - ]; - list_store.set(&list_store.append(), &col_indices, &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_zeroed_files_state.borrow_mut() = zf; - - if zeroed_files_number > 0 { - *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("save").unwrap() = true; - *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("delete").unwrap() = true; - } else { - *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("save").unwrap() = false; - *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap().get_mut("delete").unwrap() = false; - } - set_buttons(&mut *shared_buttons.borrow_mut().get_mut("zeroed_files").unwrap(), &buttons_array, &buttons_names); - } - } - } - } - // Returning false here would close the receiver and have senders fail - glib::Continue(true) - }); + startup_configuration(&gui_data); + connect_button_delete(&gui_data); + connect_button_save(&gui_data); + connect_button_search(&gui_data, sender); + connect_button_select(&gui_data); + connect_button_stop(&gui_data); + connect_notebook_tabs(&gui_data); + connect_upper_notebook(&gui_data); + connect_popover_duplicate(&gui_data); + connect_compute_results(&gui_data, receiver); // Quit the program when X in main window was clicked - window_main.connect_delete_event(|_, _| { + gui_data.window_main.connect_delete_event(|_, _| { gtk::main_quit(); Inhibit(false) }); diff --git a/czkawka_gui/src/startup_configuration.rs b/czkawka_gui/src/startup_configuration.rs new file mode 100644 index 0000000..7368053 --- /dev/null +++ b/czkawka_gui/src/startup_configuration.rs @@ -0,0 +1,241 @@ +use crate::create_tree_view::*; +use crate::double_click_opening::*; +use crate::gui_data::*; +use crate::help_functions::*; +use gtk::prelude::*; +use gtk::{SelectionMode, TreeView}; +use std::env; + +pub fn startup_configuration(gui_data: &GuiData) { + //// Setup default look(duplicate finder) + { + let entry_info = gui_data.entry_info.clone(); + let buttons_search = gui_data.buttons_search.clone(); + let buttons_save = gui_data.buttons_save.clone(); + let buttons_delete = gui_data.buttons_delete.clone(); + let buttons_stop = gui_data.buttons_stop.clone(); + let buttons_resume = gui_data.buttons_resume.clone(); + let buttons_pause = gui_data.buttons_pause.clone(); + let buttons_select = gui_data.buttons_select.clone(); + let scrolled_window_duplicate_finder = gui_data.scrolled_window_duplicate_finder.clone(); + let scrolled_window_main_empty_folder_finder = gui_data.scrolled_window_main_empty_folder_finder.clone(); + let scrolled_window_main_empty_files_finder = gui_data.scrolled_window_main_empty_files_finder.clone(); + let scrolled_window_main_temporary_files_finder = gui_data.scrolled_window_main_temporary_files_finder.clone(); + let scrolled_window_big_files_finder = gui_data.scrolled_window_big_files_finder.clone(); + let scrolled_window_similar_images_finder = gui_data.scrolled_window_similar_images_finder.clone(); + let scrolled_window_zeroed_files_finder = gui_data.scrolled_window_zeroed_files_finder.clone(); + let scrolled_window_included_directories = gui_data.scrolled_window_included_directories.clone(); + let scrolled_window_excluded_directories = gui_data.scrolled_window_excluded_directories.clone(); + let entry_excluded_items = gui_data.entry_excluded_items.clone(); + + entry_info.set_text("Duplicated Files"); + + // Disable and show buttons + buttons_search.show(); + buttons_save.hide(); + buttons_delete.hide(); + buttons_stop.hide(); + buttons_resume.hide(); + buttons_pause.hide(); + buttons_select.hide(); + + // Set Main Scrolled Window Treeviews + { + // Duplicate Files + { + let col_types: [glib::types::Type; 6] = [ + glib::types::Type::String, + glib::types::Type::String, + glib::types::Type::String, + glib::types::Type::U64, + glib::types::Type::String, + glib::types::Type::String, + ]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); + + tree_view.get_selection().set_mode(SelectionMode::Multiple); + tree_view.get_selection().set_select_function(Some(Box::new(select_function_duplicates))); + + create_tree_view_duplicates(&mut tree_view); + + tree_view.connect_button_press_event(opening_double_click_function_duplicates); + + scrolled_window_duplicate_finder.add(&tree_view); + scrolled_window_duplicate_finder.show_all(); + } + // Empty Folders + { + let col_types: [glib::types::Type; 3] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); + + tree_view.get_selection().set_mode(SelectionMode::Multiple); + + create_tree_view_empty_folders(&mut tree_view); + + tree_view.connect_button_press_event(opening_double_click_function_empty_folders); + + scrolled_window_main_empty_folder_finder.add(&tree_view); + scrolled_window_main_empty_folder_finder.show_all(); + } + // Empty Files + { + let col_types: [glib::types::Type; 3] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); + + tree_view.get_selection().set_mode(SelectionMode::Multiple); + + create_tree_view_empty_files(&mut tree_view); + + tree_view.connect_button_press_event(opening_double_click_function_empty_files); + + scrolled_window_main_empty_files_finder.add(&tree_view); + scrolled_window_main_empty_files_finder.show_all(); + } + // Temporary Files + { + let col_types: [glib::types::Type; 3] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); + + tree_view.get_selection().set_mode(SelectionMode::Multiple); + + create_tree_view_temporary_files(&mut tree_view); + + tree_view.connect_button_press_event(opening_double_click_function_temporary_files); + + scrolled_window_main_temporary_files_finder.add(&tree_view); + scrolled_window_main_temporary_files_finder.show_all(); + } + // Big Files + { + let col_types: [glib::types::Type; 4] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); + + tree_view.get_selection().set_mode(SelectionMode::Multiple); + + create_tree_view_big_files(&mut tree_view); + + tree_view.connect_button_press_event(opening_double_click_function_big_files); + + scrolled_window_big_files_finder.add(&tree_view); + scrolled_window_big_files_finder.show_all(); + } + // Similar Images + { + // TODO create maybe open button to support opening multiple files at once + let col_types: [glib::types::Type; 8] = [ + glib::types::Type::String, + glib::types::Type::String, + glib::types::Type::String, + glib::types::Type::String, + glib::types::Type::String, + glib::types::Type::String, + glib::types::Type::String, + glib::types::Type::String, + ]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); + + tree_view.get_selection().set_mode(SelectionMode::Multiple); + + create_tree_view_similar_images(&mut tree_view); + + tree_view.connect_button_press_event(opening_double_click_function_similar_images); + + scrolled_window_similar_images_finder.add(&tree_view); + scrolled_window_similar_images_finder.show_all(); + } + // Zeroed Files + { + let col_types: [glib::types::Type; 4] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + let mut tree_view: gtk::TreeView = TreeView::with_model(&list_store); + + tree_view.get_selection().set_mode(SelectionMode::Multiple); + + create_tree_view_zeroed_files(&mut tree_view); + + tree_view.connect_button_press_event(opening_double_click_function_zeroed_files); + + scrolled_window_zeroed_files_finder.add(&tree_view); + scrolled_window_zeroed_files_finder.show_all(); + } + } + + // Set Included Directory + { + let col_types: [glib::types::Type; 2] = [glib::types::Type::String, glib::types::Type::String]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + let mut tree_view_included_directory: gtk::TreeView = TreeView::with_model(&list_store); + + tree_view_included_directory.get_selection().set_mode(SelectionMode::Single); + + create_tree_view_directories(&mut tree_view_included_directory); + + let col_indices = [0, 1]; + + let current_dir: String = match env::current_dir() { + Ok(t) => t.to_str().unwrap().to_string(), + Err(_) => { + if cfg!(target_family = "unix") { + println!("Failed to read current directory, setting /home instead"); + "/home".to_string() + } else if cfg!(target_family = "windows") { + println!("Failed to read current directory, setting C:\\ instead"); + "C:\\".to_string() + } else { + "".to_string() + } + } + }; + + let values: [&dyn ToValue; 2] = [¤t_dir, &(MAIN_ROW_COLOR.to_string())]; + list_store.set(&list_store.append(), &col_indices, &values); + + scrolled_window_included_directories.add(&tree_view_included_directory); + scrolled_window_included_directories.show_all(); + } + // Set Excluded Directory + { + let col_types: [glib::types::Type; 2] = [glib::types::Type::String, glib::types::Type::String]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + let mut tree_view_excluded_directory: gtk::TreeView = TreeView::with_model(&list_store); + + tree_view_excluded_directory.get_selection().set_mode(SelectionMode::Single); + + create_tree_view_directories(&mut tree_view_excluded_directory); + + 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); + } + + scrolled_window_excluded_directories.add(&tree_view_excluded_directory); + scrolled_window_excluded_directories.show_all(); + } + // Set Excluded Items + { + if cfg!(target_family = "unix") { + entry_excluded_items.set_text("*/.git/*,*/node_modules/*,*/lost+found/*"); + } + if cfg!(target_family = "windows") { + entry_excluded_items.set_text("*/.git/*,*/node_modules/*,*/lost+found/*,*:/windows/*"); + } + } + } +}