Add ability to stop task from GUI
This commit is contained in:
parent
085da0369e
commit
9160c158cb
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "czkawka_cli"
|
||||
version = "1.0.1"
|
||||
authors = ["Rafał Mikrut <mikrutrafal54@gmail.com>"]
|
||||
authors = ["Rafał Mikrut <mikrutrafal@protonmail.com>"]
|
||||
edition = "2018"
|
||||
description = "CLI frontend of Czkawka"
|
||||
license = "MIT"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -12,4 +12,5 @@ repository = "https://github.com/qarmin/czkawka"
|
|||
[dependencies]
|
||||
humansize = "1"
|
||||
blake3 = "0.3"
|
||||
#rayon = "1"
|
||||
#rayon = "1"
|
||||
crossbeam-channel = "0.4.4"
|
|
@ -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<u64, Vec<FileEntry>> {
|
||||
&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<String> = 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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<u64, Vec<FileEntry>> {
|
||||
&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<String> = 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<String, Vec<FileEntry>>;
|
||||
|
@ -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
|
||||
|
|
|
@ -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<FileEntry> {
|
||||
&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<String> = 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
|
||||
|
|
|
@ -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<String, FolderEntry>, // 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<String, FolderEntry> {
|
||||
&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<String> = 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<String, FolderEntry> = 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<String, FolderEntry> = 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
|
||||
|
|
|
@ -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<FileEntry> {
|
||||
&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<String> = 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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "czkawka_gui"
|
||||
version = "1.0.1"
|
||||
authors = ["Rafał Mikrut <mikrutrafal54@gmail.com>"]
|
||||
authors = ["Rafał Mikrut <mikrutrafal@protonmail.com>"]
|
||||
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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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::<String>().unwrap().unwrap();
|
||||
let path = tree_model.get_value(&tree_model.get_iter(tree_path).unwrap(), ColumnsEmptyFolders::Path as i32).get::<String>().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::<gtk::TreeView>().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::<gtk::TreeView>().unwrap();
|
||||
let list_store = tree_view.get_model().unwrap().downcast::<gtk::ListStore>().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::<gtk::TreeView>().unwrap();
|
||||
let list_store = tree_view.get_model().unwrap().downcast::<gtk::ListStore>().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::<gtk::ListStore>()
|
||||
.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::<gtk::TreeView>()
|
||||
.unwrap()
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<gtk::ListStore>()
|
||||
.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::<gtk::ListStore>()
|
||||
.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::<gtk::TreeView>()
|
||||
.unwrap()
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<gtk::ListStore>()
|
||||
.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::<gtk::ListStore>()
|
||||
.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::<gtk::TreeView>()
|
||||
.unwrap()
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<gtk::ListStore>()
|
||||
.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::<gtk::TreeView>()
|
||||
.unwrap()
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<gtk::ListStore>()
|
||||
.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::<gtk::ListStore>()
|
||||
.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::<gtk::TreeView>()
|
||||
.unwrap()
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<gtk::ListStore>()
|
||||
.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::<gtk::TreeView>()
|
||||
.unwrap()
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<gtk::ListStore>()
|
||||
.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::<gtk::TreeView>()
|
||||
.unwrap()
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<gtk::ListStore>()
|
||||
.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)
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "czkawka_gui_orbtk"
|
||||
version = "1.0.1"
|
||||
authors = ["Rafał Mikrut <mikrutrafal54@gmail.com>"]
|
||||
authors = ["Rafał Mikrut <mikrutrafal@protonmail.com>"]
|
||||
edition = "2018"
|
||||
description = "Orbtk frontend of Czkawka"
|
||||
license = "MIT"
|
||||
|
|
Loading…
Reference in a new issue