Add reference folders (#516)
* Add reference folder * Basic tree view cleaning * Computations * Similar Images * Muzyka i Wideo * Podwójne dwa razy, cztery razy! * Rozmiar i Nazwa * Spanko i działanko(przynajmniej zdaje mi się że działa) * Translatanko
This commit is contained in:
parent
5db5d17afb
commit
c4f26883b1
|
@ -11,7 +11,7 @@
|
|||
- Option to not remove cache from non existent files(e.g. from unplugged pendrive) - [#472](https://github.com/qarmin/czkawka/pull/472)
|
||||
- Add multiple tooltips with helpful messages - [#472](https://github.com/qarmin/czkawka/pull/472)
|
||||
- Allow to cache prehash - [#477](https://github.com/qarmin/czkawka/pull/477)
|
||||
- Improve custom selecting of records(allows to use Rust red regex) - [#489](https://github.com/qarmin/czkawka/pull/478)
|
||||
- Improve custom selecting of records(allows to use Rust regex) - [#489](https://github.com/qarmin/czkawka/pull/478)
|
||||
- Remove support for finding zeroed files - [#461](https://github.com/qarmin/czkawka/pull/461)
|
||||
- Remove HashMB mode - [#476](https://github.com/qarmin/czkawka/pull/476)
|
||||
- Approximate comparison of music - [#483](https://github.com/qarmin/czkawka/pull/483)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
- CLI frontend - for easy automation
|
||||
- GUI frontend - uses modern GTK 3 and looks similar to FSlint
|
||||
- No spying - Czkawka does not have access to the Internet, nor does it collect any user information or statistics
|
||||
- Multilingual - app support multiple languages
|
||||
- Multilingual - support multiple languages like Polish, English or Italian
|
||||
- Multiple tools to use:
|
||||
- Duplicates - Finds duplicates based on file name, size or hash
|
||||
- Empty Folders - Finds empty folders with the help of an advanced algorithm
|
||||
|
@ -113,7 +113,7 @@ Bleachbit is a master at finding and removing temporary files, while Czkawka onl
|
|||
| Non stripped binaries | | • | | |
|
||||
| Redundant whitespace | | • | | |
|
||||
| Overwriting files | | • | | • |
|
||||
| Multiple languages(po) | • | • | • | • |
|
||||
| Multiple languages | • | • | • | • |
|
||||
| Cache support | • | | • | |
|
||||
| In active development | Yes | No | Yes | Yes |
|
||||
|
||||
|
|
|
@ -44,7 +44,6 @@ pub enum DeleteMethod {
|
|||
/// Info struck with helpful information's about results
|
||||
#[derive(Default)]
|
||||
pub struct Info {
|
||||
pub taken_space: u64,
|
||||
pub number_of_real_files: usize,
|
||||
}
|
||||
|
||||
|
@ -302,7 +301,6 @@ impl BigFile {
|
|||
if self.information.number_of_real_files < self.number_of_files_to_check {
|
||||
new_map.entry(*size).or_insert_with(Vec::new);
|
||||
new_map.get_mut(size).unwrap().push(file.clone());
|
||||
self.information.taken_space += size;
|
||||
self.information.number_of_real_files += 1;
|
||||
} else {
|
||||
break;
|
||||
|
@ -445,11 +443,6 @@ impl SaveResults for BigFile {
|
|||
impl PrintResults for BigFile {
|
||||
fn print_results(&self) {
|
||||
let start_time: SystemTime = SystemTime::now();
|
||||
println!(
|
||||
"Found {} files which take {}:",
|
||||
self.information.number_of_real_files,
|
||||
self.information.taken_space.file_size(options::BINARY).unwrap()
|
||||
);
|
||||
for (size, vector) in self.big_files.iter().rev() {
|
||||
// TODO Align all to same width
|
||||
for entry in vector {
|
||||
|
|
|
@ -60,8 +60,6 @@ pub enum TypeOfFile {
|
|||
#[derive(Default)]
|
||||
pub struct Info {
|
||||
pub number_of_broken_files: usize,
|
||||
pub number_of_removed_files: usize,
|
||||
pub number_of_failed_to_remove_files: usize,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
|
@ -550,8 +548,6 @@ impl DebugPrint for BrokenFiles {
|
|||
println!("Errors size - {}", self.text_messages.errors.len());
|
||||
println!("Warnings size - {}", self.text_messages.warnings.len());
|
||||
println!("Messages size - {}", self.text_messages.messages.len());
|
||||
println!("Number of removed files - {}", self.information.number_of_removed_files);
|
||||
println!("Number of failed to remove files - {}", self.information.number_of_failed_to_remove_files);
|
||||
|
||||
println!("### Other");
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::common_messages::Messages;
|
|||
pub struct Directories {
|
||||
pub excluded_directories: Vec<PathBuf>,
|
||||
pub included_directories: Vec<PathBuf>,
|
||||
pub reference_directories: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl Directories {
|
||||
|
@ -15,6 +16,10 @@ impl Directories {
|
|||
Default::default()
|
||||
}
|
||||
|
||||
pub fn set_reference_directory(&mut self, reference_directory: Vec<PathBuf>) {
|
||||
self.reference_directories = reference_directory
|
||||
}
|
||||
|
||||
/// Setting included directories, at least one must be provided
|
||||
pub fn set_included_directory(&mut self, included_directory: Vec<PathBuf>, text_messages: &mut Messages) -> bool {
|
||||
let start_time: SystemTime = SystemTime::now();
|
||||
|
@ -146,15 +151,18 @@ impl Directories {
|
|||
if cfg!(target_family = "windows") {
|
||||
self.included_directories = self.included_directories.iter().map(Common::normalize_windows_path).collect();
|
||||
self.excluded_directories = self.excluded_directories.iter().map(Common::normalize_windows_path).collect();
|
||||
self.reference_directories = self.reference_directories.iter().map(Common::normalize_windows_path).collect();
|
||||
}
|
||||
|
||||
// Remove duplicated entries like: "/", "/"
|
||||
|
||||
self.excluded_directories.sort();
|
||||
self.included_directories.sort();
|
||||
self.reference_directories.sort();
|
||||
|
||||
self.excluded_directories.dedup();
|
||||
self.included_directories.dedup();
|
||||
self.reference_directories.dedup();
|
||||
|
||||
// Optimize for duplicated included directories - "/", "/home". "/home/Pulpit" to "/"
|
||||
if recursive_search {
|
||||
|
@ -251,6 +259,20 @@ impl Directories {
|
|||
|
||||
self.excluded_directories = optimized_excluded;
|
||||
|
||||
// Selecting Reference folders
|
||||
{
|
||||
let mut ref_folders = Vec::new();
|
||||
for folder in &self.reference_directories {
|
||||
if self.included_directories.iter().any(|e| folder.starts_with(&e)) {
|
||||
ref_folders.push(folder.clone());
|
||||
// println!("REF: VALID reference folder {:?}", folder);
|
||||
} else {
|
||||
// println!("REF: Invalid reference folder {:?}", folder);
|
||||
}
|
||||
}
|
||||
self.reference_directories = ref_folders;
|
||||
}
|
||||
|
||||
if self.included_directories.is_empty() {
|
||||
text_messages
|
||||
.errors
|
||||
|
|
|
@ -18,7 +18,8 @@ impl Messages {
|
|||
if !self.messages.is_empty() {
|
||||
text_to_return += "-------------------------------MESSAGES--------------------------------\n";
|
||||
for i in &self.messages {
|
||||
text_to_return += format!("{}\n", i).as_str();
|
||||
text_to_return += i;
|
||||
text_to_return += "\n";
|
||||
}
|
||||
text_to_return += "---------------------------END OF MESSAGES-----------------------------\n";
|
||||
}
|
||||
|
@ -27,7 +28,8 @@ impl Messages {
|
|||
text_to_return += "-------------------------------WARNINGS--------------------------------\n";
|
||||
|
||||
for i in &self.warnings {
|
||||
text_to_return += format!("{}\n", i).as_str();
|
||||
text_to_return += i;
|
||||
text_to_return += "\n";
|
||||
}
|
||||
text_to_return += "---------------------------END OF WARNINGS-----------------------------\n";
|
||||
}
|
||||
|
@ -36,7 +38,8 @@ impl Messages {
|
|||
text_to_return += "--------------------------------ERRORS---------------------------------\n";
|
||||
|
||||
for i in &self.errors {
|
||||
text_to_return += format!("{}\n", i).as_str();
|
||||
text_to_return += i;
|
||||
text_to_return += "\n";
|
||||
}
|
||||
text_to_return += "----------------------------END OF ERRORS------------------------------\n";
|
||||
}
|
||||
|
|
|
@ -92,7 +92,6 @@ pub struct Info {
|
|||
pub number_of_duplicated_files_by_name: usize,
|
||||
pub lost_space_by_size: u64,
|
||||
pub lost_space_by_hash: u64,
|
||||
pub gained_space: u64,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
|
@ -107,7 +106,10 @@ pub struct DuplicateFinder {
|
|||
information: Info,
|
||||
files_with_identical_names: BTreeMap<String, Vec<FileEntry>>, // File Size, File Entry
|
||||
files_with_identical_size: BTreeMap<u64, Vec<FileEntry>>, // File Size, File Entry
|
||||
files_with_identical_hashes: BTreeMap<u64, Vec<Vec<FileEntry>>>, // File Size, File Entry
|
||||
files_with_identical_hashes: BTreeMap<u64, Vec<Vec<FileEntry>>>, // File Size, next grouped by file size, next grouped by hash
|
||||
files_with_identical_names_referenced: BTreeMap<String, (FileEntry, Vec<FileEntry>)>, // File Size, File Entry
|
||||
files_with_identical_size_referenced: BTreeMap<u64, (FileEntry, Vec<FileEntry>)>, // File Size, File Entry
|
||||
files_with_identical_hashes_referenced: BTreeMap<u64, Vec<(FileEntry, Vec<FileEntry>)>>, // File Size, next grouped by file size, next grouped by hash
|
||||
directories: Directories,
|
||||
allowed_extensions: Extensions,
|
||||
excluded_items: ExcludedItems,
|
||||
|
@ -125,6 +127,7 @@ pub struct DuplicateFinder {
|
|||
minimal_cache_file_size: u64,
|
||||
minimal_prehash_cache_file_size: u64,
|
||||
delete_outdated_cache: bool,
|
||||
use_reference_folders: bool,
|
||||
}
|
||||
|
||||
impl DuplicateFinder {
|
||||
|
@ -135,6 +138,9 @@ impl DuplicateFinder {
|
|||
files_with_identical_names: Default::default(),
|
||||
files_with_identical_size: Default::default(),
|
||||
files_with_identical_hashes: Default::default(),
|
||||
files_with_identical_names_referenced: Default::default(),
|
||||
files_with_identical_size_referenced: Default::default(),
|
||||
files_with_identical_hashes_referenced: Default::default(),
|
||||
recursive_search: true,
|
||||
allowed_extensions: Extensions::new(),
|
||||
check_method: CheckingMethod::None,
|
||||
|
@ -152,11 +158,13 @@ impl DuplicateFinder {
|
|||
minimal_cache_file_size: 1024 * 1024 / 4, // By default cache only >= 256 KB files
|
||||
minimal_prehash_cache_file_size: 0,
|
||||
delete_outdated_cache: true,
|
||||
use_reference_folders: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_duplicates(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) {
|
||||
self.directories.optimize_directories(self.recursive_search, &mut self.text_messages);
|
||||
self.use_reference_folders = !self.directories.reference_directories.is_empty();
|
||||
|
||||
match self.check_method {
|
||||
CheckingMethod::Name => {
|
||||
|
@ -270,23 +278,43 @@ impl DuplicateFinder {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn get_use_reference(&self) -> bool {
|
||||
self.use_reference_folders
|
||||
}
|
||||
|
||||
pub fn set_recursive_search(&mut self, recursive_search: bool) {
|
||||
self.recursive_search = recursive_search;
|
||||
}
|
||||
|
||||
pub fn set_included_directory(&mut self, included_directory: Vec<PathBuf>) -> bool {
|
||||
self.directories.set_included_directory(included_directory, &mut self.text_messages)
|
||||
pub fn set_included_directory(&mut self, included_directory: Vec<PathBuf>) {
|
||||
self.directories.set_included_directory(included_directory, &mut self.text_messages);
|
||||
}
|
||||
|
||||
pub fn set_reference_directory(&mut self, reference_directory: Vec<PathBuf>) {
|
||||
self.directories.set_reference_directory(reference_directory);
|
||||
}
|
||||
|
||||
pub fn set_excluded_directory(&mut self, excluded_directory: Vec<PathBuf>) {
|
||||
self.directories.set_excluded_directory(excluded_directory, &mut self.text_messages);
|
||||
}
|
||||
|
||||
pub fn set_excluded_items(&mut self, excluded_items: Vec<String>) {
|
||||
self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages);
|
||||
}
|
||||
pub fn set_allowed_extensions(&mut self, allowed_extensions: String) {
|
||||
self.allowed_extensions.set_allowed_extensions(allowed_extensions, &mut self.text_messages);
|
||||
}
|
||||
|
||||
pub fn set_excluded_items(&mut self, excluded_items: Vec<String>) {
|
||||
self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages);
|
||||
pub fn get_files_with_identical_hashes_referenced(&self) -> &BTreeMap<u64, Vec<(FileEntry, Vec<FileEntry>)>> {
|
||||
&self.files_with_identical_hashes_referenced
|
||||
}
|
||||
|
||||
pub fn get_files_with_identical_name_referenced(&self) -> &BTreeMap<String, (FileEntry, Vec<FileEntry>)> {
|
||||
&self.files_with_identical_names_referenced
|
||||
}
|
||||
|
||||
pub fn get_files_with_identical_size_referenced(&self) -> &BTreeMap<u64, (FileEntry, Vec<FileEntry>)> {
|
||||
&self.files_with_identical_size_referenced
|
||||
}
|
||||
|
||||
fn check_files_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool {
|
||||
|
@ -473,13 +501,53 @@ impl DuplicateFinder {
|
|||
|
||||
for (name, vector) in &self.files_with_identical_names {
|
||||
if vector.len() > 1 {
|
||||
self.information.number_of_duplicated_files_by_name += vector.len() - 1;
|
||||
self.information.number_of_groups_by_name += 1;
|
||||
new_map.insert(name.clone(), vector.clone());
|
||||
}
|
||||
}
|
||||
self.files_with_identical_names = new_map;
|
||||
|
||||
// Reference - only use in size, because later hash will be counted differently
|
||||
if self.use_reference_folders {
|
||||
let mut btree_map = Default::default();
|
||||
mem::swap(&mut self.files_with_identical_names, &mut btree_map);
|
||||
let reference_directories = self.directories.reference_directories.clone();
|
||||
let vec = btree_map
|
||||
.into_iter()
|
||||
.filter_map(|(_size, vec_file_entry)| {
|
||||
let mut files_from_referenced_folders = Vec::new();
|
||||
let mut normal_files = Vec::new();
|
||||
for file_entry in vec_file_entry {
|
||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(&e)) {
|
||||
files_from_referenced_folders.push(file_entry);
|
||||
} else {
|
||||
normal_files.push(file_entry);
|
||||
}
|
||||
}
|
||||
|
||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((files_from_referenced_folders.pop().unwrap(), normal_files))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<(FileEntry, Vec<FileEntry>)>>();
|
||||
for (fe, vec_fe) in vec {
|
||||
self.files_with_identical_names_referenced.insert(fe.path.to_string_lossy().to_string(), (fe, vec_fe));
|
||||
}
|
||||
}
|
||||
|
||||
if self.use_reference_folders {
|
||||
for (_fe, vector) in self.files_with_identical_names_referenced.values() {
|
||||
self.information.number_of_duplicated_files_by_name += vector.len();
|
||||
self.information.number_of_groups_by_name += 1;
|
||||
}
|
||||
} else {
|
||||
for vector in self.files_with_identical_names.values() {
|
||||
self.information.number_of_duplicated_files_by_name += vector.len() - 1;
|
||||
self.information.number_of_groups_by_name += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Common::print_time(start_time, SystemTime::now(), "check_files_name".to_string());
|
||||
true
|
||||
}
|
||||
|
@ -684,10 +752,51 @@ impl DuplicateFinder {
|
|||
let vector = if self.ignore_hard_links { filter_hard_links(&vec) } else { vec };
|
||||
|
||||
if vector.len() > 1 {
|
||||
self.files_with_identical_size.insert(size, vector);
|
||||
}
|
||||
}
|
||||
|
||||
// Reference - only use in size, because later hash will be counted differently
|
||||
if self.use_reference_folders && self.check_method == CheckingMethod::Size {
|
||||
let mut btree_map = Default::default();
|
||||
mem::swap(&mut self.files_with_identical_size, &mut btree_map);
|
||||
let reference_directories = self.directories.reference_directories.clone();
|
||||
let vec = btree_map
|
||||
.into_iter()
|
||||
.filter_map(|(_size, vec_file_entry)| {
|
||||
let mut files_from_referenced_folders = Vec::new();
|
||||
let mut normal_files = Vec::new();
|
||||
for file_entry in vec_file_entry {
|
||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(&e)) {
|
||||
files_from_referenced_folders.push(file_entry);
|
||||
} else {
|
||||
normal_files.push(file_entry);
|
||||
}
|
||||
}
|
||||
|
||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((files_from_referenced_folders.pop().unwrap(), normal_files))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<(FileEntry, Vec<FileEntry>)>>();
|
||||
for (fe, vec_fe) in vec {
|
||||
self.files_with_identical_size_referenced.insert(fe.size, (fe, vec_fe));
|
||||
}
|
||||
}
|
||||
|
||||
if self.use_reference_folders {
|
||||
for (size, (_fe, vector)) in &self.files_with_identical_size_referenced {
|
||||
self.information.number_of_duplicated_files_by_size += vector.len();
|
||||
self.information.number_of_groups_by_size += 1;
|
||||
self.information.lost_space_by_size += (vector.len() as u64) * size;
|
||||
}
|
||||
} else {
|
||||
for (size, vector) in &self.files_with_identical_size {
|
||||
self.information.number_of_duplicated_files_by_size += vector.len() - 1;
|
||||
self.information.number_of_groups_by_size += 1;
|
||||
self.information.lost_space_by_size += (vector.len() as u64 - 1) * size;
|
||||
self.files_with_identical_size.insert(size, vector);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1036,9 +1145,57 @@ impl DuplicateFinder {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////// HASHING END
|
||||
|
||||
// Reference - only use in size, because later hash will be counted differently
|
||||
if self.use_reference_folders {
|
||||
let mut btree_map = Default::default();
|
||||
mem::swap(&mut self.files_with_identical_hashes, &mut btree_map);
|
||||
let reference_directories = self.directories.reference_directories.clone();
|
||||
let vec = btree_map
|
||||
.into_iter()
|
||||
.filter_map(|(_size, vec_vec_file_entry)| {
|
||||
let mut all_results_with_same_size = Vec::new();
|
||||
for vec_file_entry in vec_vec_file_entry {
|
||||
let mut files_from_referenced_folders = Vec::new();
|
||||
let mut normal_files = Vec::new();
|
||||
for file_entry in vec_file_entry {
|
||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(&e)) {
|
||||
files_from_referenced_folders.push(file_entry);
|
||||
} else {
|
||||
normal_files.push(file_entry);
|
||||
}
|
||||
}
|
||||
|
||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||
continue;
|
||||
} else {
|
||||
all_results_with_same_size.push((files_from_referenced_folders.pop().unwrap(), normal_files))
|
||||
}
|
||||
}
|
||||
if all_results_with_same_size.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(all_results_with_same_size)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Vec<(FileEntry, Vec<FileEntry>)>>>();
|
||||
for vec_of_vec in vec {
|
||||
self.files_with_identical_hashes_referenced.insert(vec_of_vec[0].0.size, vec_of_vec);
|
||||
}
|
||||
}
|
||||
|
||||
if self.use_reference_folders {
|
||||
for (size, vector_vectors) in &self.files_with_identical_hashes_referenced {
|
||||
for (_fe, vector) in vector_vectors {
|
||||
self.information.number_of_duplicated_files_by_hash += vector.len();
|
||||
self.information.number_of_groups_by_hash += 1;
|
||||
self.information.lost_space_by_hash += (vector.len() as u64) * size;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size, vector_vectors) in &self.files_with_identical_hashes {
|
||||
for vector in vector_vectors {
|
||||
self.information.number_of_duplicated_files_by_hash += vector.len() - 1;
|
||||
|
@ -1048,8 +1205,6 @@ impl DuplicateFinder {
|
|||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////// HASHING END
|
||||
|
||||
Common::print_time(start_time, SystemTime::now(), "check_files_hash - full hash".to_string());
|
||||
|
||||
// Clean unused data
|
||||
|
@ -1069,22 +1224,19 @@ impl DuplicateFinder {
|
|||
match self.check_method {
|
||||
CheckingMethod::Name => {
|
||||
for vector in self.files_with_identical_names.values() {
|
||||
let tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
||||
self.information.gained_space += tuple.0;
|
||||
let _tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
||||
}
|
||||
}
|
||||
CheckingMethod::Hash => {
|
||||
for vector_vectors in self.files_with_identical_hashes.values() {
|
||||
for vector in vector_vectors.iter() {
|
||||
let tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
||||
self.information.gained_space += tuple.0;
|
||||
let _tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
||||
}
|
||||
}
|
||||
}
|
||||
CheckingMethod::Size => {
|
||||
for vector in self.files_with_identical_size.values() {
|
||||
let tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
||||
self.information.gained_space += tuple.0;
|
||||
let _tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
||||
}
|
||||
}
|
||||
CheckingMethod::None => {
|
||||
|
@ -1140,11 +1292,6 @@ impl DebugPrint for DuplicateFinder {
|
|||
self.information.lost_space_by_hash.file_size(options::BINARY).unwrap(),
|
||||
self.information.lost_space_by_hash
|
||||
);
|
||||
println!(
|
||||
"Gained space by removing duplicated entries - {} ({} bytes)",
|
||||
self.information.gained_space.file_size(options::BINARY).unwrap(),
|
||||
self.information.gained_space
|
||||
);
|
||||
|
||||
println!("### Other");
|
||||
|
||||
|
|
|
@ -43,8 +43,6 @@ pub struct FileEntry {
|
|||
#[derive(Default)]
|
||||
pub struct Info {
|
||||
pub number_of_empty_files: usize,
|
||||
pub number_of_removed_files: usize,
|
||||
pub number_of_failed_to_remove_files: usize,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
|
@ -350,8 +348,6 @@ impl DebugPrint for EmptyFiles {
|
|||
println!("Errors size - {}", self.text_messages.errors.len());
|
||||
println!("Warnings size - {}", self.text_messages.warnings.len());
|
||||
println!("Messages size - {}", self.text_messages.messages.len());
|
||||
println!("Number of removed files - {}", self.information.number_of_removed_files);
|
||||
println!("Number of failed to remove files - {}", self.information.number_of_failed_to_remove_files);
|
||||
|
||||
println!("### Other");
|
||||
|
||||
|
|
|
@ -53,8 +53,6 @@ pub struct FileEntry {
|
|||
#[derive(Default)]
|
||||
pub struct Info {
|
||||
pub number_of_invalid_symlinks: usize,
|
||||
pub number_of_removed_files: usize,
|
||||
pub number_of_failed_to_remove_files: usize,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
|
@ -400,8 +398,6 @@ impl DebugPrint for InvalidSymlinks {
|
|||
println!("Errors size - {}", self.text_messages.errors.len());
|
||||
println!("Warnings size - {}", self.text_messages.warnings.len());
|
||||
println!("Messages size - {}", self.text_messages.messages.len());
|
||||
println!("Number of removed files - {}", self.information.number_of_removed_files);
|
||||
println!("Number of failed to remove files - {}", self.information.number_of_failed_to_remove_files);
|
||||
|
||||
println!("### Other");
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|||
use std::sync::Arc;
|
||||
use std::thread::sleep;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use std::{fs, thread};
|
||||
use std::{fs, mem, thread};
|
||||
|
||||
use audiotags::Tag;
|
||||
use crossbeam_channel::Receiver;
|
||||
|
@ -71,10 +71,8 @@ pub struct FileEntry {
|
|||
/// Info struck with helpful information's about results
|
||||
#[derive(Default)]
|
||||
pub struct Info {
|
||||
pub number_of_music_entries: usize,
|
||||
pub number_of_removed_files: usize,
|
||||
pub number_of_failed_to_remove_files: usize,
|
||||
pub number_of_duplicates_music_files: usize,
|
||||
pub number_of_duplicates: usize,
|
||||
pub number_of_groups: u64,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
|
@ -90,6 +88,7 @@ pub struct SameMusic {
|
|||
music_to_check: Vec<FileEntry>,
|
||||
music_entries: Vec<FileEntry>,
|
||||
duplicated_music_entries: Vec<Vec<FileEntry>>,
|
||||
duplicated_music_entries_referenced: Vec<(FileEntry, Vec<FileEntry>)>,
|
||||
directories: Directories,
|
||||
allowed_extensions: Extensions,
|
||||
excluded_items: ExcludedItems,
|
||||
|
@ -100,6 +99,7 @@ pub struct SameMusic {
|
|||
music_similarity: MusicSimilarity,
|
||||
stopped_search: bool,
|
||||
approximate_comparison: bool,
|
||||
use_reference_folders: bool,
|
||||
}
|
||||
|
||||
impl SameMusic {
|
||||
|
@ -120,11 +120,14 @@ impl SameMusic {
|
|||
duplicated_music_entries: vec![],
|
||||
music_to_check: Vec::with_capacity(2048),
|
||||
approximate_comparison: true,
|
||||
use_reference_folders: false,
|
||||
duplicated_music_entries_referenced: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_same_music(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) {
|
||||
self.directories.optimize_directories(self.recursive_search, &mut self.text_messages);
|
||||
self.use_reference_folders = !self.directories.reference_directories.is_empty();
|
||||
if !self.check_files(stop_receiver, progress_sender) {
|
||||
self.stopped_search = true;
|
||||
return;
|
||||
|
@ -172,8 +175,13 @@ impl SameMusic {
|
|||
self.recursive_search = recursive_search;
|
||||
}
|
||||
|
||||
pub fn set_included_directory(&mut self, included_directory: Vec<PathBuf>) -> bool {
|
||||
self.directories.set_included_directory(included_directory, &mut self.text_messages)
|
||||
/// Set included dir which needs to be relative, exists etc.
|
||||
pub fn set_included_directory(&mut self, included_directory: Vec<PathBuf>) {
|
||||
self.directories.set_included_directory(included_directory, &mut self.text_messages);
|
||||
}
|
||||
|
||||
pub fn set_reference_directory(&mut self, reference_directory: Vec<PathBuf>) {
|
||||
self.directories.set_reference_directory(reference_directory);
|
||||
}
|
||||
|
||||
pub fn set_excluded_directory(&mut self, excluded_directory: Vec<PathBuf>) {
|
||||
|
@ -183,6 +191,7 @@ impl SameMusic {
|
|||
pub fn set_excluded_items(&mut self, excluded_items: Vec<String>) {
|
||||
self.excluded_items.set_excluded_items(excluded_items, &mut self.text_messages);
|
||||
}
|
||||
|
||||
pub fn set_allowed_extensions(&mut self, allowed_extensions: String) {
|
||||
self.allowed_extensions.set_allowed_extensions(allowed_extensions, &mut self.text_messages);
|
||||
}
|
||||
|
@ -198,6 +207,22 @@ impl SameMusic {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn get_similar_music_referenced(&self) -> &Vec<(FileEntry, Vec<FileEntry>)> {
|
||||
&self.duplicated_music_entries_referenced
|
||||
}
|
||||
|
||||
pub fn get_number_of_base_duplicated_files(&self) -> usize {
|
||||
if self.use_reference_folders {
|
||||
self.duplicated_music_entries_referenced.len()
|
||||
} else {
|
||||
self.duplicated_music_entries.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_use_reference(&self) -> bool {
|
||||
self.use_reference_folders
|
||||
}
|
||||
|
||||
/// Check files for any with size == 0
|
||||
fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) -> bool {
|
||||
let start_time: SystemTime = SystemTime::now();
|
||||
|
@ -382,7 +407,6 @@ impl SameMusic {
|
|||
// End thread which send info to gui
|
||||
progress_thread_run.store(false, Ordering::Relaxed);
|
||||
progress_thread_handle.join().unwrap();
|
||||
self.information.number_of_music_entries = self.music_entries.len();
|
||||
|
||||
Common::print_time(start_time, SystemTime::now(), "check_files".to_string());
|
||||
true
|
||||
|
@ -669,15 +693,50 @@ impl SameMusic {
|
|||
// new_duplicates = Vec::new();
|
||||
}
|
||||
|
||||
self.duplicated_music_entries = old_duplicates;
|
||||
|
||||
for vec in &self.duplicated_music_entries {
|
||||
self.information.number_of_duplicates_music_files += vec.len() - 1;
|
||||
}
|
||||
// End thread which send info to gui
|
||||
progress_thread_run.store(false, Ordering::Relaxed);
|
||||
progress_thread_handle.join().unwrap();
|
||||
|
||||
self.duplicated_music_entries = old_duplicates;
|
||||
|
||||
if self.use_reference_folders {
|
||||
let mut similars_vector = Default::default();
|
||||
mem::swap(&mut self.duplicated_music_entries, &mut similars_vector);
|
||||
let reference_directories = self.directories.reference_directories.clone();
|
||||
self.duplicated_music_entries_referenced = similars_vector
|
||||
.into_iter()
|
||||
.filter_map(|vec_file_entry| {
|
||||
let mut files_from_referenced_folders = Vec::new();
|
||||
let mut normal_files = Vec::new();
|
||||
for file_entry in vec_file_entry {
|
||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(&e)) {
|
||||
files_from_referenced_folders.push(file_entry);
|
||||
} else {
|
||||
normal_files.push(file_entry);
|
||||
}
|
||||
}
|
||||
|
||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((files_from_referenced_folders.pop().unwrap(), normal_files))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<(FileEntry, Vec<FileEntry>)>>();
|
||||
}
|
||||
|
||||
if self.use_reference_folders {
|
||||
for (_fe, vector) in &self.duplicated_music_entries_referenced {
|
||||
self.information.number_of_duplicates += vector.len();
|
||||
self.information.number_of_groups += 1;
|
||||
}
|
||||
} else {
|
||||
for vector in &self.duplicated_music_entries {
|
||||
self.information.number_of_duplicates += vector.len() - 1;
|
||||
self.information.number_of_groups += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Common::print_time(start_time, SystemTime::now(), "check_for_duplicates".to_string());
|
||||
|
||||
// Clear unused data
|
||||
|
@ -735,9 +794,6 @@ impl DebugPrint for SameMusic {
|
|||
println!("Errors size - {}", self.text_messages.errors.len());
|
||||
println!("Warnings size - {}", self.text_messages.warnings.len());
|
||||
println!("Messages size - {}", self.text_messages.messages.len());
|
||||
println!("Number of removed files - {}", self.information.number_of_removed_files);
|
||||
println!("Number of failed to remove files - {}", self.information.number_of_failed_to_remove_files);
|
||||
println!("Number of duplicated music files - {}", self.information.number_of_duplicates_music_files);
|
||||
|
||||
println!("### Other");
|
||||
|
||||
|
@ -780,7 +836,7 @@ impl SaveResults for SameMusic {
|
|||
}
|
||||
|
||||
if !self.music_entries.is_empty() {
|
||||
writeln!(writer, "Found {} same music files.", self.information.number_of_music_entries).unwrap();
|
||||
writeln!(writer, "Found {} same music files.", self.information.number_of_duplicates).unwrap();
|
||||
for file_entry in self.music_entries.iter() {
|
||||
writeln!(writer, "{}", file_entry.path.display()).unwrap();
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@ pub struct SimilarImages {
|
|||
excluded_items: ExcludedItems,
|
||||
bktree: BKTree<Vec<u8>, Hamming>,
|
||||
similar_vectors: Vec<Vec<FileEntry>>,
|
||||
similar_referenced_vectors: Vec<(FileEntry, Vec<FileEntry>)>,
|
||||
recursive_search: bool,
|
||||
minimal_file_size: u64,
|
||||
maximal_file_size: u64,
|
||||
|
@ -106,14 +107,14 @@ pub struct SimilarImages {
|
|||
use_cache: bool,
|
||||
delete_outdated_cache: bool,
|
||||
exclude_images_with_same_size: bool,
|
||||
use_reference_folders: bool,
|
||||
}
|
||||
|
||||
/// Info struck with helpful information's about results
|
||||
#[derive(Default)]
|
||||
pub struct Info {
|
||||
pub number_of_removed_files: usize,
|
||||
pub number_of_failed_to_remove_files: usize,
|
||||
pub gained_space: u64,
|
||||
pub number_of_duplicates: usize,
|
||||
pub number_of_groups: u64,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
|
@ -134,6 +135,7 @@ impl SimilarImages {
|
|||
allowed_extensions: Extensions::new(),
|
||||
bktree: BKTree::new(Hamming),
|
||||
similar_vectors: vec![],
|
||||
similar_referenced_vectors: Default::default(),
|
||||
recursive_search: true,
|
||||
minimal_file_size: 1024 * 16, // 16 KB should be enough to exclude too small images from search
|
||||
maximal_file_size: u64::MAX,
|
||||
|
@ -147,6 +149,7 @@ impl SimilarImages {
|
|||
use_cache: true,
|
||||
delete_outdated_cache: true,
|
||||
exclude_images_with_same_size: false,
|
||||
use_reference_folders: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,6 +190,14 @@ impl SimilarImages {
|
|||
&self.similar_vectors
|
||||
}
|
||||
|
||||
pub fn get_similar_images_referenced(&self) -> &Vec<(FileEntry, Vec<FileEntry>)> {
|
||||
&self.similar_referenced_vectors
|
||||
}
|
||||
|
||||
pub fn get_use_reference(&self) -> bool {
|
||||
self.use_reference_folders
|
||||
}
|
||||
|
||||
pub const fn get_information(&self) -> &Info {
|
||||
&self.information
|
||||
}
|
||||
|
@ -221,6 +232,7 @@ impl SimilarImages {
|
|||
/// Public function used by CLI to search for empty folders
|
||||
pub fn find_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) {
|
||||
self.directories.optimize_directories(true, &mut self.text_messages);
|
||||
self.use_reference_folders = !self.directories.reference_directories.is_empty();
|
||||
if !self.check_for_similar_images(stop_receiver, progress_sender) {
|
||||
self.stopped_search = true;
|
||||
return;
|
||||
|
@ -695,8 +707,46 @@ impl SimilarImages {
|
|||
}
|
||||
}
|
||||
|
||||
if self.use_reference_folders {
|
||||
let mut similars_vector = Default::default();
|
||||
mem::swap(&mut self.similar_vectors, &mut similars_vector);
|
||||
let reference_directories = self.directories.reference_directories.clone();
|
||||
self.similar_referenced_vectors = similars_vector
|
||||
.into_iter()
|
||||
.filter_map(|vec_file_entry| {
|
||||
let mut files_from_referenced_folders = Vec::new();
|
||||
let mut normal_files = Vec::new();
|
||||
for file_entry in vec_file_entry {
|
||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(&e)) {
|
||||
files_from_referenced_folders.push(file_entry);
|
||||
} else {
|
||||
normal_files.push(file_entry);
|
||||
}
|
||||
}
|
||||
|
||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((files_from_referenced_folders.pop().unwrap(), normal_files))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<(FileEntry, Vec<FileEntry>)>>();
|
||||
}
|
||||
|
||||
Common::print_time(hash_map_modification, SystemTime::now(), "sort_images - selecting data from BtreeMap".to_string());
|
||||
|
||||
if self.use_reference_folders {
|
||||
for (_fe, vector) in &self.similar_referenced_vectors {
|
||||
self.information.number_of_duplicates += vector.len();
|
||||
self.information.number_of_groups += 1;
|
||||
}
|
||||
} else {
|
||||
for vector in &self.similar_vectors {
|
||||
self.information.number_of_duplicates += vector.len() - 1;
|
||||
self.information.number_of_groups += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean unused data
|
||||
self.image_hashes = Default::default();
|
||||
self.images_to_check = Default::default();
|
||||
|
@ -710,6 +760,10 @@ impl SimilarImages {
|
|||
self.directories.set_included_directory(included_directory, &mut self.text_messages);
|
||||
}
|
||||
|
||||
pub fn set_reference_directory(&mut self, reference_directory: Vec<PathBuf>) {
|
||||
self.directories.set_reference_directory(reference_directory);
|
||||
}
|
||||
|
||||
pub fn set_excluded_directory(&mut self, excluded_directory: Vec<PathBuf>) {
|
||||
self.directories.set_excluded_directory(excluded_directory, &mut self.text_messages);
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ pub struct SimilarVideos {
|
|||
excluded_items: ExcludedItems,
|
||||
allowed_extensions: Extensions,
|
||||
similar_vectors: Vec<Vec<FileEntry>>,
|
||||
similar_referenced_vectors: Vec<(FileEntry, Vec<FileEntry>)>,
|
||||
recursive_search: bool,
|
||||
minimal_file_size: u64,
|
||||
maximal_file_size: u64,
|
||||
|
@ -78,14 +79,14 @@ pub struct SimilarVideos {
|
|||
tolerance: i32,
|
||||
delete_outdated_cache: bool,
|
||||
exclude_videos_with_same_size: bool,
|
||||
use_reference_folders: bool,
|
||||
}
|
||||
|
||||
/// Info struck with helpful information's about results
|
||||
#[derive(Default)]
|
||||
pub struct Info {
|
||||
pub number_of_removed_files: usize,
|
||||
pub number_of_failed_to_remove_files: usize,
|
||||
pub gained_space: u64,
|
||||
pub number_of_duplicates: usize,
|
||||
pub number_of_groups: u64,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
|
@ -115,6 +116,8 @@ impl SimilarVideos {
|
|||
tolerance: 10,
|
||||
delete_outdated_cache: false,
|
||||
exclude_videos_with_same_size: false,
|
||||
use_reference_folders: false,
|
||||
similar_referenced_vectors: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +174,21 @@ impl SimilarVideos {
|
|||
t => t,
|
||||
};
|
||||
}
|
||||
pub fn get_similar_videos_referenced(&self) -> &Vec<(FileEntry, Vec<FileEntry>)> {
|
||||
&self.similar_referenced_vectors
|
||||
}
|
||||
|
||||
pub fn get_number_of_base_duplicated_files(&self) -> usize {
|
||||
if self.use_reference_folders {
|
||||
self.similar_referenced_vectors.len()
|
||||
} else {
|
||||
self.similar_vectors.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_use_reference(&self) -> bool {
|
||||
self.use_reference_folders
|
||||
}
|
||||
|
||||
/// Public function used by CLI to search for empty folders
|
||||
pub fn find_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender<ProgressData>>) {
|
||||
|
@ -178,6 +196,7 @@ impl SimilarVideos {
|
|||
self.text_messages.errors.push("Cannot find proper installation of FFmpeg.".to_string());
|
||||
} else {
|
||||
self.directories.optimize_directories(true, &mut self.text_messages);
|
||||
self.use_reference_folders = !self.directories.reference_directories.is_empty();
|
||||
if !self.check_for_similar_videos(stop_receiver, progress_sender) {
|
||||
self.stopped_search = true;
|
||||
return;
|
||||
|
@ -539,6 +558,44 @@ impl SimilarVideos {
|
|||
|
||||
self.similar_vectors = collected_similar_videos;
|
||||
|
||||
if self.use_reference_folders {
|
||||
let mut similars_vector = Default::default();
|
||||
mem::swap(&mut self.similar_vectors, &mut similars_vector);
|
||||
let reference_directories = self.directories.reference_directories.clone();
|
||||
self.similar_referenced_vectors = similars_vector
|
||||
.into_iter()
|
||||
.filter_map(|vec_file_entry| {
|
||||
let mut files_from_referenced_folders = Vec::new();
|
||||
let mut normal_files = Vec::new();
|
||||
for file_entry in vec_file_entry {
|
||||
if reference_directories.iter().any(|e| file_entry.path.starts_with(&e)) {
|
||||
files_from_referenced_folders.push(file_entry);
|
||||
} else {
|
||||
normal_files.push(file_entry);
|
||||
}
|
||||
}
|
||||
|
||||
if files_from_referenced_folders.is_empty() || normal_files.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((files_from_referenced_folders.pop().unwrap(), normal_files))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<(FileEntry, Vec<FileEntry>)>>();
|
||||
}
|
||||
|
||||
if self.use_reference_folders {
|
||||
for (_fe, vector) in &self.similar_referenced_vectors {
|
||||
self.information.number_of_duplicates += vector.len();
|
||||
self.information.number_of_groups += 1;
|
||||
}
|
||||
} else {
|
||||
for vector in &self.similar_vectors {
|
||||
self.information.number_of_duplicates += vector.len() - 1;
|
||||
self.information.number_of_groups += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Common::print_time(hash_map_modification, SystemTime::now(), "sort_videos - selecting data from BtreeMap".to_string());
|
||||
|
||||
// Clean unused data
|
||||
|
@ -553,6 +610,10 @@ impl SimilarVideos {
|
|||
self.directories.set_included_directory(included_directory, &mut self.text_messages);
|
||||
}
|
||||
|
||||
pub fn set_reference_directory(&mut self, reference_directory: Vec<PathBuf>) {
|
||||
self.directories.set_reference_directory(reference_directory);
|
||||
}
|
||||
|
||||
pub fn set_excluded_directory(&mut self, excluded_directory: Vec<PathBuf>) {
|
||||
self.directories.set_excluded_directory(excluded_directory, &mut self.text_messages);
|
||||
}
|
||||
|
|
|
@ -42,8 +42,6 @@ pub struct FileEntry {
|
|||
#[derive(Default)]
|
||||
pub struct Info {
|
||||
pub number_of_temporary_files: usize,
|
||||
pub number_of_removed_files: usize,
|
||||
pub number_of_failed_to_remove_files: usize,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
|
@ -357,8 +355,6 @@ impl DebugPrint for Temporary {
|
|||
println!("Errors size - {}", self.text_messages.errors.len());
|
||||
println!("Warnings size - {}", self.text_messages.warnings.len());
|
||||
println!("Messages size - {}", self.text_messages.messages.len());
|
||||
println!("Number of removed files - {}", self.information.number_of_removed_files);
|
||||
println!("Number of failed to remove files - {}", self.information.number_of_failed_to_remove_files);
|
||||
|
||||
println!("### Other");
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ use crate::gui_data::GuiData;
|
|||
use crate::help_combo_box::IMAGES_HASH_SIZE_COMBO_BOX;
|
||||
use crate::help_functions::*;
|
||||
use crate::notebook_enums::*;
|
||||
use crate::opening_selecting_records::*;
|
||||
use czkawka_core::localizer::generate_translation_hashmap;
|
||||
|
||||
pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<Message>) {
|
||||
let combo_box_image_hash_size = gui_data.main_notebook.combo_box_image_hash_size.clone();
|
||||
|
@ -76,6 +78,12 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
if df.get_stopped_search() {
|
||||
entry_info.set_text(&fl!("compute_stopped_by_user"));
|
||||
} else {
|
||||
if df.get_use_reference() {
|
||||
tree_view_duplicate_finder.selection().set_select_function(Some(Box::new(select_function_always_true)));
|
||||
} else {
|
||||
tree_view_duplicate_finder.selection().set_select_function(Some(Box::new(select_function_duplicates)));
|
||||
}
|
||||
|
||||
let information = df.get_information();
|
||||
let text_messages = df.get_text_messages();
|
||||
|
||||
|
@ -83,31 +91,31 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
let duplicates_size: u64;
|
||||
let duplicates_group: usize;
|
||||
|
||||
let fl_found = fl!("compute_found");
|
||||
let fl_groups = fl!("compute_groups");
|
||||
let fl_groups_which_took = fl!("compute_groups_which_took");
|
||||
let fl_duplicated_files_in = fl!("compute_duplicated_files_in");
|
||||
|
||||
match df.get_check_method() {
|
||||
CheckingMethod::Name => {
|
||||
duplicates_number = information.number_of_duplicated_files_by_name;
|
||||
// duplicates_size = 0;
|
||||
duplicates_group = information.number_of_groups_by_name;
|
||||
entry_info.set_text(format!("{} {} {} {} {}", fl_found, duplicates_number, fl_duplicated_files_in, duplicates_group, fl_groups).as_str());
|
||||
entry_info.set_text(
|
||||
fl!(
|
||||
"compute_found_duplicates_name",
|
||||
generate_translation_hashmap(vec![("number_files", duplicates_number.to_string()), ("number_groups", duplicates_group.to_string())])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
CheckingMethod::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;
|
||||
entry_info.set_text(
|
||||
format!(
|
||||
"{} {} {} {} {} {}.",
|
||||
fl_found,
|
||||
duplicates_number,
|
||||
fl_duplicated_files_in,
|
||||
duplicates_group,
|
||||
fl_groups_which_took,
|
||||
duplicates_size.file_size(options::BINARY).unwrap()
|
||||
fl!(
|
||||
"compute_found_duplicates_hash_size",
|
||||
generate_translation_hashmap(vec![
|
||||
("number_files", duplicates_number.to_string()),
|
||||
("number_groups", duplicates_group.to_string()),
|
||||
("size", duplicates_size.file_size(options::BINARY).unwrap())
|
||||
])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
@ -117,14 +125,13 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
duplicates_size = information.lost_space_by_size;
|
||||
duplicates_group = information.number_of_groups_by_size;
|
||||
entry_info.set_text(
|
||||
format!(
|
||||
"{} {} {} {} {} {}.",
|
||||
fl_found,
|
||||
duplicates_number,
|
||||
fl_duplicated_files_in,
|
||||
duplicates_group,
|
||||
fl_groups_which_took,
|
||||
duplicates_size.file_size(options::BINARY).unwrap()
|
||||
fl!(
|
||||
"compute_found_duplicates_hash_size",
|
||||
generate_translation_hashmap(vec![
|
||||
("number_files", duplicates_number.to_string()),
|
||||
("number_groups", duplicates_group.to_string()),
|
||||
("size", duplicates_size.file_size(options::BINARY).unwrap())
|
||||
])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
@ -138,11 +145,12 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
{
|
||||
let list_store = get_list_store(&tree_view_duplicate_finder);
|
||||
|
||||
if df.get_use_reference() {
|
||||
match df.get_check_method() {
|
||||
CheckingMethod::Name => {
|
||||
let btreemap = df.get_files_sorted_by_names();
|
||||
let btreemap = df.get_files_with_identical_name_referenced();
|
||||
|
||||
for (name, vector) in btreemap.iter().rev() {
|
||||
for (_name, (base_file_entry, vector)) in btreemap.iter().rev() {
|
||||
// Sort
|
||||
let vector = if vector.len() >= 2 {
|
||||
let mut vector = vector.clone();
|
||||
|
@ -155,10 +163,195 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
vector.clone()
|
||||
};
|
||||
|
||||
let values: [(u32, &dyn ToValue); 8] = [
|
||||
// HEADER
|
||||
let (directory, file) = split_path(&base_file_entry.path);
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &false),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(ColumnsDuplicates::Name as u32, &name),
|
||||
(ColumnsDuplicates::Size as u32, (&base_file_entry.size.file_size(options::BINARY).unwrap())),
|
||||
(ColumnsDuplicates::Name as u32, &file),
|
||||
(ColumnsDuplicates::Path as u32, &directory),
|
||||
(
|
||||
ColumnsDuplicates::Modification as u32,
|
||||
&(format!(
|
||||
"{} - ({})",
|
||||
NaiveDateTime::from_timestamp(base_file_entry.modified_date as i64, 0),
|
||||
base_file_entry.size.file_size(options::BINARY).unwrap()
|
||||
)),
|
||||
),
|
||||
(ColumnsDuplicates::ModificationAsSecs as u32, &(base_file_entry.modified_date)),
|
||||
(ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())),
|
||||
(ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
|
||||
list_store.set(&list_store.append(), &values);
|
||||
|
||||
// MEAT
|
||||
for entry in vector {
|
||||
let (directory, file) = split_path(&entry.path);
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &true),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(ColumnsDuplicates::Size as u32, (&entry.size.file_size(options::BINARY).unwrap())),
|
||||
(ColumnsDuplicates::Name as u32, &file),
|
||||
(ColumnsDuplicates::Path as u32, &directory),
|
||||
(
|
||||
ColumnsDuplicates::Modification as u32,
|
||||
&(format!(
|
||||
"{} - ({})",
|
||||
NaiveDateTime::from_timestamp(entry.modified_date as i64, 0),
|
||||
entry.size.file_size(options::BINARY).unwrap()
|
||||
)),
|
||||
),
|
||||
(ColumnsDuplicates::ModificationAsSecs as u32, &(entry.modified_date)),
|
||||
(ColumnsDuplicates::Color as u32, &(MAIN_ROW_COLOR.to_string())),
|
||||
(ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
}
|
||||
CheckingMethod::Hash => {
|
||||
let btreemap = df.get_files_with_identical_hashes_referenced();
|
||||
|
||||
for (_size, vectors_vector) in btreemap.iter().rev() {
|
||||
for (base_file_entry, vector) in vectors_vector {
|
||||
// Sort
|
||||
let vector = if vector.len() >= 2 {
|
||||
let mut vector = vector.clone();
|
||||
vector.sort_by_key(|e| {
|
||||
let t = split_path(e.path.as_path());
|
||||
(t.0, t.1)
|
||||
});
|
||||
vector
|
||||
} else {
|
||||
vector.clone()
|
||||
};
|
||||
|
||||
// HEADER
|
||||
let (directory, file) = split_path(&base_file_entry.path);
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &false),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(ColumnsDuplicates::Size as u32, (&base_file_entry.size.file_size(options::BINARY).unwrap())),
|
||||
(ColumnsDuplicates::Name as u32, &file),
|
||||
(ColumnsDuplicates::Path as u32, &directory),
|
||||
(
|
||||
ColumnsDuplicates::Modification as u32,
|
||||
&(NaiveDateTime::from_timestamp(base_file_entry.modified_date as i64, 0).to_string()),
|
||||
),
|
||||
(ColumnsDuplicates::ModificationAsSecs as u32, &(base_file_entry.modified_date)),
|
||||
(ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())),
|
||||
(ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
|
||||
// MEAT
|
||||
list_store.set(&list_store.append(), &values);
|
||||
for entry in vector {
|
||||
let (directory, file) = split_path(&entry.path);
|
||||
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &true),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(ColumnsDuplicates::Size as u32, (&entry.size.file_size(options::BINARY).unwrap())),
|
||||
(ColumnsDuplicates::Name as u32, &file),
|
||||
(ColumnsDuplicates::Path as u32, &directory),
|
||||
(
|
||||
ColumnsDuplicates::Modification as u32,
|
||||
&(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string()),
|
||||
),
|
||||
(ColumnsDuplicates::ModificationAsSecs as u32, &(entry.modified_date)),
|
||||
(ColumnsDuplicates::Color as u32, &(MAIN_ROW_COLOR.to_string())),
|
||||
(ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CheckingMethod::Size => {
|
||||
let btreemap = df.get_files_with_identical_size_referenced();
|
||||
|
||||
for (_size, (base_file_entry, vector)) in btreemap.iter().rev() {
|
||||
// Sort
|
||||
let vector = if vector.len() >= 2 {
|
||||
let mut vector = vector.clone();
|
||||
vector.sort_by_key(|e| {
|
||||
let t = split_path(e.path.as_path());
|
||||
(t.0, t.1)
|
||||
});
|
||||
vector
|
||||
} else {
|
||||
vector.clone()
|
||||
};
|
||||
|
||||
// HEADER
|
||||
let (directory, file) = split_path(&base_file_entry.path);
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &false),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(ColumnsDuplicates::Size as u32, (&base_file_entry.size.file_size(options::BINARY).unwrap())),
|
||||
(ColumnsDuplicates::Name as u32, &file),
|
||||
(ColumnsDuplicates::Path as u32, &directory),
|
||||
(
|
||||
ColumnsDuplicates::Modification as u32,
|
||||
&(NaiveDateTime::from_timestamp(base_file_entry.modified_date as i64, 0).to_string()),
|
||||
),
|
||||
(ColumnsDuplicates::ModificationAsSecs as u32, &(base_file_entry.modified_date)),
|
||||
(ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())),
|
||||
(ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
|
||||
// MEAT
|
||||
list_store.set(&list_store.append(), &values);
|
||||
for entry in vector {
|
||||
let (directory, file) = split_path(&entry.path);
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &true),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(ColumnsDuplicates::Size as u32, (&entry.size.file_size(options::BINARY).unwrap())),
|
||||
(ColumnsDuplicates::Name as u32, &file),
|
||||
(ColumnsDuplicates::Path as u32, &directory),
|
||||
(
|
||||
ColumnsDuplicates::Modification as u32,
|
||||
&(NaiveDateTime::from_timestamp(entry.modified_date as i64, 0).to_string()),
|
||||
),
|
||||
(ColumnsDuplicates::ModificationAsSecs as u32, &(entry.modified_date)),
|
||||
(ColumnsDuplicates::Color as u32, &(MAIN_ROW_COLOR.to_string())),
|
||||
(ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
}
|
||||
CheckingMethod::None => {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match df.get_check_method() {
|
||||
CheckingMethod::Name => {
|
||||
let btreemap = df.get_files_sorted_by_names();
|
||||
|
||||
for (_name, vector) in btreemap.iter().rev() {
|
||||
// Sort
|
||||
let vector = if vector.len() >= 2 {
|
||||
let mut vector = vector.clone();
|
||||
vector.sort_by_key(|e| {
|
||||
let t = split_path(e.path.as_path());
|
||||
(t.0, t.1)
|
||||
});
|
||||
vector
|
||||
} else {
|
||||
vector.clone()
|
||||
};
|
||||
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &false),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(ColumnsDuplicates::Size as u32, (&"".to_string())),
|
||||
(ColumnsDuplicates::Name as u32, (&"".to_string())),
|
||||
(ColumnsDuplicates::Path as u32, (&(format!("{} results", vector.len())))),
|
||||
(ColumnsDuplicates::Modification as u32, (&"".to_string())), // No text in 3 column
|
||||
(ColumnsDuplicates::ModificationAsSecs as u32, (&(0))), // Not used here
|
||||
|
@ -169,9 +362,10 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
list_store.set(&list_store.append(), &values);
|
||||
for entry in vector {
|
||||
let (directory, file) = split_path(&entry.path);
|
||||
let values: [(u32, &dyn ToValue); 8] = [
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &true),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(ColumnsDuplicates::Size as u32, (&entry.size.file_size(options::BINARY).unwrap())),
|
||||
(ColumnsDuplicates::Name as u32, &file),
|
||||
(ColumnsDuplicates::Path as u32, &directory),
|
||||
(
|
||||
|
@ -193,7 +387,7 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
CheckingMethod::Hash => {
|
||||
let btreemap = df.get_files_sorted_by_hash();
|
||||
|
||||
for (size, vectors_vector) in btreemap.iter().rev() {
|
||||
for (_size, vectors_vector) in btreemap.iter().rev() {
|
||||
for vector in vectors_vector {
|
||||
// Sort
|
||||
let vector = if vector.len() >= 2 {
|
||||
|
@ -207,23 +401,12 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
vector.clone()
|
||||
};
|
||||
|
||||
let values: [(u32, &dyn ToValue); 8] = [
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &false),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(
|
||||
ColumnsDuplicates::Name as u32,
|
||||
&(format!("{} x {} ({} {})", vector.len(), size.file_size(options::BINARY).unwrap(), size, fl!("general_bytes"))),
|
||||
),
|
||||
(
|
||||
ColumnsDuplicates::Path as u32,
|
||||
&(format!(
|
||||
"{} ({} {}) {}",
|
||||
((vector.len() - 1) as u64 * *size as u64).file_size(options::BINARY).unwrap(),
|
||||
(vector.len() - 1) as u64 * *size as u64,
|
||||
fl!("general_bytes"),
|
||||
fl!("general_lost")
|
||||
)),
|
||||
),
|
||||
(ColumnsDuplicates::Size as u32, (&"".to_string())),
|
||||
(ColumnsDuplicates::Name as u32, (&"".to_string())),
|
||||
(ColumnsDuplicates::Path as u32, (&"".to_string())),
|
||||
(ColumnsDuplicates::Modification as u32, &"".to_string()), // No text in 3 column
|
||||
(ColumnsDuplicates::ModificationAsSecs as u32, &(0)),
|
||||
(ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())),
|
||||
|
@ -234,9 +417,10 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
for entry in vector {
|
||||
let (directory, file) = split_path(&entry.path);
|
||||
|
||||
let values: [(u32, &dyn ToValue); 8] = [
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &true),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(ColumnsDuplicates::Size as u32, (&entry.size.file_size(options::BINARY).unwrap())),
|
||||
(ColumnsDuplicates::Name as u32, &file),
|
||||
(ColumnsDuplicates::Path as u32, &directory),
|
||||
(
|
||||
|
@ -256,7 +440,7 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
CheckingMethod::Size => {
|
||||
let btreemap = df.get_files_sorted_by_size();
|
||||
|
||||
for (size, vector) in btreemap.iter().rev() {
|
||||
for (_size, vector) in btreemap.iter().rev() {
|
||||
// Sort
|
||||
let vector = if vector.len() >= 2 {
|
||||
let mut vector = vector.clone();
|
||||
|
@ -268,23 +452,12 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
} else {
|
||||
vector.clone()
|
||||
};
|
||||
let values: [(u32, &dyn ToValue); 8] = [
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &false),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(
|
||||
ColumnsDuplicates::Name as u32,
|
||||
&(format!("{} x {} ({} {})", vector.len(), size.file_size(options::BINARY).unwrap(), size, fl!("general_bytes"))),
|
||||
),
|
||||
(
|
||||
ColumnsDuplicates::Path as u32,
|
||||
&(format!(
|
||||
"{} ({} {}) {}",
|
||||
((vector.len() - 1) as u64 * *size as u64).file_size(options::BINARY).unwrap(),
|
||||
(vector.len() - 1) as u64 * *size as u64,
|
||||
fl!("general_bytes"),
|
||||
fl!("general_lost")
|
||||
)),
|
||||
),
|
||||
(ColumnsDuplicates::Size as u32, (&"".to_string())),
|
||||
(ColumnsDuplicates::Name as u32, (&"".to_string())),
|
||||
(ColumnsDuplicates::Path as u32, (&"".to_string())),
|
||||
(ColumnsDuplicates::Modification as u32, &"".to_string()), // No text in 3 column
|
||||
(ColumnsDuplicates::ModificationAsSecs as u32, &(0)), // Not used here
|
||||
(ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())),
|
||||
|
@ -294,9 +467,10 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
list_store.set(&list_store.append(), &values);
|
||||
for entry in vector {
|
||||
let (directory, file) = split_path(&entry.path);
|
||||
let values: [(u32, &dyn ToValue); 8] = [
|
||||
let values: [(u32, &dyn ToValue); 9] = [
|
||||
(ColumnsDuplicates::ActivatableSelectButton as u32, &true),
|
||||
(ColumnsDuplicates::SelectionButton as u32, &false),
|
||||
(ColumnsDuplicates::Size as u32, (&entry.size.file_size(options::BINARY).unwrap())),
|
||||
(ColumnsDuplicates::Name as u32, &file),
|
||||
(ColumnsDuplicates::Path as u32, &directory),
|
||||
(
|
||||
|
@ -315,7 +489,7 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
print_text_messages_to_text_view(text_messages, &text_view_errors);
|
||||
}
|
||||
|
||||
|
@ -347,7 +521,13 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
|
||||
let empty_folder_number: usize = information.number_of_empty_folders;
|
||||
|
||||
entry_info.set_text(format!("{} {} {}.", fl!("compute_found"), empty_folder_number, fl!("compute_empty_folders")).as_str());
|
||||
entry_info.set_text(
|
||||
fl!(
|
||||
"compute_found_empty_folders",
|
||||
generate_translation_hashmap(vec![("number_files", empty_folder_number.to_string()),])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
// Create GUI
|
||||
{
|
||||
|
@ -406,7 +586,13 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
|
||||
let empty_files_number: usize = information.number_of_empty_files;
|
||||
|
||||
entry_info.set_text(format!("{} {} {}.", fl!("compute_found"), empty_files_number, fl!("compute_empty_files")).as_str());
|
||||
entry_info.set_text(
|
||||
fl!(
|
||||
"compute_found_empty_files",
|
||||
generate_translation_hashmap(vec![("number_files", empty_files_number.to_string()),])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
// Create GUI
|
||||
{
|
||||
|
@ -466,7 +652,13 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
|
||||
let biggest_files_number: usize = information.number_of_real_files;
|
||||
|
||||
entry_info.set_text(format!("{} {} {}.", fl!("compute_found"), biggest_files_number, fl!("compute_biggest_files")).as_str());
|
||||
entry_info.set_text(
|
||||
fl!(
|
||||
"compute_found_big_files",
|
||||
generate_translation_hashmap(vec![("number_files", biggest_files_number.to_string()),])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
// Create GUI
|
||||
{
|
||||
|
@ -530,7 +722,13 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
let text_messages = tf.get_text_messages();
|
||||
|
||||
let temporary_files_number: usize = information.number_of_temporary_files;
|
||||
entry_info.set_text(format!("{} {} {}.", fl!("compute_found"), temporary_files_number, fl!("compute_temporary_files")).as_str());
|
||||
entry_info.set_text(
|
||||
fl!(
|
||||
"compute_found_temporary_files",
|
||||
generate_translation_hashmap(vec![("number_files", temporary_files_number.to_string()),])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
// Create GUI
|
||||
{
|
||||
|
@ -585,18 +783,25 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
if sf.get_stopped_search() {
|
||||
entry_info.set_text(&fl!("compute_stopped_by_user"));
|
||||
} else {
|
||||
//let information = sf.get_information();
|
||||
if sf.get_use_reference() {
|
||||
tree_view_similar_images_finder.selection().set_select_function(Some(Box::new(select_function_always_true)));
|
||||
} else {
|
||||
tree_view_similar_images_finder
|
||||
.selection()
|
||||
.set_select_function(Some(Box::new(select_function_similar_images)));
|
||||
}
|
||||
let information = sf.get_information();
|
||||
let text_messages = sf.get_text_messages();
|
||||
|
||||
let base_images_size = sf.get_similar_images().len();
|
||||
let found_any_duplicates = information.number_of_duplicates > 0;
|
||||
|
||||
entry_info.set_text(
|
||||
format!(
|
||||
"{} {} {} {}.",
|
||||
fl!("compute_found"),
|
||||
fl!("compute_duplicates_for"),
|
||||
base_images_size,
|
||||
fl!("compute_similar_image")
|
||||
fl!(
|
||||
"compute_found_images",
|
||||
generate_translation_hashmap(vec![
|
||||
("number_files", information.number_of_duplicates.to_string()),
|
||||
("number_groups", information.number_of_groups.to_string()),
|
||||
])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
@ -605,8 +810,71 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
{
|
||||
let list_store = get_list_store(&tree_view_similar_images_finder);
|
||||
|
||||
let vec_struct_similar = sf.get_similar_images();
|
||||
if sf.get_use_reference() {
|
||||
let vec_struct_similar: &Vec<(czkawka_core::similar_images::FileEntry, Vec<czkawka_core::similar_images::FileEntry>)> =
|
||||
sf.get_similar_images_referenced();
|
||||
for (base_file_entry, vec_file_entry) in vec_struct_similar.iter() {
|
||||
// Sort
|
||||
let vec_file_entry = if vec_file_entry.len() >= 2 {
|
||||
let mut vec_file_entry = vec_file_entry.clone();
|
||||
vec_file_entry.sort_by_key(|e| {
|
||||
let t = split_path(e.path.as_path());
|
||||
(t.0, t.1)
|
||||
});
|
||||
vec_file_entry
|
||||
} else {
|
||||
vec_file_entry.clone()
|
||||
};
|
||||
|
||||
// Header
|
||||
let (directory, file) = split_path(&base_file_entry.path);
|
||||
let values: [(u32, &dyn ToValue); 12] = [
|
||||
(ColumnsSimilarImages::ActivatableSelectButton as u32, &false),
|
||||
(ColumnsSimilarImages::SelectionButton as u32, &false),
|
||||
(ColumnsSimilarImages::Similarity as u32, &"".to_string()),
|
||||
(ColumnsSimilarImages::Size as u32, &base_file_entry.size.file_size(options::BINARY).unwrap()),
|
||||
(ColumnsSimilarImages::SizeAsBytes as u32, &base_file_entry.size),
|
||||
(ColumnsSimilarImages::Dimensions as u32, &base_file_entry.dimensions),
|
||||
(ColumnsSimilarImages::Name as u32, &file),
|
||||
(ColumnsSimilarImages::Path as u32, &directory),
|
||||
(
|
||||
ColumnsSimilarImages::Modification as u32,
|
||||
&(NaiveDateTime::from_timestamp(base_file_entry.modified_date as i64, 0).to_string()),
|
||||
),
|
||||
(ColumnsSimilarImages::ModificationAsSecs as u32, &(base_file_entry.modified_date)),
|
||||
(ColumnsSimilarImages::Color as u32, &(HEADER_ROW_COLOR.to_string())),
|
||||
(ColumnsSimilarImages::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
|
||||
// Meat
|
||||
for file_entry in vec_file_entry.iter() {
|
||||
let (directory, file) = split_path(&file_entry.path);
|
||||
let values: [(u32, &dyn ToValue); 12] = [
|
||||
(ColumnsSimilarImages::ActivatableSelectButton as u32, &true),
|
||||
(ColumnsSimilarImages::SelectionButton as u32, &false),
|
||||
(
|
||||
ColumnsSimilarImages::Similarity as u32,
|
||||
&(similar_images::get_string_from_similarity(&file_entry.similarity, hash_size).to_string()),
|
||||
),
|
||||
(ColumnsSimilarImages::Size as u32, &file_entry.size.file_size(options::BINARY).unwrap()),
|
||||
(ColumnsSimilarImages::SizeAsBytes as u32, &file_entry.size),
|
||||
(ColumnsSimilarImages::Dimensions as u32, &file_entry.dimensions),
|
||||
(ColumnsSimilarImages::Name as u32, &file),
|
||||
(ColumnsSimilarImages::Path as u32, &directory),
|
||||
(
|
||||
ColumnsSimilarImages::Modification as u32,
|
||||
&(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()),
|
||||
),
|
||||
(ColumnsSimilarImages::ModificationAsSecs as u32, &(file_entry.modified_date)),
|
||||
(ColumnsSimilarImages::Color as u32, &(MAIN_ROW_COLOR.to_string())),
|
||||
(ColumnsSimilarImages::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let vec_struct_similar = sf.get_similar_images();
|
||||
for vec_file_entry in vec_struct_similar.iter() {
|
||||
// Sort
|
||||
let vec_file_entry = if vec_file_entry.len() >= 2 {
|
||||
|
@ -663,6 +931,7 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_text_messages_to_text_view(text_messages, &text_view_errors);
|
||||
}
|
||||
|
@ -675,7 +944,7 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
&shared_buttons,
|
||||
&NotebookMainEnum::SimilarImages,
|
||||
&["save", "delete", "select", "symlink", "hardlink", "move"],
|
||||
base_images_size > 0,
|
||||
found_any_duplicates,
|
||||
);
|
||||
|
||||
set_buttons(
|
||||
|
@ -690,18 +959,24 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
if ff.get_stopped_search() {
|
||||
entry_info.set_text(&fl!("compute_stopped_by_user"));
|
||||
} else {
|
||||
//let information = ff.get_information();
|
||||
if ff.get_use_reference() {
|
||||
tree_view_similar_videos_finder.selection().set_select_function(Some(Box::new(select_function_always_true)));
|
||||
} else {
|
||||
tree_view_similar_videos_finder
|
||||
.selection()
|
||||
.set_select_function(Some(Box::new(select_function_similar_videos)));
|
||||
}
|
||||
let information = ff.get_information();
|
||||
let text_messages = ff.get_text_messages();
|
||||
|
||||
let base_videos_size = ff.get_similar_videos().len();
|
||||
let found_any_duplicates = information.number_of_duplicates > 0;
|
||||
|
||||
entry_info.set_text(
|
||||
format!(
|
||||
"{} {} {} {}.",
|
||||
fl!("compute_found"),
|
||||
fl!("compute_duplicates_for"),
|
||||
base_videos_size,
|
||||
fl!("compute_similar_videos")
|
||||
fl!(
|
||||
"compute_found_videos",
|
||||
generate_translation_hashmap(vec![
|
||||
("number_files", information.number_of_duplicates.to_string()),
|
||||
("number_groups", information.number_of_groups.to_string()),
|
||||
])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
@ -710,6 +985,63 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
{
|
||||
let list_store = get_list_store(&tree_view_similar_videos_finder);
|
||||
|
||||
if ff.get_use_reference() {
|
||||
let vec_struct_similar = ff.get_similar_videos_referenced();
|
||||
|
||||
for (base_file_entry, vec_file_entry) in vec_struct_similar.iter() {
|
||||
// Sort
|
||||
let vec_file_entry = if vec_file_entry.len() >= 2 {
|
||||
let mut vec_file_entry = vec_file_entry.clone();
|
||||
vec_file_entry.sort_by_key(|e| {
|
||||
let t = split_path(e.path.as_path());
|
||||
(t.0, t.1)
|
||||
});
|
||||
vec_file_entry
|
||||
} else {
|
||||
vec_file_entry.clone()
|
||||
};
|
||||
|
||||
// Header
|
||||
let (directory, file) = split_path(&base_file_entry.path);
|
||||
let values: [(u32, &dyn ToValue); 10] = [
|
||||
(ColumnsSimilarVideos::ActivatableSelectButton as u32, &false),
|
||||
(ColumnsSimilarVideos::SelectionButton as u32, &false),
|
||||
(ColumnsSimilarVideos::Size as u32, &base_file_entry.size.file_size(options::BINARY).unwrap()),
|
||||
(ColumnsSimilarVideos::SizeAsBytes as u32, &base_file_entry.size),
|
||||
(ColumnsSimilarVideos::Name as u32, &file),
|
||||
(ColumnsSimilarVideos::Path as u32, &directory),
|
||||
(
|
||||
ColumnsSimilarVideos::Modification as u32,
|
||||
&(NaiveDateTime::from_timestamp(base_file_entry.modified_date as i64, 0).to_string()),
|
||||
),
|
||||
(ColumnsSimilarVideos::ModificationAsSecs as u32, &(base_file_entry.modified_date)),
|
||||
(ColumnsSimilarVideos::Color as u32, &(HEADER_ROW_COLOR.to_string())),
|
||||
(ColumnsSimilarVideos::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
|
||||
// Meat
|
||||
for file_entry in vec_file_entry.iter() {
|
||||
let (directory, file) = split_path(&file_entry.path);
|
||||
let values: [(u32, &dyn ToValue); 10] = [
|
||||
(ColumnsSimilarVideos::ActivatableSelectButton as u32, &true),
|
||||
(ColumnsSimilarVideos::SelectionButton as u32, &false),
|
||||
(ColumnsSimilarVideos::Size as u32, &file_entry.size.file_size(options::BINARY).unwrap()),
|
||||
(ColumnsSimilarVideos::SizeAsBytes as u32, &file_entry.size),
|
||||
(ColumnsSimilarVideos::Name as u32, &file),
|
||||
(ColumnsSimilarVideos::Path as u32, &directory),
|
||||
(
|
||||
ColumnsSimilarVideos::Modification as u32,
|
||||
&(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()),
|
||||
),
|
||||
(ColumnsSimilarVideos::ModificationAsSecs as u32, &(file_entry.modified_date)),
|
||||
(ColumnsSimilarVideos::Color as u32, &(MAIN_ROW_COLOR.to_string())),
|
||||
(ColumnsSimilarVideos::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let vec_struct_similar = ff.get_similar_videos();
|
||||
|
||||
for vec_file_entry in vec_struct_similar.iter() {
|
||||
|
@ -761,6 +1093,7 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_text_messages_to_text_view(text_messages, &text_view_errors);
|
||||
}
|
||||
|
@ -773,7 +1106,7 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
&shared_buttons,
|
||||
&NotebookMainEnum::SimilarVideos,
|
||||
&["save", "delete", "select", "symlink", "hardlink", "move"],
|
||||
base_videos_size > 0,
|
||||
found_any_duplicates,
|
||||
);
|
||||
|
||||
set_buttons(
|
||||
|
@ -788,19 +1121,32 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
if mf.get_stopped_search() {
|
||||
entry_info.set_text(&fl!("compute_stopped_by_user"));
|
||||
} else {
|
||||
if mf.get_use_reference() {
|
||||
tree_view_same_music_finder.selection().set_select_function(Some(Box::new(select_function_always_true)));
|
||||
} else {
|
||||
tree_view_same_music_finder.selection().set_select_function(Some(Box::new(select_function_same_music)));
|
||||
}
|
||||
|
||||
let information = mf.get_information();
|
||||
let text_messages = mf.get_text_messages();
|
||||
|
||||
let same_music_number: usize = information.number_of_duplicates_music_files;
|
||||
let same_music_number: usize = information.number_of_duplicates;
|
||||
|
||||
entry_info.set_text(format!("{} {} {}.", fl!("compute_found"), same_music_number, fl!("compute_music_files")).as_str());
|
||||
entry_info.set_text(
|
||||
fl!(
|
||||
"compute_found_music",
|
||||
generate_translation_hashmap(vec![
|
||||
("number_files", information.number_of_duplicates.to_string()),
|
||||
("number_groups", information.number_of_groups.to_string()),
|
||||
])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
// Create GUI
|
||||
{
|
||||
let list_store = get_list_store(&tree_view_same_music_finder);
|
||||
|
||||
let vector = mf.get_duplicated_music_entries();
|
||||
|
||||
let music_similarity = *mf.get_music_similarity();
|
||||
|
||||
let is_title = (MusicSimilarity::TITLE & music_similarity) != MusicSimilarity::NONE;
|
||||
|
@ -809,6 +1155,72 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
let is_album_artist = (MusicSimilarity::ALBUM_ARTIST & music_similarity) != MusicSimilarity::NONE;
|
||||
let is_year = (MusicSimilarity::YEAR & music_similarity) != MusicSimilarity::NONE;
|
||||
|
||||
if mf.get_use_reference() {
|
||||
let vector = mf.get_similar_music_referenced();
|
||||
|
||||
for (base_file_entry, vec_file_entry) in vector {
|
||||
// Sort
|
||||
let vec_file_entry = if vec_file_entry.len() >= 2 {
|
||||
let mut vec_file_entry = vec_file_entry.clone();
|
||||
vec_file_entry.sort_by_key(|e| {
|
||||
let t = split_path(e.path.as_path());
|
||||
(t.0, t.1)
|
||||
});
|
||||
vec_file_entry
|
||||
} else {
|
||||
vec_file_entry.clone()
|
||||
};
|
||||
|
||||
let (directory, file) = split_path(&base_file_entry.path);
|
||||
let values: [(u32, &dyn ToValue); 15] = [
|
||||
(ColumnsSameMusic::ActivatableSelectButton as u32, &false),
|
||||
(ColumnsSameMusic::SelectionButton as u32, &false),
|
||||
(ColumnsSameMusic::Size as u32, &base_file_entry.size.file_size(options::BINARY).unwrap()),
|
||||
(ColumnsSameMusic::SizeAsBytes as u32, &base_file_entry.size),
|
||||
(ColumnsSameMusic::Name as u32, &file),
|
||||
(ColumnsSameMusic::Path as u32, &directory),
|
||||
(ColumnsSameMusic::Title as u32, &base_file_entry.title),
|
||||
(ColumnsSameMusic::Artist as u32, &base_file_entry.artist),
|
||||
(ColumnsSameMusic::AlbumTitle as u32, &base_file_entry.album_title),
|
||||
(ColumnsSameMusic::AlbumArtist as u32, &base_file_entry.album_artist),
|
||||
(ColumnsSameMusic::Year as u32, &base_file_entry.year.to_string()),
|
||||
(
|
||||
ColumnsSameMusic::Modification as u32,
|
||||
&(NaiveDateTime::from_timestamp(base_file_entry.modified_date as i64, 0).to_string()),
|
||||
),
|
||||
(ColumnsSameMusic::ModificationAsSecs as u32, &(base_file_entry.modified_date)),
|
||||
(ColumnsSameMusic::Color as u32, &(HEADER_ROW_COLOR.to_string())),
|
||||
(ColumnsSameMusic::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
for file_entry in vec_file_entry {
|
||||
let (directory, file) = split_path(&file_entry.path);
|
||||
let values: [(u32, &dyn ToValue); 15] = [
|
||||
(ColumnsSameMusic::ActivatableSelectButton as u32, &true),
|
||||
(ColumnsSameMusic::SelectionButton as u32, &false),
|
||||
(ColumnsSameMusic::Size as u32, &file_entry.size.file_size(options::BINARY).unwrap()),
|
||||
(ColumnsSameMusic::SizeAsBytes as u32, &file_entry.size),
|
||||
(ColumnsSameMusic::Name as u32, &file),
|
||||
(ColumnsSameMusic::Path as u32, &directory),
|
||||
(ColumnsSameMusic::Title as u32, &file_entry.title),
|
||||
(ColumnsSameMusic::Artist as u32, &file_entry.artist),
|
||||
(ColumnsSameMusic::AlbumTitle as u32, &file_entry.album_title),
|
||||
(ColumnsSameMusic::AlbumArtist as u32, &file_entry.album_artist),
|
||||
(ColumnsSameMusic::Year as u32, &file_entry.year.to_string()),
|
||||
(
|
||||
ColumnsSameMusic::Modification as u32,
|
||||
&(NaiveDateTime::from_timestamp(file_entry.modified_date as i64, 0).to_string()),
|
||||
),
|
||||
(ColumnsSameMusic::ModificationAsSecs as u32, &(file_entry.modified_date)),
|
||||
(ColumnsSameMusic::Color as u32, &(MAIN_ROW_COLOR.to_string())),
|
||||
(ColumnsSameMusic::TextColor as u32, &(TEXT_COLOR.to_string())),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let vector = mf.get_duplicated_music_entries();
|
||||
|
||||
let text: String = "-----".to_string();
|
||||
|
||||
for vec_file_entry in vector {
|
||||
|
@ -897,6 +1309,7 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
}
|
||||
print_text_messages_to_text_view(text_messages, &text_view_errors);
|
||||
}
|
||||
|
||||
|
@ -928,7 +1341,13 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
|
||||
let invalid_symlinks: usize = information.number_of_invalid_symlinks;
|
||||
|
||||
entry_info.set_text(format!("{} {} {}.", fl!("compute_found"), invalid_symlinks, fl!("compute_symlinks")).as_str());
|
||||
entry_info.set_text(
|
||||
fl!(
|
||||
"compute_found_invalid_symlinks",
|
||||
generate_translation_hashmap(vec![("number_files", invalid_symlinks.to_string()),])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
// Create GUI
|
||||
{
|
||||
|
@ -986,7 +1405,13 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
|||
|
||||
let broken_files_number: usize = information.number_of_broken_files;
|
||||
|
||||
entry_info.set_text(format!("{} {} {}.", fl!("compute_found"), broken_files_number, fl!("compute_broken_files")).as_str());
|
||||
entry_info.set_text(
|
||||
fl!(
|
||||
"compute_found_broken_files",
|
||||
generate_translation_hashmap(vec![("number_files", broken_files_number.to_string()),])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
// Create GUI
|
||||
{
|
||||
|
|
|
@ -63,6 +63,7 @@ pub async fn delete_things(gui_data: GuiData) {
|
|||
tree_view,
|
||||
column_color,
|
||||
nb_object.column_selection,
|
||||
nb_object.column_path,
|
||||
&window_main,
|
||||
&check_button_settings_confirm_group_deletion,
|
||||
)
|
||||
|
@ -201,6 +202,7 @@ pub async fn check_if_deleting_all_files_in_group(
|
|||
tree_view: >k::TreeView,
|
||||
column_color: i32,
|
||||
column_selection: i32,
|
||||
column_path: i32,
|
||||
window_main: >k::Window,
|
||||
check_button_settings_confirm_group_deletion: >k::CheckButton,
|
||||
) -> bool {
|
||||
|
@ -211,6 +213,11 @@ pub async fn check_if_deleting_all_files_in_group(
|
|||
if let Some(iter) = model.iter_first() {
|
||||
assert_eq!(model.value(&iter, column_color).get::<String>().unwrap(), HEADER_ROW_COLOR); // First element should be header
|
||||
|
||||
// It is safe to remove any number of files in reference mode
|
||||
if !model.value(&iter, column_path).get::<String>().unwrap().is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
loop {
|
||||
if !model.iter_next(&iter) {
|
||||
break;
|
||||
|
@ -523,7 +530,7 @@ pub fn tree_remove(
|
|||
}
|
||||
}
|
||||
|
||||
clean_invalid_headers(&model, column_color);
|
||||
clean_invalid_headers(&model, column_color, column_path);
|
||||
|
||||
text_view_errors.buffer().unwrap().set_text(messages.as_str());
|
||||
}
|
||||
|
|
|
@ -253,7 +253,7 @@ pub fn hardlink_symlink(
|
|||
model.remove(&model.iter(tree_path).unwrap());
|
||||
}
|
||||
|
||||
clean_invalid_headers(&model, column_color);
|
||||
clean_invalid_headers(&model, column_color, column_path);
|
||||
}
|
||||
|
||||
fn create_dialog_non_group(window_main: >k::Window) -> Dialog {
|
||||
|
|
|
@ -175,7 +175,7 @@ fn move_with_tree(
|
|||
|
||||
move_files_common(&selected_rows, &model, column_file_name, column_path, &destination_folder, entry_info, text_view_errors);
|
||||
|
||||
clean_invalid_headers(&model, column_color);
|
||||
clean_invalid_headers(&model, column_color, column_path);
|
||||
}
|
||||
|
||||
fn move_with_list(
|
||||
|
|
|
@ -72,6 +72,7 @@ pub fn connect_button_search(
|
|||
let entry_settings_prehash_cache_file_minimal_size = gui_data.settings.entry_settings_prehash_cache_file_minimal_size.clone();
|
||||
let grid_progress_stages = gui_data.progress_window.grid_progress_stages.clone();
|
||||
let image_preview_similar_images = gui_data.main_notebook.image_preview_similar_images.clone();
|
||||
let image_preview_duplicates = gui_data.main_notebook.image_preview_duplicates.clone();
|
||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let notebook_upper = gui_data.upper_notebook.notebook_upper.clone();
|
||||
|
@ -102,8 +103,13 @@ pub fn connect_button_search(
|
|||
let check_button_music_approximate_comparison = gui_data.main_notebook.check_button_music_approximate_comparison.clone();
|
||||
|
||||
buttons_search_clone.connect_clicked(move |_| {
|
||||
let included_directories = get_path_buf_from_vector_of_strings(get_string_from_list_store(&tree_view_included_directories));
|
||||
let excluded_directories = get_path_buf_from_vector_of_strings(get_string_from_list_store(&tree_view_excluded_directories));
|
||||
let included_directories = get_path_buf_from_vector_of_strings(get_string_from_list_store(&tree_view_included_directories, ColumnsIncludedDirectory::Path as i32, None));
|
||||
let excluded_directories = get_path_buf_from_vector_of_strings(get_string_from_list_store(&tree_view_excluded_directories, ColumnsExcludedDirectory::Path as i32, None));
|
||||
let reference_directories = get_path_buf_from_vector_of_strings(get_string_from_list_store(
|
||||
&tree_view_included_directories,
|
||||
ColumnsIncludedDirectory::Path as i32,
|
||||
Some(ColumnsIncludedDirectory::ReferenceButton as i32),
|
||||
));
|
||||
let recursive_search = check_button_recursive.is_active();
|
||||
let excluded_items = entry_excluded_items.text().as_str().to_string().split(',').map(|e| e.to_string()).collect::<Vec<String>>();
|
||||
let allowed_extensions = entry_allowed_extensions.text().as_str().to_string();
|
||||
|
@ -111,6 +117,9 @@ pub fn connect_button_search(
|
|||
let use_cache = check_button_settings_use_cache.is_active();
|
||||
let minimal_cache_file_size = entry_settings_cache_file_minimal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 1024 / 4);
|
||||
|
||||
let minimal_file_size = entry_general_minimal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 8);
|
||||
let maximal_file_size = entry_general_maximal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 1024 * 1024 * 1024);
|
||||
|
||||
let show_dialog = Arc::new(AtomicBool::new(true));
|
||||
|
||||
hide_all_buttons(&buttons_array);
|
||||
|
@ -133,6 +142,8 @@ pub fn connect_button_search(
|
|||
|
||||
match to_notebook_main_enum(notebook_main.current_page().unwrap()) {
|
||||
NotebookMainEnum::Duplicate => {
|
||||
image_preview_duplicates.hide();
|
||||
|
||||
label_stage.show();
|
||||
grid_progress_stages.show_all();
|
||||
window_progress.resize(1, 1);
|
||||
|
@ -145,9 +156,6 @@ pub fn connect_button_search(
|
|||
let hash_type_index = combo_box_duplicate_hash_type.active().unwrap() as usize;
|
||||
let hash_type = DUPLICATES_HASH_TYPE_COMBO_BOX[hash_type_index].hash_type;
|
||||
|
||||
let minimal_file_size = entry_general_minimal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 8);
|
||||
let maximal_file_size = entry_general_maximal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 1024 * 1024 * 1024);
|
||||
|
||||
let use_prehash_cache = check_button_duplicates_use_prehash_cache.is_active();
|
||||
let minimal_prehash_cache_file_size = entry_settings_prehash_cache_file_minimal_size.text().as_str().parse::<u64>().unwrap_or(0);
|
||||
|
||||
|
@ -159,6 +167,7 @@ pub fn connect_button_search(
|
|||
let mut df = DuplicateFinder::new();
|
||||
df.set_included_directory(included_directories);
|
||||
df.set_excluded_directory(excluded_directories);
|
||||
df.set_reference_directory(reference_directories);
|
||||
df.set_recursive_search(recursive_search);
|
||||
df.set_excluded_items(excluded_items);
|
||||
df.set_allowed_extensions(allowed_extensions);
|
||||
|
@ -277,9 +286,6 @@ pub fn connect_button_search(
|
|||
let hash_alg_index = combo_box_image_hash_algorithm.active().unwrap() as usize;
|
||||
let hash_alg = IMAGES_HASH_TYPE_COMBO_BOX[hash_alg_index].hash_alg;
|
||||
|
||||
let minimal_file_size = entry_general_minimal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 16);
|
||||
let maximal_file_size = entry_general_maximal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 1024 * 1024 * 1024);
|
||||
|
||||
let ignore_same_size = check_button_image_ignore_same_size.is_active();
|
||||
|
||||
let similarity = similar_images::Similarity::Similar(scale_similarity_similar_images.value() as u32);
|
||||
|
@ -293,6 +299,7 @@ pub fn connect_button_search(
|
|||
|
||||
sf.set_included_directory(included_directories);
|
||||
sf.set_excluded_directory(excluded_directories);
|
||||
sf.set_reference_directory(reference_directories);
|
||||
sf.set_recursive_search(recursive_search);
|
||||
sf.set_excluded_items(excluded_items);
|
||||
sf.set_minimal_file_size(minimal_file_size);
|
||||
|
@ -316,9 +323,6 @@ pub fn connect_button_search(
|
|||
|
||||
get_list_store(&tree_view_similar_videos_finder).clear();
|
||||
|
||||
let minimal_file_size = entry_general_minimal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 16);
|
||||
let maximal_file_size = entry_general_maximal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 1024 * 1024 * 1024);
|
||||
|
||||
let tolerance = scale_similarity_similar_videos.value() as i32;
|
||||
|
||||
let delete_outdated_cache = check_button_settings_similar_videos_delete_outdated_cache.is_active();
|
||||
|
@ -332,6 +336,7 @@ pub fn connect_button_search(
|
|||
|
||||
sf.set_included_directory(included_directories);
|
||||
sf.set_excluded_directory(excluded_directories);
|
||||
sf.set_reference_directory(reference_directories);
|
||||
sf.set_recursive_search(recursive_search);
|
||||
sf.set_excluded_items(excluded_items);
|
||||
sf.set_minimal_file_size(minimal_file_size);
|
||||
|
@ -352,8 +357,6 @@ pub fn connect_button_search(
|
|||
|
||||
get_list_store(&tree_view_same_music_finder).clear();
|
||||
|
||||
let minimal_file_size = entry_general_minimal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 8);
|
||||
let maximal_file_size = entry_general_maximal_size.text().as_str().parse::<u64>().unwrap_or(1024 * 1024 * 1024 * 1024);
|
||||
let approximate_comparison = check_button_music_approximate_comparison.is_active();
|
||||
|
||||
let mut music_similarity: MusicSimilarity = MusicSimilarity::NONE;
|
||||
|
@ -382,6 +385,7 @@ pub fn connect_button_search(
|
|||
|
||||
mf.set_included_directory(included_directories);
|
||||
mf.set_excluded_directory(excluded_directories);
|
||||
mf.set_reference_directory(reference_directories);
|
||||
mf.set_excluded_items(excluded_items);
|
||||
mf.set_minimal_file_size(minimal_file_size);
|
||||
mf.set_maximal_file_size(maximal_file_size);
|
||||
|
|
|
@ -45,18 +45,18 @@ pub fn load_system_language(gui_data: &GuiData) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
let mut found: bool = false;
|
||||
// let mut found: bool = false;
|
||||
for (index, lang) in LANGUAGES_ALL.iter().enumerate() {
|
||||
if lang.short_text == short_lang {
|
||||
found = true;
|
||||
// found = true;
|
||||
gui_data.settings.combo_box_settings_language.set_active(Some(index as u32));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if found {
|
||||
println!("INFO: Default system language {} is available, so choosing them", short_lang);
|
||||
} else {
|
||||
println!("INFO: Default system language {} is not available, using English(en) instead", short_lang);
|
||||
}
|
||||
// if found {
|
||||
// println!("INFO: Default system language {} is available, so choosing them", short_lang);
|
||||
// } else {
|
||||
// println!("INFO: Default system language {} is not available, using English(en) instead", short_lang);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,13 @@ pub fn connect_duplicate_combo_box(gui_data: &GuiData) {
|
|||
let combo_box_duplicate_check_method = gui_data.main_notebook.combo_box_duplicate_check_method.clone();
|
||||
let combo_box_duplicate_hash_type = gui_data.main_notebook.combo_box_duplicate_hash_type.clone();
|
||||
combo_box_duplicate_check_method.connect_changed(move |combo_box_duplicate_check_method| {
|
||||
let chosen_index = combo_box_duplicate_check_method.active().unwrap() as usize;
|
||||
if DUPLICATES_CHECK_METHOD_COMBO_BOX[chosen_index].check_method == CheckingMethod::Hash {
|
||||
// None active can be if when adding elements(this signal is activated when e.g. adding new fields or removing them)
|
||||
if let Some(chosen_index) = combo_box_duplicate_check_method.active() {
|
||||
if DUPLICATES_CHECK_METHOD_COMBO_BOX[chosen_index as usize].check_method == CheckingMethod::Hash {
|
||||
combo_box_duplicate_hash_type.set_sensitive(true);
|
||||
} else {
|
||||
combo_box_duplicate_hash_type.set_sensitive(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use czkawka_core::common::Common;
|
|||
use czkawka_core::fl;
|
||||
|
||||
use crate::gui_data::GuiData;
|
||||
use crate::help_functions::{get_dialog_box_child, get_list_store, ColumnsDirectory};
|
||||
use crate::help_functions::{get_dialog_box_child, get_list_store, ColumnsIncludedDirectory};
|
||||
|
||||
pub fn connect_selection_of_directories(gui_data: &GuiData) {
|
||||
// Add manually directory
|
||||
|
@ -120,7 +120,10 @@ fn add_chosen_directories(window_main: &Window, tree_view: &TreeView, excluded_i
|
|||
let list_store = get_list_store(&tree_view);
|
||||
|
||||
for file_entry in &folders {
|
||||
let values: [(u32, &dyn ToValue); 1] = [(ColumnsDirectory::Path as u32, &file_entry.to_string_lossy().to_string())];
|
||||
let values: [(u32, &dyn ToValue); 2] = [
|
||||
(ColumnsIncludedDirectory::Path as u32, &file_entry.to_string_lossy().to_string()),
|
||||
(ColumnsIncludedDirectory::ReferenceButton as u32, &false),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +157,7 @@ fn add_manually_directories(window_main: &Window, tree_view: &TreeView) {
|
|||
if !text.is_empty() {
|
||||
let list_store = get_list_store(&tree_view);
|
||||
|
||||
let values: [(u32, &dyn ToValue); 1] = [(ColumnsDirectory::Path as u32, &text)];
|
||||
let values: [(u32, &dyn ToValue); 2] = [(ColumnsIncludedDirectory::Path as u32, &text), (ColumnsIncludedDirectory::ReferenceButton as u32, &false)];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,46 @@ use gtk::TreeViewColumn;
|
|||
|
||||
use crate::help_functions::*;
|
||||
|
||||
// When adding new column do not forget to update translations
|
||||
|
||||
pub fn create_tree_view_included_directories(tree_view: >k::TreeView) {
|
||||
let model = get_list_store(tree_view);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.set_title("Folders to check");
|
||||
column.pack_start(&renderer, true);
|
||||
column.add_attribute(&renderer, "text", ColumnsIncludedDirectory::Path as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererToggle::new();
|
||||
renderer.connect_toggled(move |_r, path| {
|
||||
let iter = model.iter(&path).unwrap();
|
||||
let mut fixed = model
|
||||
.value(&iter, ColumnsIncludedDirectory::ReferenceButton as i32)
|
||||
.get::<bool>()
|
||||
.unwrap_or_else(|err| panic!("ListStore value missing at path {:?}: {}", path, err));
|
||||
fixed = !fixed;
|
||||
model.set_value(&iter, ColumnsIncludedDirectory::ReferenceButton as u32, &fixed.to_value());
|
||||
});
|
||||
renderer.set_activatable(true);
|
||||
let column = gtk::TreeViewColumn::new();
|
||||
column.set_title("Reference folder");
|
||||
column.pack_start(&renderer, true);
|
||||
column.add_attribute(&renderer, "active", ColumnsIncludedDirectory::ReferenceButton as i32);
|
||||
tree_view.append_column(&column);
|
||||
}
|
||||
|
||||
pub fn create_tree_view_excluded_directories(tree_view: >k::TreeView) {
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.add_attribute(&renderer, "text", ColumnsExcludedDirectory::Path as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
tree_view.set_headers_visible(false);
|
||||
}
|
||||
|
||||
pub fn create_tree_view_duplicates(tree_view: >k::TreeView) {
|
||||
let model = get_list_store(tree_view);
|
||||
|
||||
|
@ -25,6 +65,17 @@ pub fn create_tree_view_duplicates(tree_view: >k::TreeView) {
|
|||
column.add_attribute(&renderer, "cell-background", ColumnsDuplicates::Color as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.set_title("Size");
|
||||
column.set_resizable(true);
|
||||
column.set_min_width(50);
|
||||
column.add_attribute(&renderer, "text", ColumnsDuplicates::Size as i32);
|
||||
column.add_attribute(&renderer, "background", ColumnsDuplicates::Color as i32);
|
||||
column.add_attribute(&renderer, "foreground", ColumnsDuplicates::TextColor as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
|
@ -443,16 +494,6 @@ pub fn create_tree_view_similar_videos(tree_view: >k::TreeView) {
|
|||
tree_view.set_vexpand(true);
|
||||
}
|
||||
|
||||
pub fn create_tree_view_directories(tree_view: >k::TreeView) {
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
let column: gtk::TreeViewColumn = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.add_attribute(&renderer, "text", ColumnsDirectory::Path as i32);
|
||||
tree_view.append_column(&column);
|
||||
|
||||
tree_view.set_headers_visible(false);
|
||||
}
|
||||
|
||||
pub fn create_tree_view_same_music(tree_view: >k::TreeView) {
|
||||
let model = get_list_store(tree_view);
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use czkawka_core::duplicate::CheckingMethod;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{EventControllerKey, TreeView};
|
||||
|
||||
use czkawka_core::similar_images::{get_string_from_similarity, Similarity, SIMILAR_VALUES};
|
||||
|
||||
use crate::fl;
|
||||
use crate::help_combo_box::IMAGES_HASH_SIZE_COMBO_BOX;
|
||||
use crate::help_combo_box::{DUPLICATES_CHECK_METHOD_COMBO_BOX, IMAGES_HASH_SIZE_COMBO_BOX};
|
||||
use crate::notebook_enums::{NotebookMainEnum, NUMBER_OF_NOTEBOOK_MAIN_TABS};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -406,6 +407,7 @@ impl GuiMainNotebook {
|
|||
// Change names of columns
|
||||
let names_of_columns = [
|
||||
vec![
|
||||
fl!("main_tree_view_column_size"),
|
||||
fl!("main_tree_view_column_file_name"),
|
||||
fl!("main_tree_view_column_path"),
|
||||
fl!("main_tree_view_column_modification"),
|
||||
|
@ -479,5 +481,22 @@ impl GuiMainNotebook {
|
|||
column.set_title(&names_of_columns[notebook_index][column_index - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let active = self.combo_box_duplicate_check_method.active().unwrap_or(0);
|
||||
self.combo_box_duplicate_check_method.remove_all();
|
||||
for i in &DUPLICATES_CHECK_METHOD_COMBO_BOX {
|
||||
let text = match i.check_method {
|
||||
CheckingMethod::Hash => fl!("duplicate_mode_hash_combo_box"),
|
||||
CheckingMethod::Size => fl!("duplicate_mode_size_combo_box"),
|
||||
CheckingMethod::Name => fl!("duplicate_mode_name_combo_box"),
|
||||
_ => {
|
||||
panic!()
|
||||
}
|
||||
};
|
||||
self.combo_box_duplicate_check_method.append_text(&text);
|
||||
}
|
||||
self.combo_box_duplicate_check_method.set_active(Some(active));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,5 +164,15 @@ impl GuiUpperNotebook {
|
|||
.unwrap()
|
||||
.set_text(&fl_thing);
|
||||
}
|
||||
|
||||
let names_of_columns = [
|
||||
vec![fl!("upper_tree_view_included_folder_column_title"), fl!("upper_tree_view_included_reference_column_title")], // Included folders
|
||||
];
|
||||
|
||||
for (notebook_index, tree_view) in [self.tree_view_included_directories.clone()].iter().enumerate() {
|
||||
for (column_index, column) in tree_view.columns().iter().enumerate() {
|
||||
column.set_title(&names_of_columns[notebook_index][column_index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ use czkawka_core::similar_videos::SimilarVideos;
|
|||
use czkawka_core::temporary::Temporary;
|
||||
use czkawka_core::{fl, invalid_symlinks};
|
||||
|
||||
use crate::notebook_enums::{NotebookMainEnum, NUMBER_OF_NOTEBOOK_MAIN_TABS};
|
||||
use crate::notebook_enums::{NotebookMainEnum, NotebookUpperEnum, NUMBER_OF_NOTEBOOK_MAIN_TABS};
|
||||
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
pub const CHARACTER: char = '/';
|
||||
|
@ -69,8 +69,8 @@ pub static NOTEBOOKS_INFOS: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [
|
|||
column_selection: ColumnsDuplicates::SelectionButton as i32,
|
||||
column_color: Some(ColumnsDuplicates::Color as i32),
|
||||
column_dimensions: None,
|
||||
column_size: None,
|
||||
column_size_as_bytes: None,
|
||||
column_size: None, // Do not add, useless in hash and size mode
|
||||
column_size_as_bytes: None, // Do not add, useless in hash and size mode
|
||||
column_modification_as_secs: Some(ColumnsDuplicates::ModificationAsSecs as i32),
|
||||
},
|
||||
NotebookObject {
|
||||
|
@ -209,6 +209,7 @@ pub enum ColumnsDuplicates {
|
|||
// Columns for duplicate treeview
|
||||
ActivatableSelectButton = 0,
|
||||
SelectionButton,
|
||||
Size,
|
||||
Name,
|
||||
Path,
|
||||
Modification,
|
||||
|
@ -226,8 +227,13 @@ pub enum ColumnsEmptyFolders {
|
|||
ModificationAsSecs,
|
||||
}
|
||||
|
||||
pub enum ColumnsDirectory {
|
||||
// Columns for Included and Excluded Directories in upper Notebook
|
||||
pub enum ColumnsIncludedDirectory {
|
||||
// Columns for Included Directories in upper Notebook
|
||||
Path = 0,
|
||||
ReferenceButton,
|
||||
}
|
||||
pub enum ColumnsExcludedDirectory {
|
||||
// Columns for Excluded Directories in upper Notebook
|
||||
Path = 0,
|
||||
}
|
||||
|
||||
|
@ -328,7 +334,7 @@ pub const HEADER_ROW_COLOR: &str = "#272727";
|
|||
//pub const MAIN_ROW_COLOR: &str = "#f4f434"; // TEST
|
||||
//pub const HEADER_ROW_COLOR: &str = "#010101"; // TEST
|
||||
|
||||
pub fn get_string_from_list_store(tree_view: >k::TreeView) -> Vec<String> {
|
||||
pub fn get_string_from_list_store(tree_view: >k::TreeView, column_full_path: i32, column_selection: Option<i32>) -> Vec<String> {
|
||||
let list_store: gtk::ListStore = get_list_store(tree_view);
|
||||
|
||||
let mut string_vector: Vec<String> = Vec::new();
|
||||
|
@ -339,11 +345,21 @@ pub fn get_string_from_list_store(tree_view: >k::TreeView) -> Vec<String> {
|
|||
return string_vector;
|
||||
}
|
||||
};
|
||||
loop {
|
||||
string_vector.push(list_store.value(&tree_iter, 0).get::<String>().unwrap());
|
||||
match column_selection {
|
||||
Some(column_selection) => loop {
|
||||
if list_store.value(&tree_iter, column_selection).get::<bool>().unwrap() {
|
||||
string_vector.push(list_store.value(&tree_iter, column_full_path).get::<String>().unwrap());
|
||||
}
|
||||
if !list_store.iter_next(&tree_iter) {
|
||||
return string_vector;
|
||||
}
|
||||
},
|
||||
None => loop {
|
||||
string_vector.push(list_store.value(&tree_iter, column_full_path).get::<String>().unwrap());
|
||||
if !list_store.iter_next(&tree_iter) {
|
||||
return string_vector;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -470,6 +486,16 @@ pub fn get_notebook_enum_from_tree_view(tree_view: >k::TreeView) -> NotebookMa
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_notebook_upper_enum_from_tree_view(tree_view: >k::TreeView) -> NotebookUpperEnum {
|
||||
match (*tree_view).widget_name().to_string().as_str() {
|
||||
"tree_view_upper_included_directories" => NotebookUpperEnum::IncludedDirectories,
|
||||
"tree_view_upper_excluded_directories" => NotebookUpperEnum::ExcludedDirectories,
|
||||
e => {
|
||||
panic!("{}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_notebook_object_from_tree_view(tree_view: >k::TreeView) -> &NotebookObject {
|
||||
let nb_enum = get_notebook_enum_from_tree_view(tree_view);
|
||||
&NOTEBOOKS_INFOS[nb_enum as usize]
|
||||
|
@ -484,7 +510,7 @@ pub fn get_full_name_from_path_name(path: &str, name: &str) -> String {
|
|||
}
|
||||
|
||||
// After e.g. deleting files, header may become orphan or have one child, so should be deleted in this case
|
||||
pub fn clean_invalid_headers(model: >k::ListStore, column_color: i32) {
|
||||
pub fn clean_invalid_headers(model: >k::ListStore, column_color: i32, column_path: i32) {
|
||||
// Remove only child from header
|
||||
if let Some(first_iter) = model.iter_first() {
|
||||
let mut vec_tree_path_to_delete: Vec<gtk::TreePath> = Vec::new();
|
||||
|
@ -495,6 +521,9 @@ pub fn clean_invalid_headers(model: >k::ListStore, column_color: i32) {
|
|||
|
||||
let mut next_iter;
|
||||
let mut next_next_iter;
|
||||
|
||||
// Empty means default check type
|
||||
if model.value(¤t_iter, column_path).get::<String>().unwrap().is_empty() {
|
||||
'main: loop {
|
||||
if model.value(¤t_iter, column_color).get::<String>().unwrap() != HEADER_ROW_COLOR {
|
||||
panic!("First deleted element, should be a header"); // First element should be header
|
||||
|
@ -546,6 +575,57 @@ pub fn clean_invalid_headers(model: >k::ListStore, column_color: i32) {
|
|||
model.remove(&model.iter(tree_path).unwrap());
|
||||
}
|
||||
}
|
||||
// Non empty means that header points at reference folder
|
||||
else {
|
||||
// TODO verify how it works
|
||||
'reference: loop {
|
||||
if model.value(¤t_iter, column_color).get::<String>().unwrap() != HEADER_ROW_COLOR {
|
||||
panic!("First deleted element, should be a header"); // First element should be header
|
||||
};
|
||||
|
||||
next_iter = current_iter.clone();
|
||||
if !model.iter_next(&next_iter) {
|
||||
// There is only single header left (H1 -> END) -> (NOTHING)
|
||||
vec_tree_path_to_delete.push(model.path(¤t_iter).unwrap());
|
||||
break 'reference;
|
||||
}
|
||||
|
||||
if model.value(&next_iter, column_color).get::<String>().unwrap() == HEADER_ROW_COLOR {
|
||||
// There are two headers each others(we remove just first) -> (H1 -> H2) -> (H2)
|
||||
vec_tree_path_to_delete.push(model.path(¤t_iter).unwrap());
|
||||
current_iter = next_iter.clone();
|
||||
continue 'reference;
|
||||
}
|
||||
|
||||
next_next_iter = next_iter.clone();
|
||||
if !model.iter_next(&next_next_iter) {
|
||||
// There is only one child of header left, so we remove it with header (H1 -> C1 -> END) -> (NOTHING)
|
||||
break 'reference;
|
||||
}
|
||||
|
||||
if model.value(&next_next_iter, column_color).get::<String>().unwrap() == HEADER_ROW_COLOR {
|
||||
// One child between two headers, we can remove them (H1 -> C1 -> H2) -> (H2)
|
||||
current_iter = next_next_iter.clone();
|
||||
continue 'reference;
|
||||
}
|
||||
|
||||
loop {
|
||||
// (H1 -> C1 -> C2 -> Cn -> END) -> (NO CHANGE, BECAUSE IS GOOD)
|
||||
if !model.iter_next(&next_next_iter) {
|
||||
break 'reference;
|
||||
}
|
||||
// Move to next header
|
||||
if model.value(&next_next_iter, column_color).get::<String>().unwrap() == HEADER_ROW_COLOR {
|
||||
current_iter = next_next_iter.clone();
|
||||
continue 'reference;
|
||||
}
|
||||
}
|
||||
}
|
||||
for tree_path in vec_tree_path_to_delete.iter().rev() {
|
||||
model.remove(&model.iter(tree_path).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last step, remove orphan header if exists
|
||||
if let Some(iter) = model.iter_first() {
|
||||
|
|
|
@ -101,8 +101,8 @@ pub fn initialize_gui(gui_data: &mut GuiData) {
|
|||
// Set step increment
|
||||
{
|
||||
let scale_similarity_similar_images = gui_data.main_notebook.scale_similarity_similar_images.clone();
|
||||
scale_similarity_similar_images.set_range(0_f64, SIMILAR_VALUES[1][5] as f64); // This defaults to value of minimal size of hash 8
|
||||
scale_similarity_similar_images.set_fill_level(SIMILAR_VALUES[1][5] as f64);
|
||||
scale_similarity_similar_images.set_range(0_f64, SIMILAR_VALUES[0][5] as f64); // This defaults to value of minimal size of hash 8
|
||||
scale_similarity_similar_images.set_fill_level(SIMILAR_VALUES[0][5] as f64);
|
||||
scale_similarity_similar_images.adjustment().set_step_increment(1_f64);
|
||||
}
|
||||
// Set step increment
|
||||
|
@ -124,9 +124,10 @@ pub fn initialize_gui(gui_data: &mut GuiData) {
|
|||
let image_preview = gui_data.main_notebook.image_preview_duplicates.clone();
|
||||
image_preview.hide();
|
||||
|
||||
let col_types: [glib::types::Type; 8] = [
|
||||
let col_types: [glib::types::Type; 9] = [
|
||||
glib::types::Type::BOOL, // ActivatableSelectButton
|
||||
glib::types::Type::BOOL, // SelectionButton
|
||||
glib::types::Type::STRING, // Size
|
||||
glib::types::Type::STRING, // Name
|
||||
glib::types::Type::STRING, // Path
|
||||
glib::types::Type::STRING, // Modification
|
||||
|
@ -397,17 +398,23 @@ pub fn initialize_gui(gui_data: &mut GuiData) {
|
|||
let tree_view = gui_data.upper_notebook.tree_view_included_directories.clone();
|
||||
let evk = gui_data.upper_notebook.evk_tree_view_included_directories.clone();
|
||||
|
||||
let col_types: [glib::types::Type; 1] = [glib::types::Type::STRING];
|
||||
let col_types: [glib::types::Type; 2] = [
|
||||
glib::types::Type::STRING, // Path
|
||||
glib::types::Type::BOOL, // ReferenceButton
|
||||
];
|
||||
let list_store: gtk::ListStore = gtk::ListStore::new(&col_types);
|
||||
|
||||
tree_view.set_model(Some(&list_store));
|
||||
tree_view.selection().set_mode(SelectionMode::Multiple);
|
||||
|
||||
create_tree_view_directories(&tree_view);
|
||||
create_tree_view_included_directories(&tree_view);
|
||||
|
||||
tree_view.set_widget_name("tree_view_upper_included_directories");
|
||||
scrolled_window.add(&tree_view);
|
||||
scrolled_window.show_all();
|
||||
|
||||
tree_view.connect_button_press_event(opening_double_click_function_directories);
|
||||
evk.connect_key_pressed(opening_enter_function_ported_upper_directories);
|
||||
evk.connect_key_released(move |_event_controller_key, _key_value, key_code, _modifier_type| {
|
||||
if key_code == KEY_DELETE {
|
||||
let list_store = get_list_store(&tree_view);
|
||||
|
@ -433,11 +440,14 @@ pub fn initialize_gui(gui_data: &mut GuiData) {
|
|||
tree_view.set_model(Some(&list_store));
|
||||
tree_view.selection().set_mode(SelectionMode::Multiple);
|
||||
|
||||
create_tree_view_directories(&tree_view);
|
||||
create_tree_view_excluded_directories(&tree_view);
|
||||
|
||||
tree_view.set_widget_name("tree_view_upper_excluded_directories");
|
||||
scrolled_window.add(&tree_view);
|
||||
scrolled_window.show_all();
|
||||
|
||||
tree_view.connect_button_press_event(opening_double_click_function_directories);
|
||||
evk.connect_key_pressed(opening_enter_function_ported_upper_directories);
|
||||
evk.connect_key_released(move |_event_controller_key, _key_value, key_code, _modifier_type| {
|
||||
if key_code == KEY_DELETE {
|
||||
let list_store = get_list_store(&tree_view);
|
||||
|
@ -516,7 +526,6 @@ fn connect_event_mouse(gui_data: &GuiData) {
|
|||
let preview_path = gui_data.preview_path.clone();
|
||||
let image_preview = gui_data.main_notebook.image_preview_similar_images.clone();
|
||||
|
||||
tree_view.connect_button_press_event(opening_double_click_function);
|
||||
tree_view.connect_button_release_event(move |tree_view, _event| {
|
||||
let nb_object = &NOTEBOOKS_INFOS[NotebookMainEnum::SimilarImages as usize];
|
||||
let preview_path = preview_path.clone();
|
||||
|
|
|
@ -2,6 +2,7 @@ use gdk::ModifierType;
|
|||
use gtk::prelude::*;
|
||||
|
||||
use crate::help_functions::*;
|
||||
use crate::notebook_enums::NotebookUpperEnum;
|
||||
|
||||
// TODO add option to open files and folders from context menu activated by pressing ONCE with right mouse button
|
||||
|
||||
|
@ -13,7 +14,40 @@ pub fn opening_enter_function_ported(event_controller: >k::EventControllerKey,
|
|||
}
|
||||
|
||||
let nt_object = get_notebook_object_from_tree_view(&tree_view);
|
||||
handle_tree_keypress(&tree_view, key_code, nt_object.column_name, nt_object.column_path, nt_object.column_selection);
|
||||
handle_tree_keypress(
|
||||
&tree_view,
|
||||
key_code,
|
||||
nt_object.column_name,
|
||||
nt_object.column_path,
|
||||
nt_object.column_selection,
|
||||
nt_object.column_color,
|
||||
);
|
||||
false // True catches signal, and don't send it to function, e.g. up button is catched and don't move selection
|
||||
}
|
||||
|
||||
pub fn opening_enter_function_ported_upper_directories(event_controller: >k::EventControllerKey, _key_value: u32, key_code: u32, _modifier_type: ModifierType) -> bool {
|
||||
let tree_view = event_controller.widget().unwrap().downcast::<gtk::TreeView>().unwrap();
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
println!("key_code {}", key_code);
|
||||
}
|
||||
|
||||
match get_notebook_upper_enum_from_tree_view(&tree_view) {
|
||||
NotebookUpperEnum::IncludedDirectories => {
|
||||
handle_tree_keypress_upper_directories(
|
||||
&tree_view,
|
||||
key_code,
|
||||
ColumnsIncludedDirectory::Path as i32,
|
||||
Some(ColumnsIncludedDirectory::ReferenceButton as i32),
|
||||
);
|
||||
}
|
||||
NotebookUpperEnum::ExcludedDirectories => {
|
||||
handle_tree_keypress_upper_directories(&tree_view, key_code, ColumnsExcludedDirectory::Path as i32, None);
|
||||
}
|
||||
_ => {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
false // True catches signal, and don't send it to function, e.g. up button is catched and don't move selection
|
||||
}
|
||||
|
||||
|
@ -27,6 +61,23 @@ pub fn opening_double_click_function(tree_view: >k::TreeView, event: &gdk::Eve
|
|||
gtk::Inhibit(false)
|
||||
}
|
||||
|
||||
pub fn opening_double_click_function_directories(tree_view: >k::TreeView, event: &gdk::EventButton) -> gtk::Inhibit {
|
||||
if event.event_type() == gdk::EventType::DoubleButtonPress && (event.button() == 1 || event.button() == 3) {
|
||||
match get_notebook_upper_enum_from_tree_view(tree_view) {
|
||||
NotebookUpperEnum::IncludedDirectories => {
|
||||
common_open_function_upper_directories(tree_view, ColumnsIncludedDirectory::Path as i32);
|
||||
}
|
||||
NotebookUpperEnum::ExcludedDirectories => {
|
||||
common_open_function_upper_directories(tree_view, ColumnsExcludedDirectory::Path as i32);
|
||||
}
|
||||
_ => {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
gtk::Inhibit(false)
|
||||
}
|
||||
|
||||
// // GTK 4
|
||||
// pub fn opening_enter_function_ported(event_controller: >k4::EventControllerKey, _key: gdk4::keys::Key, key_code: u32, _modifier_type: ModifierType) -> gtk4::Inhibit {
|
||||
// let tree_view = event_controller.widget().unwrap().downcast::<gtk4::TreeView>().unwrap();
|
||||
|
@ -58,15 +109,20 @@ enum OpenMode {
|
|||
PathAndName,
|
||||
}
|
||||
|
||||
fn common_mark_function(tree_view: >k::TreeView, column_name: i32) {
|
||||
fn common_mark_function(tree_view: >k::TreeView, column_selection: i32, column_color: Option<i32>) {
|
||||
let selection = tree_view.selection();
|
||||
let (selected_rows, tree_model) = selection.selected_rows();
|
||||
|
||||
let model = get_list_store(tree_view);
|
||||
|
||||
for tree_path in selected_rows.iter().rev() {
|
||||
let value = !tree_model.value(&tree_model.iter(tree_path).unwrap(), column_name).get::<bool>().unwrap();
|
||||
model.set_value(&tree_model.iter(tree_path).unwrap(), column_name as u32, &value.to_value());
|
||||
if let Some(column_color) = column_color {
|
||||
if model.value(&model.iter(tree_path).unwrap(), column_color).get::<String>().unwrap() == HEADER_ROW_COLOR {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let value = !tree_model.value(&tree_model.iter(tree_path).unwrap(), column_selection).get::<bool>().unwrap();
|
||||
model.set_value(&tree_model.iter(tree_path).unwrap(), column_selection as u32, &value.to_value());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,13 +147,38 @@ fn common_open_function(tree_view: >k::TreeView, column_name: i32, column_path
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_tree_keypress(tree_view: >k::TreeView, key_code: u32, name_column: i32, path_column: i32, mark_column: i32) {
|
||||
fn common_open_function_upper_directories(tree_view: >k::TreeView, column_full_path: i32) {
|
||||
let selection = tree_view.selection();
|
||||
let (selected_rows, tree_model) = selection.selected_rows();
|
||||
|
||||
for tree_path in selected_rows.iter().rev() {
|
||||
let full_path = tree_model.value(&tree_model.iter(tree_path).unwrap(), column_full_path).get::<String>().unwrap();
|
||||
|
||||
open::that_in_background(&full_path);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_tree_keypress_upper_directories(tree_view: >k::TreeView, key_code: u32, full_path_column: i32, mark_column: Option<i32>) {
|
||||
match key_code {
|
||||
KEY_ENTER => {
|
||||
common_open_function_upper_directories(tree_view, full_path_column);
|
||||
}
|
||||
KEY_SPACE => {
|
||||
if let Some(mark_column) = mark_column {
|
||||
common_mark_function(tree_view, mark_column, None);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_tree_keypress(tree_view: >k::TreeView, key_code: u32, name_column: i32, path_column: i32, mark_column: i32, column_color: Option<i32>) {
|
||||
match key_code {
|
||||
KEY_ENTER => {
|
||||
common_open_function(tree_view, name_column, path_column, OpenMode::PathAndName);
|
||||
}
|
||||
KEY_SPACE => {
|
||||
common_mark_function(tree_view, mark_column);
|
||||
common_mark_function(tree_view, mark_column, column_color);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -154,3 +235,6 @@ pub fn select_function_similar_videos(_tree_selection: >k::TreeSelection, tree
|
|||
|
||||
true
|
||||
}
|
||||
pub fn select_function_always_true(_tree_selection: >k::TreeSelection, _tree_model: >k::TreeModel, _tree_path: >k::TreePath, _is_path_currently_selected: bool) -> bool {
|
||||
true
|
||||
}
|
||||
|
|
|
@ -57,7 +57,8 @@ pub fn save_configuration(manual_execution: bool, upper_notebook: &GuiUpperNoteb
|
|||
let list_store = get_list_store(&tree_view_included_directories);
|
||||
if let Some(iter) = list_store.iter_first() {
|
||||
loop {
|
||||
data_to_save.push(list_store.value(&iter, ColumnsDirectory::Path as i32).get::<String>().unwrap());
|
||||
// TODO maybe save also here reference directories?
|
||||
data_to_save.push(list_store.value(&iter, ColumnsIncludedDirectory::Path as i32).get::<String>().unwrap());
|
||||
if !list_store.iter_next(&iter) {
|
||||
break;
|
||||
}
|
||||
|
@ -70,7 +71,7 @@ pub fn save_configuration(manual_execution: bool, upper_notebook: &GuiUpperNoteb
|
|||
let list_store = get_list_store(&tree_view_excluded_directories);
|
||||
if let Some(iter) = list_store.iter_first() {
|
||||
loop {
|
||||
data_to_save.push(list_store.value(&iter, ColumnsDirectory::Path as i32).get::<String>().unwrap());
|
||||
data_to_save.push(list_store.value(&iter, ColumnsExcludedDirectory::Path as i32).get::<String>().unwrap());
|
||||
if !list_store.iter_next(&iter) {
|
||||
break;
|
||||
}
|
||||
|
@ -213,12 +214,26 @@ pub fn save_configuration(manual_execution: bool, upper_notebook: &GuiUpperNoteb
|
|||
}
|
||||
}
|
||||
if data_saved {
|
||||
add_text_to_text_view(&text_view_errors, format!("{} {}", fl!("saving_loading_saving_success"), config_file.display()).as_str());
|
||||
add_text_to_text_view(
|
||||
&text_view_errors,
|
||||
fl!(
|
||||
"saving_loading_saving_success",
|
||||
generate_translation_hashmap(vec![("name", config_file.display().to_string())])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
} else {
|
||||
add_text_to_text_view(&text_view_errors, format!("Failed to save configuration data to file {}", config_file.display()).as_str());
|
||||
add_text_to_text_view(
|
||||
&text_view_errors,
|
||||
fl!(
|
||||
"saving_loading_saving_failure",
|
||||
generate_translation_hashmap(vec![("name", config_file.display().to_string())])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
add_text_to_text_view(&text_view_errors, "Failed to get home directory, so can't save file.");
|
||||
add_text_to_text_view(&text_view_errors, fl!("saving_loading_failed_to_get_home_directory").as_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -682,7 +697,10 @@ pub fn load_configuration(manual_execution: bool, upper_notebook: &GuiUpperNoteb
|
|||
list_store.clear();
|
||||
|
||||
for directory in included_directories {
|
||||
let values: [(u32, &dyn ToValue); 1] = [(ColumnsDirectory::Path as u32, &directory)];
|
||||
let values: [(u32, &dyn ToValue); 2] = [
|
||||
(ColumnsIncludedDirectory::Path as u32, &directory),
|
||||
(ColumnsIncludedDirectory::ReferenceButton as u32, &false),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
|
||||
|
@ -692,7 +710,7 @@ pub fn load_configuration(manual_execution: bool, upper_notebook: &GuiUpperNoteb
|
|||
list_store.clear();
|
||||
|
||||
for directory in excluded_directories {
|
||||
let values: [(u32, &dyn ToValue); 1] = [(ColumnsDirectory::Path as u32, &directory)];
|
||||
let values: [(u32, &dyn ToValue); 1] = [(ColumnsExcludedDirectory::Path as u32, &directory)];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
|
||||
|
@ -782,7 +800,10 @@ pub fn reset_configuration(manual_clearing: bool, upper_notebook: &GuiUpperNoteb
|
|||
}
|
||||
};
|
||||
|
||||
let values: [(u32, &dyn ToValue); 1] = [(ColumnsDirectory::Path as u32, ¤t_dir)];
|
||||
let values: [(u32, &dyn ToValue); 2] = [
|
||||
(ColumnsIncludedDirectory::Path as u32, ¤t_dir),
|
||||
(ColumnsIncludedDirectory::ReferenceButton as u32, &false),
|
||||
];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
// Resetting excluded directories
|
||||
|
@ -792,7 +813,7 @@ pub fn reset_configuration(manual_clearing: bool, upper_notebook: &GuiUpperNoteb
|
|||
list_store.clear();
|
||||
if cfg!(target_family = "unix") {
|
||||
for i in ["/proc", "/dev", "/sys", "/run", "/snap"].iter() {
|
||||
let values: [(u32, &dyn ToValue); 1] = [(ColumnsDirectory::Path as u32, &i)];
|
||||
let values: [(u32, &dyn ToValue); 1] = [(ColumnsExcludedDirectory::Path as u32, &i)];
|
||||
list_store.set(&list_store.append(), &values);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,8 +117,11 @@ check_button_general_same_size_tooltip = Ignore from results, files which have i
|
|||
main_label_size_bytes_tooltip = Size of files which will be used in scan
|
||||
|
||||
# Upper window
|
||||
upper_tree_view_included_folder_column_title = Folders to Search
|
||||
upper_tree_view_included_reference_column_title = Reference Folders
|
||||
|
||||
upper_recursive_button = Recursive
|
||||
upper_recursive_button_tooltip = If selected, search also for files which are not placed directly under chosen folders
|
||||
upper_recursive_button_tooltip = If selected, search also for files which are not placed directly under chosen folders.
|
||||
|
||||
upper_manual_add_included_button = Manual Add
|
||||
upper_add_included_button = Add
|
||||
|
@ -127,19 +130,19 @@ upper_manual_add_excluded_button = Manual Add
|
|||
upper_add_excluded_button = Add
|
||||
upper_remove_excluded_button = Remove
|
||||
|
||||
upper_manual_add_included_button_tooltip = Allows to add directory name to search by hand
|
||||
upper_add_included_button_tooltip = Add new directory to search
|
||||
upper_remove_included_button_tooltip = Delete directory from search
|
||||
upper_manual_add_excluded_button_tooltip = Allows to add excluded directory name by hand
|
||||
upper_add_excluded_button_tooltip = Add directory to be excluded in search
|
||||
upper_remove_excluded_button_tooltip = Delete directory from excluded
|
||||
upper_manual_add_included_button_tooltip = Allows to add directory name to search by hand.
|
||||
upper_add_included_button_tooltip = Add new directory to search.
|
||||
upper_remove_included_button_tooltip = Delete directory from search.
|
||||
upper_manual_add_excluded_button_tooltip = Allows to add excluded directory name by hand.
|
||||
upper_add_excluded_button_tooltip = Add directory to be excluded in search.
|
||||
upper_remove_excluded_button_tooltip = Delete directory from excluded.
|
||||
|
||||
upper_notebook_items_configuration = Items Configuration
|
||||
upper_notebook_excluded_directories = Excluded Directories
|
||||
upper_notebook_included_directories = Included Directories
|
||||
|
||||
upper_allowed_extensions_tooltip =
|
||||
Allowed extensions must be separated by commas(by default all are available)
|
||||
Allowed extensions must be separated by commas(by default all are available).
|
||||
|
||||
Macros IMAGE, VIDEO, MUSIC, TEXT which adds multiple extensions at once are also available.
|
||||
|
||||
|
@ -181,7 +184,7 @@ popover_custom_name_check_button_entry_tooltip =
|
|||
popover_custom_regex_check_button_entry_tooltip =
|
||||
Allows to select records by specified Regex.
|
||||
|
||||
With this mode, searched text is Path with Name
|
||||
With this mode, searched text is Path with Name.
|
||||
|
||||
Example usage:
|
||||
/usr/bin/ziemniak.txt can be found with /ziem[a-z]+
|
||||
|
@ -216,9 +219,9 @@ bottom_symlink_button = Symlink
|
|||
bottom_hardlink_button = Hardlink
|
||||
bottom_move_button = Move
|
||||
|
||||
bottom_search_button_tooltip = Start to search for files/folders
|
||||
bottom_search_button_tooltip = Start to search for files/folders.
|
||||
bottom_select_button_tooltip = Selects records. Only selected files/folders can be later processed.
|
||||
bottom_delete_button_tooltip = Delete selected files/folders
|
||||
bottom_delete_button_tooltip = Delete selected files/folders.
|
||||
bottom_save_button_tooltip = Save data about search to file
|
||||
bottom_symlink_button_tooltip =
|
||||
Creates symbolic links.
|
||||
|
@ -362,21 +365,17 @@ settings_folder_settings_open = Open settings folder
|
|||
# Compute results
|
||||
compute_stopped_by_user = Searching was stopped by user
|
||||
|
||||
compute_found = Found
|
||||
compute_duplicated_files_in = duplicated files in
|
||||
compute_groups_which_took = groups which took
|
||||
compute_groups = groups
|
||||
compute_duplicates_for = duplicates for
|
||||
|
||||
compute_empty_folders = empty folders
|
||||
compute_empty_files = empty files
|
||||
compute_biggest_files = biggest files
|
||||
compute_temporary_files = temporary files
|
||||
compute_similar_image = images
|
||||
compute_similar_videos = videos
|
||||
compute_music_files = music files
|
||||
compute_symlinks = invalid symlinks
|
||||
compute_broken_files = broken files
|
||||
compute_found_duplicates_hash_size = Found { $number_files } duplicates in { $number_groups } groups which took { $size }
|
||||
compute_found_duplicates_name = Found { $number_files } duplicates in { $number_groups } groups
|
||||
compute_found_empty_folders = Found { $number_files } empty folders
|
||||
compute_found_empty_files = Found { $number_files } empty files
|
||||
compute_found_big_files = Found { $number_files } big files
|
||||
compute_found_temporary_files = Found { $number_files } temporary files
|
||||
compute_found_images = Found { $number_files } similar images in { $number_groups } groups
|
||||
compute_found_videos = Found { $number_files } similar videos in { $number_groups } groups
|
||||
compute_found_music = Found { $number_files } similar music files in { $number_groups } groups
|
||||
compute_found_invalid_symlinks = Found { $number_files } invalid symlinks
|
||||
compute_found_broken_files = Found { $number_files } broken files
|
||||
|
||||
# Progress window
|
||||
progress_scanning_general_file = Scanning {$file_number} file
|
||||
|
@ -396,9 +395,12 @@ progress_current_stage = Current Stage:{" "}
|
|||
progress_all_stages = All Stages:{" "}
|
||||
|
||||
# Saving loading
|
||||
saving_loading_saving_success = Saved configuration to file
|
||||
saving_loading_saving_success = Saved configuration to file { $name }.
|
||||
saving_loading_saving_failure = Failed to save configuration data to file { $name }.
|
||||
saving_loading_reset_configuration = Current configuration was cleared.
|
||||
saving_loading_loading_success = Properly loaded configuration from file
|
||||
saving_loading_loading_success = Properly loaded configuration from file.
|
||||
|
||||
saving_loading_failed_to_get_home_directory = Failed to get home directory to open/save config file.
|
||||
|
||||
# Invalid symlinks
|
||||
invalid_symlink_infinite_recursion = Infinite recursion
|
||||
|
@ -438,7 +440,7 @@ hard_sym_link_label = Are you sure that you want to link this files?
|
|||
move_folder_failed = Failed to move folder {$name}, reason {$reason}
|
||||
move_file_failed = Failed to move file {$name}, reason {$reason}
|
||||
move_files_title_dialog = Choose folder to which you want to move duplicated files
|
||||
move_files_choose_more_than_1_path = Only 1 path must be selected to be able to copy there duplicated files, selected {$path_number}
|
||||
move_files_choose_more_than_1_path = Only 1 path must be selected to be able to copy there duplicated files, selected {$path_number}.
|
||||
move_stats = Properly moved {$num_files}/{$all_files} items
|
||||
|
||||
save_results_to_file = Saved results to file {$name}
|
||||
|
@ -460,8 +462,8 @@ cache_clear_message_label_3 = This may speedup a little loading/saving to cache.
|
|||
cache_clear_message_label_4 = WARNING: Operation will remove all cached data from unplugged external drives, so hash will need to be generated again.
|
||||
|
||||
# Show preview
|
||||
preview_temporary_file = Failed to open temporary image file {$name}, reason {$reason}
|
||||
preview_0_size = Cannot create preview of image {$name}, with 0 width or height
|
||||
preview_temporary_image_save = Failed to save temporary image file to {$name}, reason {$reason}
|
||||
preview_temporary_image_remove = Failed to delete temporary image file {$name}, reason {$reason}
|
||||
preview_failed_to_create_cache_dir = Failed to create dir {$name} needed by image preview, reason {$reason}
|
||||
preview_temporary_file = Failed to open temporary image file {$name}, reason {$reason}.
|
||||
preview_0_size = Cannot create preview of image {$name}, with 0 width or height.
|
||||
preview_temporary_image_save = Failed to save temporary image file to {$name}, reason {$reason}.
|
||||
preview_temporary_image_remove = Failed to delete temporary image file {$name}, reason {$reason}.
|
||||
preview_failed_to_create_cache_dir = Failed to create dir {$name} needed by image preview, reason {$reason}.
|
||||
|
|
|
@ -97,6 +97,8 @@ check_button_general_same_size = Ignoruj identyczny rozmiar
|
|||
check_button_general_same_size_tooltip = Wyrzuca z wyników skanowania pliki, które posiadają identyczny rozmiar, po to by w wynikach zostały tylko niemal identyczne rekordy.
|
||||
main_label_size_bytes_tooltip = Rozmiar plików które będą zawarte przy przeszukiwaniu
|
||||
# Upper window
|
||||
upper_tree_view_included_folder_column_title = Foldery do Przeszukania
|
||||
upper_tree_view_included_reference_column_title = Źródłowy Folder
|
||||
upper_recursive_button = Rekursywnie
|
||||
upper_recursive_button_tooltip = Jeśli zaznaczony, szuka plików i folderów również w katalogach wewnątrz, nawet jeśli nie znajdują się one bezpośrednio w tym folderze.
|
||||
upper_manual_add_included_button = Ręcznie Dodaj
|
||||
|
|
Loading…
Reference in a new issue