From 8bbb12c7d421c1cd2b77554c00b0c0f15e5d7fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= <41945903+qarmin@users.noreply.github.com> Date: Sat, 10 Oct 2020 11:15:20 +0200 Subject: [PATCH] Add ability to stop task from GUI (#55) --- Cargo.lock | 2 + Changelog.md | 7 +- czkawka_cli/Cargo.toml | 2 +- czkawka_cli/src/main.rs | 10 +- czkawka_core/Cargo.toml | 3 +- czkawka_core/src/big_file.rs | 35 +- czkawka_core/src/common.rs | 1 - czkawka_core/src/duplicate.rs | 49 ++- czkawka_core/src/empty_files.rs | 36 +- czkawka_core/src/empty_folder.rs | 78 ++-- czkawka_core/src/temporary.rs | 23 +- czkawka_gui/Cargo.toml | 3 +- czkawka_gui/src/help_functions.rs | 3 - czkawka_gui/src/main.rs | 576 ++++++++++++++++++------------ czkawka_gui_orbtk/Cargo.toml | 2 +- 15 files changed, 487 insertions(+), 343 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index feb49fd..97ea0ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -344,6 +344,7 @@ name = "czkawka_core" version = "1.0.1" dependencies = [ "blake3", + "crossbeam-channel", "humansize", ] @@ -352,6 +353,7 @@ name = "czkawka_gui" version = "1.0.1" dependencies = [ "chrono", + "crossbeam-channel", "czkawka_core", "gdk", "gio", diff --git a/Changelog.md b/Changelog.md index 2836380..2c3f246 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,9 @@ - +## Version 1.x +- Improve code quality/Simplify codebase [#52](https://github.com/qarmin/czkawka/pull/52) +- Fixed skipping some correct results in specific situations [#52](https://github.com/qarmin/czkawka/pull/52#discussion_r502613895) +- Added support for searching in other thread [#51](https://github.com/qarmin/czkawka/pull/51) +- Divide CI across files [#48](https://github.com/qarmin/czkawka/pull/48) +- Added ability to stop task from GUI [#55](https://github.com/qarmin/czkawka/pull/55) ## Version 1.0.1 - 06.10.2020r - Replaced default argument parser with StructOpt [#37](https://github.com/qarmin/czkawka/pull/37) diff --git a/czkawka_cli/Cargo.toml b/czkawka_cli/Cargo.toml index 10e3601..52559c7 100644 --- a/czkawka_cli/Cargo.toml +++ b/czkawka_cli/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "czkawka_cli" version = "1.0.1" -authors = ["Rafał Mikrut "] +authors = ["Rafał Mikrut "] edition = "2018" description = "CLI frontend of Czkawka" license = "MIT" diff --git a/czkawka_cli/src/main.rs b/czkawka_cli/src/main.rs index b3daa97..9ad0597 100644 --- a/czkawka_cli/src/main.rs +++ b/czkawka_cli/src/main.rs @@ -49,7 +49,7 @@ fn main() { df.set_delete_method(delete_method); df.set_recursive_search(!not_recursive.not_recursive); - df.find_duplicates(); + df.find_duplicates(None); if let Some(file_name) = file_to_save.file_name() { if !df.save_results_to_file(file_name) { @@ -68,7 +68,7 @@ fn main() { ef.set_included_directory(path_list_to_str(directories.directories)); ef.set_delete_folder(delete_folders); - ef.find_empty_folders(); + ef.find_empty_folders(None); if let Some(file_name) = file_to_save.file_name() { if !ef.save_results_to_file(file_name) { @@ -99,7 +99,7 @@ fn main() { bf.set_number_of_files_to_check(number_of_files); bf.set_recursive_search(!not_recursive.not_recursive); - bf.find_big_files(); + bf.find_big_files(None); if let Some(file_name) = file_to_save.file_name() { if !bf.save_results_to_file(file_name) { @@ -133,7 +133,7 @@ fn main() { ef.set_delete_method(empty_files::DeleteMethod::Delete); } - ef.find_empty_files(); + ef.find_empty_files(None); if let Some(file_name) = file_to_save.file_name() { if !ef.save_results_to_file(file_name) { @@ -165,7 +165,7 @@ fn main() { tf.set_delete_method(temporary::DeleteMethod::Delete); } - tf.find_temporary_files(); + tf.find_temporary_files(None); if let Some(file_name) = file_to_save.file_name() { if !tf.save_results_to_file(file_name) { diff --git a/czkawka_core/Cargo.toml b/czkawka_core/Cargo.toml index a811afa..e210060 100644 --- a/czkawka_core/Cargo.toml +++ b/czkawka_core/Cargo.toml @@ -12,4 +12,5 @@ repository = "https://github.com/qarmin/czkawka" [dependencies] humansize = "1" blake3 = "0.3" -#rayon = "1" \ No newline at end of file +#rayon = "1" +crossbeam-channel = "0.4.4" \ No newline at end of file diff --git a/czkawka_core/src/big_file.rs b/czkawka_core/src/big_file.rs index 7213f84..86829c2 100644 --- a/czkawka_core/src/big_file.rs +++ b/czkawka_core/src/big_file.rs @@ -4,6 +4,7 @@ use crate::common_extensions::Extensions; use crate::common_items::ExcludedItems; use crate::common_messages::Messages; use crate::common_traits::{DebugPrint, PrintResults, SaveResults}; +use crossbeam_channel::Receiver; use humansize::{file_size_opts as options, FileSize}; use std::collections::BTreeMap; use std::fs; @@ -45,6 +46,7 @@ pub struct BigFile { allowed_extensions: Extensions, recursive_search: bool, number_of_files_to_check: usize, + stopped_search: bool, } impl BigFile { @@ -58,14 +60,21 @@ impl BigFile { allowed_extensions: Extensions::new(), recursive_search: true, number_of_files_to_check: 50, + stopped_search: false, } } - pub fn find_big_files(&mut self) { + pub fn find_big_files(&mut self, rx: Option<&Receiver<()>>) { self.optimize_directories(); - self.look_for_big_files(); + if !self.look_for_big_files(rx) { + self.stopped_search = true; + return; + } self.debug_print(); } + pub fn get_stopped_search(&self) -> bool { + self.stopped_search + } pub const fn get_big_files(&self) -> &BTreeMap> { &self.big_files @@ -88,7 +97,7 @@ impl BigFile { self.allowed_extensions.set_allowed_extensions(allowed_extensions, &mut self.text_messages); } - fn look_for_big_files(&mut self) { + fn look_for_big_files(&mut self, rx: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -101,6 +110,9 @@ impl BigFile { let mut current_folder: String; let mut next_folder: String; while !folders_to_check.is_empty() { + if rx.is_some() && rx.unwrap().try_recv().is_ok() { + return false; + } current_folder = folders_to_check.pop().unwrap(); let read_dir = match fs::read_dir(¤t_folder) { @@ -127,10 +139,6 @@ impl BigFile { }; if metadata.is_dir() { self.information.number_of_checked_folders += 1; - // if entry_data.file_name().into_string().is_err() { // Probably this can be removed, if crash still will be happens, then uncomment this line - // self.text_messages.warnings.push("Cannot read folder name in dir ".to_string() + current_folder.as_str()); - // continue; // Permissions denied - // } if !self.recursive_search { continue; @@ -163,11 +171,13 @@ impl BigFile { .to_lowercase(); // Checking allowed extensions - let allowed = self.allowed_extensions.file_extensions.iter().any(|e| file_name_lowercase.ends_with((".".to_string() + e.to_lowercase().as_str()).as_str())); - if !allowed { - // Not an allowed extension, ignore it. - self.information.number_of_ignored_files += 1; - continue 'dir; + if !self.allowed_extensions.file_extensions.is_empty() { + let allowed = self.allowed_extensions.file_extensions.iter().any(|e| file_name_lowercase.ends_with((".".to_string() + e.to_lowercase().as_str()).as_str())); + if !allowed { + // Not an allowed extension, ignore it. + self.information.number_of_ignored_files += 1; + continue 'dir; + } } // Checking files let current_file_name = "".to_owned() @@ -236,6 +246,7 @@ impl BigFile { self.big_files = new_map; Common::print_time(start_time, SystemTime::now(), "look_for_big_files".to_string()); + true } pub fn set_number_of_files_to_check(&mut self, number_of_files_to_check: usize) { diff --git a/czkawka_core/src/common.rs b/czkawka_core/src/common.rs index 5a45d22..d814db9 100644 --- a/czkawka_core/src/common.rs +++ b/czkawka_core/src/common.rs @@ -85,7 +85,6 @@ impl Common { } // `*home` shouldn't be true for `/homeowner` if !expression.ends_with('*') && !directory.ends_with(splits.last().unwrap()) { - // && !directory.ends_with(&(splits.last().unwrap().to_string() + "/")){ return false; } diff --git a/czkawka_core/src/duplicate.rs b/czkawka_core/src/duplicate.rs index aca5908..c2d3b12 100644 --- a/czkawka_core/src/duplicate.rs +++ b/czkawka_core/src/duplicate.rs @@ -1,3 +1,4 @@ +use crossbeam_channel::Receiver; use humansize::{file_size_opts as options, FileSize}; use std::collections::{BTreeMap, HashMap}; use std::fs; @@ -76,6 +77,7 @@ pub struct DuplicateFinder { minimal_file_size: u64, check_method: CheckingMethod, delete_method: DeleteMethod, + stopped_search: bool, } impl DuplicateFinder { @@ -92,15 +94,22 @@ impl DuplicateFinder { minimal_file_size: 1024, directories: Directories::new(), excluded_items: ExcludedItems::new(), + stopped_search: false, } } - /// Finding duplicates, save results to internal struct variables - pub fn find_duplicates(&mut self) { + pub fn find_duplicates(&mut self, rx: Option<&Receiver<()>>) { self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); - self.check_files_size(); + if !self.check_files_size(rx) { + self.stopped_search = true; + return; + } + #[allow(clippy::collapsible_if)] if self.check_method == CheckingMethod::Hash || self.check_method == CheckingMethod::HashMB { - self.check_files_hash(); + if !self.check_files_hash(rx) { + self.stopped_search = true; + return; + } } self.delete_files(); self.debug_print(); @@ -110,6 +119,10 @@ impl DuplicateFinder { &self.check_method } + pub fn get_stopped_search(&self) -> bool { + self.stopped_search + } + pub const fn get_files_sorted_by_size(&self) -> &BTreeMap> { &self.files_with_identical_size } @@ -162,7 +175,7 @@ impl DuplicateFinder { /// Read file length and puts it to different boxes(each for different lengths) /// If in box is only 1 result, then it is removed - fn check_files_size(&mut self) { + fn check_files_size(&mut self, rx: Option<&Receiver<()>>) -> bool { // TODO maybe add multithreading checking for file hash let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -176,6 +189,9 @@ impl DuplicateFinder { let mut current_folder: String; let mut next_folder: String; while !folders_to_check.is_empty() { + if rx.is_some() && rx.unwrap().try_recv().is_ok() { + return false; + } current_folder = folders_to_check.pop().unwrap(); // Read current dir, if permission are denied just go to next @@ -205,10 +221,6 @@ impl DuplicateFinder { }; if metadata.is_dir() { self.information.number_of_checked_folders += 1; - // if entry_data.file_name().into_string().is_err() { // Probably this can be removed, if crash still will be happens, then uncomment this line - // self.text_messages.warnings.push("Cannot read folder name in dir ".to_string() + current_folder.as_str()); - // continue; // Permissions denied - // } if !self.recursive_search { continue; @@ -242,11 +254,13 @@ impl DuplicateFinder { .to_lowercase(); // Checking allowed extensions - let allowed = self.allowed_extensions.file_extensions.iter().any(|e| file_name_lowercase.ends_with((".".to_string() + e.to_lowercase().as_str()).as_str())); - if !allowed { - // Not an allowed extension, ignore it. - self.information.number_of_ignored_files += 1; - continue 'dir; + if !self.allowed_extensions.file_extensions.is_empty() { + let allowed = self.allowed_extensions.file_extensions.iter().any(|e| file_name_lowercase.ends_with((".".to_string() + e.to_lowercase().as_str()).as_str())); + if !allowed { + // Not an allowed extension, ignore it. + self.information.number_of_ignored_files += 1; + continue 'dir; + } } // Checking files if metadata.len() >= self.minimal_file_size { @@ -315,10 +329,11 @@ impl DuplicateFinder { self.files_with_identical_size = new_map; Common::print_time(start_time, SystemTime::now(), "check_files_size".to_string()); + true } /// The slowest checking type, which must be applied after checking for size - fn check_files_hash(&mut self) { + fn check_files_hash(&mut self, rx: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut file_handler: File; let mut hashmap_with_hash: HashMap>; @@ -327,6 +342,9 @@ impl DuplicateFinder { hashmap_with_hash = Default::default(); for file_entry in vector { + if rx.is_some() && rx.unwrap().try_recv().is_ok() { + return false; + } file_handler = match File::open(&file_entry.path) { Ok(t) => t, Err(_) => { @@ -384,6 +402,7 @@ impl DuplicateFinder { } Common::print_time(start_time, SystemTime::now(), "check_files_hash".to_string()); + true } /// Function to delete files, from filed before BTreeMap diff --git a/czkawka_core/src/empty_files.rs b/czkawka_core/src/empty_files.rs index 778aadb..3c982c6 100644 --- a/czkawka_core/src/empty_files.rs +++ b/czkawka_core/src/empty_files.rs @@ -9,6 +9,7 @@ use crate::common_extensions::Extensions; use crate::common_items::ExcludedItems; use crate::common_messages::Messages; use crate::common_traits::*; +use crossbeam_channel::Receiver; #[derive(Eq, PartialEq, Clone, Debug)] pub enum DeleteMethod { @@ -49,6 +50,7 @@ pub struct EmptyFiles { excluded_items: ExcludedItems, recursive_search: bool, delete_method: DeleteMethod, + stopped_search: bool, } impl EmptyFiles { @@ -62,17 +64,25 @@ impl EmptyFiles { excluded_items: ExcludedItems::new(), empty_files: vec![], delete_method: DeleteMethod::None, + stopped_search: false, } } /// Finding empty files, save results to internal struct variables - pub fn find_empty_files(&mut self) { + pub fn find_empty_files(&mut self, rx: Option<&Receiver<()>>) { self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); - self.check_files(); + if !self.check_files(rx) { + self.stopped_search = true; + return; + } self.delete_files(); self.debug_print(); } + pub fn get_stopped_search(&self) -> bool { + self.stopped_search + } + pub const fn get_empty_files(&self) -> &Vec { &self.empty_files } @@ -109,7 +119,7 @@ impl EmptyFiles { } /// Check files for any with size == 0 - fn check_files(&mut self) { + fn check_files(&mut self, rx: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -122,6 +132,9 @@ impl EmptyFiles { let mut current_folder: String; let mut next_folder: String; while !folders_to_check.is_empty() { + if rx.is_some() && rx.unwrap().try_recv().is_ok() { + return false; + } current_folder = folders_to_check.pop().unwrap(); // Read current dir, if permission are denied just go to next @@ -151,10 +164,6 @@ impl EmptyFiles { }; if metadata.is_dir() { self.information.number_of_checked_folders += 1; - // if entry_data.file_name().into_string().is_err() { // Probably this can be removed, if crash still will be happens, then uncomment this line - // self.text_messages.warnings.push("Cannot read folder name in dir ".to_string() + current_folder.as_str()); - // continue; // Permissions denied - // } if !self.recursive_search { continue; @@ -187,11 +196,13 @@ impl EmptyFiles { .to_lowercase(); // Checking allowed extensions - let allowed = self.allowed_extensions.file_extensions.iter().any(|e| file_name_lowercase.ends_with((".".to_string() + e.to_lowercase().as_str()).as_str())); - if !allowed { - // Not an allowed extension, ignore it. - self.information.number_of_ignored_files += 1; - continue 'dir; + if !self.allowed_extensions.file_extensions.is_empty() { + let allowed = self.allowed_extensions.file_extensions.iter().any(|e| file_name_lowercase.ends_with((".".to_string() + e.to_lowercase().as_str()).as_str())); + if !allowed { + // Not an allowed extension, ignore it. + self.information.number_of_ignored_files += 1; + continue 'dir; + } } // Checking files if metadata.len() == 0 { @@ -243,6 +254,7 @@ impl EmptyFiles { self.information.number_of_empty_files = self.empty_files.len(); Common::print_time(start_time, SystemTime::now(), "check_files_size".to_string()); + true } /// Function to delete files, from filed Vector diff --git a/czkawka_core/src/empty_folder.rs b/czkawka_core/src/empty_folder.rs index 8735690..e6e7a06 100644 --- a/czkawka_core/src/empty_folder.rs +++ b/czkawka_core/src/empty_folder.rs @@ -2,6 +2,7 @@ use crate::common::Common; use crate::common_directory::Directories; use crate::common_messages::Messages; use crate::common_traits::{DebugPrint, PrintResults, SaveResults}; +use crossbeam_channel::Receiver; use std::collections::BTreeMap; use std::fs; use std::fs::{File, Metadata}; @@ -31,6 +32,7 @@ pub struct EmptyFolder { text_messages: Messages, empty_folder_list: BTreeMap, // Path, FolderEntry directories: Directories, + stopped_search: bool, } /// Info struck with helpful information's about results @@ -55,9 +57,14 @@ impl EmptyFolder { text_messages: Messages::new(), empty_folder_list: Default::default(), directories: Directories::new(), + stopped_search: false, } } + pub fn get_stopped_search(&self) -> bool { + self.stopped_search + } + pub const fn get_empty_folder_list(&self) -> &BTreeMap { &self.empty_folder_list } @@ -70,10 +77,12 @@ impl EmptyFolder { } /// Public function used by CLI to search for empty folders - pub fn find_empty_folders(&mut self) { + pub fn find_empty_folders(&mut self, rx: Option<&Receiver<()>>) { self.directories.optimize_directories(true, &mut self.text_messages); - self.check_for_empty_folders(true); - //self.check_for_empty_folders(false); // Second check, should be done before deleting to be sure that empty folder is still empty + if !self.check_for_empty_folders(rx) { + self.stopped_search = true; + return; + } self.optimize_folders(); if self.delete_folders { self.delete_empty_folders(); @@ -108,42 +117,30 @@ impl EmptyFolder { /// Function to check if folder are empty. /// Parameter initial_checking for second check before deleting to be sure that checked folder is still empty - fn check_for_empty_folders(&mut self, initial_checking: bool) { + fn check_for_empty_folders(&mut self, rx: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector let mut folders_checked: BTreeMap = Default::default(); - if initial_checking { - // Add root folders for finding - for id in &self.directories.included_directories { - folders_checked.insert( - id.clone(), - FolderEntry { - parent_path: None, - is_empty: FolderEmptiness::Maybe, - modified_date: 0, - }, - ); - folders_to_check.push(id.clone()); - } - } else { - // Add folders searched before - for (name, folder_entry) in &self.empty_folder_list { - folders_checked.insert( - name.clone(), - FolderEntry { - parent_path: None, - is_empty: FolderEmptiness::Maybe, - modified_date: folder_entry.modified_date, - }, - ); - folders_to_check.push(name.clone()); - } + // Add root folders for finding + for id in &self.directories.included_directories { + folders_checked.insert( + id.clone(), + FolderEntry { + parent_path: None, + is_empty: FolderEmptiness::Maybe, + modified_date: 0, + }, + ); + folders_to_check.push(id.clone()); } let mut current_folder: String; let mut next_folder: String; while !folders_to_check.is_empty() { + if rx.is_some() && rx.unwrap().try_recv().is_ok() { + return false; + } self.information.number_of_checked_folders += 1; current_folder = folders_to_check.pop().unwrap(); // Checked folder may be deleted or we may not have permissions to open it so we assume that this folder is not be empty @@ -214,26 +211,15 @@ impl EmptyFolder { } } - // Now we check if checked folders are really empty, and if are, then - if initial_checking { - // We need to set empty folder list - for (name, folder_entry) in folders_checked { - if folder_entry.is_empty != FolderEmptiness::No { - self.empty_folder_list.insert(name, folder_entry); - } + // We need to set empty folder list + for (name, folder_entry) in folders_checked { + if folder_entry.is_empty != FolderEmptiness::No { + self.empty_folder_list.insert(name, folder_entry); } - } else { - // We need to check if parent of folder isn't also empty, because we want to delete only parent with two empty folders except this folders and at the end parent folder - let mut new_folders_list: BTreeMap = Default::default(); - for (name, folder_entry) in folders_checked { - if folder_entry.is_empty != FolderEmptiness::No && self.empty_folder_list.contains_key(&name) { - new_folders_list.insert(name, folder_entry); - } - } - self.empty_folder_list = new_folders_list; } Common::print_time(start_time, SystemTime::now(), "check_for_empty_folder".to_string()); + true } /// Deletes earlier found empty folders diff --git a/czkawka_core/src/temporary.rs b/czkawka_core/src/temporary.rs index f7efdf8..ef3d798 100644 --- a/czkawka_core/src/temporary.rs +++ b/czkawka_core/src/temporary.rs @@ -8,6 +8,7 @@ use crate::common_directory::Directories; use crate::common_items::ExcludedItems; use crate::common_messages::Messages; use crate::common_traits::*; +use crossbeam_channel::Receiver; #[derive(Eq, PartialEq, Clone, Debug)] pub enum DeleteMethod { @@ -47,6 +48,7 @@ pub struct Temporary { excluded_items: ExcludedItems, recursive_search: bool, delete_method: DeleteMethod, + stopped_search: bool, } impl Temporary { @@ -59,16 +61,23 @@ impl Temporary { excluded_items: ExcludedItems::new(), delete_method: DeleteMethod::None, temporary_files: vec![], + stopped_search: false, } } /// Finding temporary files, save results to internal struct variables - pub fn find_temporary_files(&mut self) { + pub fn find_temporary_files(&mut self, rx: Option<&Receiver<()>>) { self.directories.optimize_directories(self.recursive_search, &mut self.text_messages); - self.check_files(); + if !self.check_files(rx) { + self.stopped_search = true; + return; + } self.delete_files(); self.debug_print(); } + pub fn get_stopped_search(&self) -> bool { + self.stopped_search + } pub const fn get_temporary_files(&self) -> &Vec { &self.temporary_files @@ -101,7 +110,7 @@ impl Temporary { self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages); } - fn check_files(&mut self) { + fn check_files(&mut self, rx: Option<&Receiver<()>>) -> bool { let start_time: SystemTime = SystemTime::now(); let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector @@ -114,6 +123,9 @@ impl Temporary { let mut current_folder: String; let mut next_folder: String; while !folders_to_check.is_empty() { + if rx.is_some() && rx.unwrap().try_recv().is_ok() { + return false; + } current_folder = folders_to_check.pop().unwrap(); // Read current dir, if permission are denied just go to next @@ -143,10 +155,6 @@ impl Temporary { }; if metadata.is_dir() { self.information.number_of_checked_folders += 1; - // if entry_data.file_name().into_string().is_err() { // Probably this can be removed, if crash still will be happens, then uncomment this line - // self.text_messages.warnings.push("Cannot read folder name in dir ".to_string() + current_folder.as_str()); - // continue; // Permissions denied - // } if !self.recursive_search { continue; @@ -232,6 +240,7 @@ impl Temporary { self.information.number_of_temporary_files = self.temporary_files.len(); Common::print_time(start_time, SystemTime::now(), "check_files_size".to_string()); + true } /// Function to delete files, from filed Vector diff --git a/czkawka_gui/Cargo.toml b/czkawka_gui/Cargo.toml index 0fa4578..d543d19 100644 --- a/czkawka_gui/Cargo.toml +++ b/czkawka_gui/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "czkawka_gui" version = "1.0.1" -authors = ["Rafał Mikrut "] +authors = ["Rafał Mikrut "] edition = "2018" description = "GTK frontend of Czkawka" license = "MIT" @@ -17,6 +17,7 @@ glib = "0.10.1" humansize = "1" chrono = "0.4" +crossbeam-channel = "0.4.4" [dependencies.gtk] version = "0.9.2" diff --git a/czkawka_gui/src/help_functions.rs b/czkawka_gui/src/help_functions.rs index 8e91c81..a81b9b5 100644 --- a/czkawka_gui/src/help_functions.rs +++ b/czkawka_gui/src/help_functions.rs @@ -24,20 +24,17 @@ pub enum ColumnsDirectory { Path = 0, } pub enum ColumnsBigFiles { - // Columns for Big Files Size = 0, Name, Path, Modification, } pub enum ColumnsEmptyFiles { - // Columns for Big Files Name = 0, Path, Modification, } pub enum ColumnsTemporaryFiles { - // Columns for Big Files Name = 0, Path, Modification, diff --git a/czkawka_gui/src/main.rs b/czkawka_gui/src/main.rs index 0671375..e9b597f 100644 --- a/czkawka_gui/src/main.rs +++ b/czkawka_gui/src/main.rs @@ -6,6 +6,7 @@ use humansize::{file_size_opts as options, FileSize}; extern crate gtk; 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; @@ -184,7 +185,7 @@ fn main() { let scrolled_window_excluded_directories: gtk::ScrolledWindow = builder.get_object("scrolled_window_excluded_directories").unwrap(); //// Threads - // Messages + // Types of messages to send to main thread where gui can be draw. enum Message { Duplicates(DuplicateFinder), EmptyFolders(EmptyFolder), @@ -192,10 +193,14 @@ fn main() { BigFiles(BigFile), Temporary(Temporary), } - // Sender/Reciver + + // Used for getting data from thread let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); - //// Setup default look for + // 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"); @@ -395,7 +400,7 @@ fn main() { //// Connect Buttons - // Down notepad + // Main buttons { assert!(notebook_main_children_names.contains(&"notebook_main_duplicate_finder_label".to_string())); assert!(notebook_main_children_names.contains(&"scrolled_window_main_empty_folder_finder".to_string())); @@ -444,7 +449,7 @@ fn main() { 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(); @@ -456,24 +461,26 @@ fn main() { df.set_minimal_file_size(minimal_file_size); df.set_check_method(check_method); df.set_delete_method(delete_method); - df.find_duplicates(); + 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(); + 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 || { @@ -484,12 +491,13 @@ fn main() { vf.set_recursive_search(recursive_search); vf.set_excluded_items(excluded_items); vf.set_allowed_extensions(allowed_extensions); - vf.find_empty_files(); + 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 || { @@ -499,7 +507,7 @@ fn main() { tf.set_excluded_directory(excluded_directories); tf.set_recursive_search(recursive_search); tf.set_excluded_items(excluded_items); - tf.find_temporary_files(); + tf.find_temporary_files(Option::from(&receiver_stop)); let _ = sender.send(Message::Temporary(tf)); }); } @@ -510,6 +518,7 @@ fn main() { }; let sender = sender.clone(); + let receiver_stop = rx.clone(); // Find big files thread::spawn(move || { @@ -520,7 +529,7 @@ fn main() { 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(); + bf.find_big_files(Option::from(&receiver_stop)); let _ = sender.send(Message::BigFiles(bf)); }); } @@ -616,6 +625,7 @@ fn main() { 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(); + // TODO this should be fs::remove_dir_all(), because it doesn't delete folders with empty folders inside match fs::remove_dir(format!("{}/{}", path, name)) { Ok(_) => { list_store.remove(&list_store.get_iter(tree_path).unwrap()); @@ -819,6 +829,12 @@ fn main() { e => panic!("Not existent {}", e), }); } + // Stop button + { + buttons_stop.connect_clicked(move |_| { + sx.send(()).unwrap(); + }); + } } // Popover Buttons { @@ -1072,7 +1088,6 @@ fn main() { // All one newest { let scrolled_window_duplicate_finder = scrolled_window_duplicate_finder.clone(); - // let popover_select = 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(); @@ -1188,7 +1203,6 @@ fn main() { } // Remove Excluded Folder { - //let scrolled_window_excluded_directories = 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(); @@ -1205,7 +1219,6 @@ fn main() { } // Remove Included Folder { - //let scrolled_window_included_directories = 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(); @@ -1235,34 +1248,11 @@ fn main() { match msg { Message::Duplicates(df) => { - let information = df.get_information(); - let text_messages = df.get_text_messages(); + if df.get_stopped_search() { + entry_info.set_text("Searching for duplicated was stopped by user"); - let duplicates_number: usize; - let duplicates_size: u64; - let duplicates_group: usize; - - match df.get_check_method() { - 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; - } - 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; - } - 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 + //Also clear list + scrolled_window_duplicate_finder .get_children() .get(0) .unwrap() @@ -1272,17 +1262,87 @@ fn main() { .get_model() .unwrap() .downcast::() - .unwrap(); - list_store.clear(); + .unwrap() + .clear(); + } else { + let information = df.get_information(); + let text_messages = df.get_text_messages(); - let col_indices = [0, 1, 2, 3, 4, 5]; + let duplicates_number: usize; + let duplicates_size: u64; + let duplicates_group: usize; match df.get_check_method() { CheckingMethod::Hash | CheckingMethod::HashMB => { - let btreemap = df.get_files_sorted_by_hash(); + duplicates_number = information.number_of_duplicated_files_by_hash; + duplicates_size = information.lost_space_by_hash; + duplicates_group = information.number_of_groups_by_hash; + } + 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; + } + CheckingMethod::None => { + panic!(); + } + } - for (size, vectors_vector) in btreemap.iter().rev() { - for vector in vectors_vector { + 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::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] = [ + &(vector.len().to_string() + " x " + size.to_string().as_str()), + &(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 path = &entry.path; + let index = path.rfind('/').unwrap(); + + let values: [&dyn ToValue; 6] = [ + &(path[index + 1..].to_string()), + &(path[..index].to_string()), + &(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] = [ &(vector.len().to_string() + " x " + size.to_string().as_str()), &(format!("{} ({} bytes) lost", ((vector.len() - 1) as u64 * *size as u64).file_size(options::BINARY).unwrap(), (vector.len() - 1) as u64 * *size as u64)), @@ -1308,71 +1368,37 @@ fn main() { } } } - } - CheckingMethod::Size => { - let btreemap = df.get_files_sorted_by_size(); - - for (size, vector) in btreemap.iter().rev() { - let values: [&dyn ToValue; 6] = [ - &(vector.len().to_string() + " x " + size.to_string().as_str()), - &(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 path = &entry.path; - let index = path.rfind('/').unwrap(); - - let values: [&dyn ToValue; 6] = [ - &(path[index + 1..].to_string()), - &(path[..index].to_string()), - &(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!(); } } - CheckingMethod::None => { - panic!(); + + print_text_messages_to_text_view(text_messages, &text_view_errors); + } + + // Set state + { + *shared_duplication_state.borrow_mut() = df; + + if duplicates_size > 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); } - - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_duplication_state.borrow_mut() = df; - - if duplicates_size > 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) => { - let information = ef.get_information(); - let text_messages = ef.get_text_messages(); + if ef.get_stopped_search() { + entry_info.set_text("Searching for empty folders was stopped by user"); - 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 + //Also clear list + scrolled_window_main_empty_folder_finder .get_children() .get(0) .unwrap() @@ -1382,47 +1408,65 @@ fn main() { .get_model() .unwrap() .downcast::() - .unwrap(); - list_store.clear(); + .unwrap() + .clear(); + } else { + let information = ef.get_information(); + let text_messages = ef.get_text_messages(); - let col_indices = [0, 1, 2]; + let empty_folder_number: usize = information.number_of_empty_folders; - let hashmap = ef.get_empty_folder_list(); + entry_info.set_text(format!("Found {} empty folders.", empty_folder_number).as_str()); - for (name, entry) in hashmap { - let name: String = name[..(name.len() - 1)].to_string(); - let index = name.rfind('/').unwrap(); - let values: [&dyn ToValue; 3] = [&(name[index + 1..].to_string()), &(name[..index].to_string()), &(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string())]; - list_store.set(&list_store.append(), &col_indices, &values); + // 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 (name, entry) in hashmap { + let name: String = name[..(name.len() - 1)].to_string(); + let index = name.rfind('/').unwrap(); + let values: [&dyn ToValue; 3] = [&(name[index + 1..].to_string()), &(name[..index].to_string()), &(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); } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - // Set state - { - *shared_empty_folders_state.borrow_mut() = ef; + // 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; + 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); } - set_buttons(&mut *shared_buttons.borrow_mut().get_mut("empty_folder").unwrap(), &buttons_array, &buttons_names); } } Message::EmptyFiles(vf) => { - let information = vf.get_information(); - let text_messages = vf.get_text_messages(); + if vf.get_stopped_search() { + entry_info.set_text("Searching for empty files was stopped by user"); - 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 + //Also clear list + scrolled_window_main_empty_files_finder .get_children() .get(0) .unwrap() @@ -1432,104 +1476,65 @@ fn main() { .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 name: String = file_entry.path.to_string(); - let index = name.rfind('/').unwrap(); - let values: [&dyn ToValue; 3] = [&(name[index + 1..].to_string()), &(name[..index].to_string()), &(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) => { - 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(); + .clear(); + } else { + let information = vf.get_information(); + let text_messages = vf.get_text_messages(); - let col_indices = [0, 1, 2, 3]; + let empty_files_number: usize = information.number_of_empty_files; - let btreemap = bf.get_big_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 (size, vector) in btreemap.iter().rev() { for file_entry in vector { let name: String = file_entry.path.to_string(); let index = name.rfind('/').unwrap(); - let values: [&dyn ToValue; 4] = [ - &(format!("{} ({} bytes)", size.file_size(options::BINARY).unwrap(), size)), - &(name[index + 1..].to_string()), - &(name[..index].to_string()), - &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()), - ]; + let values: [&dyn ToValue; 3] = [&(name[index + 1..].to_string()), &(name[..index].to_string()), &(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); } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - // Set state - { - *shared_big_files_state.borrow_mut() = bf; + // Set state + { + *shared_empty_files_state.borrow_mut() = vf; - 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; + 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); } - set_buttons(&mut *shared_buttons.borrow_mut().get_mut("big_file").unwrap(), &buttons_array, &buttons_names); } } - Message::Temporary(tf) => { - let information = tf.get_information(); - let text_messages = tf.get_text_messages(); + Message::BigFiles(bf) => { + if bf.get_stopped_search() { + entry_info.set_text("Searching for big files was stopped by user"); - 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 + //Also clear list + scrolled_window_duplicate_finder .get_children() .get(0) .unwrap() @@ -1539,39 +1544,136 @@ fn main() { .get_model() .unwrap() .downcast::() - .unwrap(); - list_store.clear(); + .unwrap() + .clear(); + } else { + let information = bf.get_information(); + let text_messages = bf.get_text_messages(); - let col_indices = [0, 1, 2]; + let biggest_files_number: usize = information.number_of_real_files; - let vector = tf.get_temporary_files(); + entry_info.set_text(format!("Found {} biggest files.", biggest_files_number).as_str()); - for file_entry in vector { - let name: String = file_entry.path.to_string(); - let index = name.rfind('/').unwrap(); - let values: [&dyn ToValue; 3] = [&(name[index + 1..].to_string()), &(name[..index].to_string()), &(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string())]; - list_store.set(&list_store.append(), &col_indices, &values); + // 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 name: String = file_entry.path.to_string(); + let index = name.rfind('/').unwrap(); + let values: [&dyn ToValue; 4] = [ + &(format!("{} ({} bytes)", size.file_size(options::BINARY).unwrap(), size)), + &(name[index + 1..].to_string()), + &(name[..index].to_string()), + &(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); } - print_text_messages_to_text_view(text_messages, &text_view_errors); } + } + Message::Temporary(tf) => { + if tf.get_stopped_search() { + entry_info.set_text("Searching for temporary files was stopped by user"); - // Set state - { - *shared_temporary_files_state.borrow_mut() = tf; + //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(); - 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; + 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 name: String = file_entry.path.to_string(); + let index = name.rfind('/').unwrap(); + let values: [&dyn ToValue; 3] = [&(name[index + 1..].to_string()), &(name[..index].to_string()), &(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); } - set_buttons(&mut *shared_buttons.borrow_mut().get_mut("temporary_file").unwrap(), &buttons_array, &buttons_names); } } } - // Returning false here would close the receiver - // and have senders fail + // Returning false here would close the receiver and have senders fail glib::Continue(true) }); diff --git a/czkawka_gui_orbtk/Cargo.toml b/czkawka_gui_orbtk/Cargo.toml index 43c424b..a74735f 100644 --- a/czkawka_gui_orbtk/Cargo.toml +++ b/czkawka_gui_orbtk/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "czkawka_gui_orbtk" version = "1.0.1" -authors = ["Rafał Mikrut "] +authors = ["Rafał Mikrut "] edition = "2018" description = "Orbtk frontend of Czkawka" license = "MIT"