diff --git a/Cargo.lock b/Cargo.lock index 6999b23..ef47709 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -502,6 +502,7 @@ dependencies = [ "gtk", "humansize", "image", + "img_hash", "open", "trash", "winapi", diff --git a/czkawka_cli/src/commands.rs b/czkawka_cli/src/commands.rs index 450251a..3378b18 100644 --- a/czkawka_cli/src/commands.rs +++ b/czkawka_cli/src/commands.rs @@ -125,7 +125,7 @@ pub enum Commands { hash_alg: HashAlg, #[structopt(short = "f", long, default_value = "Lanczos3", parse(try_from_str = parse_similar_image_filter), help="Hash algorithm (allowed: Lanczos3, Nearest, Triangle, Faussian, Catmullrom)")] image_filter: FilterType, - #[structopt(short = "c", long, default_value = "8", parse(try_from_str = parse_image_hash_size), help="Hash size (allowed: 4, 8, 16, 32)")] + #[structopt(short = "c", long, default_value = "8", parse(try_from_str = parse_image_hash_size), help="Hash size (allowed: 4, 8, 16)")] hash_size: u8, }, #[structopt(name = "zeroed", about = "Finds zeroed files", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka zeroed -d /home/rafal -e /home/rafal/Pulpit -f results.txt")] @@ -368,8 +368,7 @@ fn parse_image_hash_size(src: &str) -> Result { "4" => 4, "8" => 8, "16" => 16, - "32" => 32, - _ => return Err("Couldn't parse the image hash size (allowed: 4, 8, 16, 32)".to_string()), + _ => return Err("Couldn't parse the image hash size (allowed: 4, 8, 16)".to_string()), }; Ok(hash_size) } diff --git a/czkawka_core/src/similar_images.rs b/czkawka_core/src/similar_images.rs index cf51d57..3d127ed 100644 --- a/czkawka_core/src/similar_images.rs +++ b/czkawka_core/src/similar_images.rs @@ -22,6 +22,13 @@ use std::thread::sleep; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::{fs, mem, thread}; +// TODO check for better values +pub const SIMILAR_VALUES: [[u32; 6]; 3] = [ + [0, 1, 2, 3, 4, 5], // 4 - Max 16 + [0, 2, 5, 7, 14, 20], // 8 - Max 256 + [2, 5, 10, 20, 40, 80], // 16 - Max 65536 +]; + #[derive(Debug)] pub struct ProgressData { pub current_stage: u8, @@ -36,8 +43,6 @@ pub enum Similarity { Similar(u32), } -const MAX_SIMILARITY: u32 = 12; - #[derive(Clone, Debug)] pub struct FileEntry { pub path: PathBuf, @@ -134,7 +139,7 @@ impl SimilarImages { pub fn set_hash_size(&mut self, hash_size: u8) { self.hash_size = match hash_size { - 4 | 8 | 16 | 32 | 64 => hash_size, + 4 | 8 | 16 => hash_size, e => { panic!("Invalid value of hash size {}", e); } @@ -313,7 +318,7 @@ impl SimilarImages { .to_lowercase(); // Checking allowed image extensions - let allowed_image_extensions = [".jpg", ".jpeg", ".png" /*, ".bmp"*/, ".tiff", ".tif", ".pnm", ".tga", ".ff" /*, ".gif"*/, ".jif", ".jfi", ".webp"]; + let allowed_image_extensions = [".jpg", ".jpeg", ".png" /*, ".bmp"*/, ".tiff", ".tif", ".pnm", ".tga", ".ff" /*, ".gif"*/, ".jif", ".jfi" /*, ".webp"*/]; // webp cannot be seen in preview, gif needs to be enabled after releasing image crate 0.24.0, bmp needs to be fixed in image crate if !allowed_image_extensions.iter().any(|e| file_name_lowercase.ends_with(e)) { continue 'dir; } @@ -455,10 +460,17 @@ impl SimilarImages { let hash = hasher.hash_image(&image); let buf: Vec = hash.as_bytes().to_vec(); - if buf.iter().all(|e| *e == 0) { - // A little broken image - return Some(None); + + // Images with hashes with full of 0 or 255 usually means that algorithm fails to decode them because e.g. contains a log of alpha channel + { + if buf.iter().all(|e| *e == 0) { + return Some(None); + } + if buf.iter().all(|e| *e == 255) { + return Some(None); + } } + file_entry.hash = buf.clone(); Some(Some((file_entry, buf))) @@ -512,7 +524,15 @@ impl SimilarImages { let mut this_time_check_hashes; let mut master_of_group: BTreeSet> = Default::default(); // Lista wszystkich głównych hashy, które odpowiadają za porównywanie - for current_similarity in 0..=MAX_SIMILARITY { + // TODO optimize this for big temp_max_similarity values + let temp_max_similarity = match self.hash_size { + 4 => SIMILAR_VALUES[0][5], + 8 => SIMILAR_VALUES[1][5], + 16 => SIMILAR_VALUES[1][5], + _ => panic!(), + }; + + for current_similarity in 0..=temp_max_similarity { this_time_check_hashes = available_hashes.clone(); if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { @@ -757,6 +777,8 @@ fn load_hashes_from_file(text_messages: &mut Messages, hash_size: u8, hash_alg: let mut hashmap_loaded_entries: BTreeMap = Default::default(); + let number_of_results: usize = hash_size as usize * hash_size as usize / 8; + // Read the file line by line using the lines() iterator from std::io::BufRead. for (index, line) in reader.lines().enumerate() { let line = match line { @@ -767,14 +789,21 @@ fn load_hashes_from_file(text_messages: &mut Messages, hash_size: u8, hash_alg: } }; let uuu = line.split("//").collect::>(); - if uuu.len() != 12 { - text_messages.warnings.push(format!("Found invalid data in line {} - ({}) in cache file {}", index + 1, line, cache_file.display())); + if uuu.len() != (number_of_results + 4) { + text_messages.warnings.push(format!( + "Found invalid data in line {} - ({}) in cache file {}, expected {} values, found {}", + index + 1, + line, + cache_file.display(), + uuu.len(), + number_of_results + 4 + )); continue; } // Don't load cache data if destination file not exists if Path::new(uuu[0]).exists() { let mut hash: Vec = Vec::new(); - for i in 0..hash_size { + for i in 0..number_of_results { hash.push(match uuu[4 + i as usize].parse::() { Ok(t) => t, Err(e) => { @@ -843,127 +872,74 @@ fn get_cache_file(hash_size: &u8, hash_alg: &HashAlg, image_filter: &FilterType) format!("cache_similar_images_{}_{}_{}.txt", hash_size, convert_algorithm_to_string(hash_alg), convert_filters_to_string(image_filter)) } -// TODO check for better values pub fn get_string_from_similarity(similarity: &Similarity, hash_size: u8) -> String { + let index_preset = match hash_size { + 4 => 0, + 8 => 1, + 16 => 2, + _ => panic!(), + }; + match similarity { Similarity::None => { panic!() } - Similarity::Similar(h) => match hash_size { - 4 => { - if *h == 0 { + Similarity::Similar(h) => { + #[cfg(debug_assertions)] + { + if *h <= SIMILAR_VALUES[index_preset][0] { format!("Very High {}", *h) - } else if *h <= 1 { + } else if *h <= SIMILAR_VALUES[index_preset][1] { format!("High {}", *h) - } else if *h <= 2 { + } else if *h <= SIMILAR_VALUES[index_preset][2] { format!("Medium {}", *h) - } else if *h <= 3 { + } else if *h <= SIMILAR_VALUES[index_preset][3] { format!("Small {}", *h) - } else if *h <= 4 { + } else if *h <= SIMILAR_VALUES[index_preset][4] { format!("Very Small {}", *h) - } else if *h <= 5 { + } else if *h <= SIMILAR_VALUES[index_preset][5] { format!("Minimal {}", *h) } else { panic!(); } } - 8 => { - if *h == 0 { - format!("Very High {}", *h) - } else if *h <= 1 { - format!("High {}", *h) - } else if *h <= 3 { - format!("Medium {}", *h) - } else if *h <= 5 { - format!("Small {}", *h) - } else if *h <= 8 { - format!("Very Small {}", *h) - } else if *h <= 12 { - format!("Minimal {}", *h) + #[cfg(not(debug_assertions))] + { + if *h <= SIMILAR_VALUES[index_preset][0] { + format!("Very High") + } else if *h <= SIMILAR_VALUES[index_preset][1] { + format!("High") + } else if *h <= SIMILAR_VALUES[index_preset][2] { + format!("Medium") + } else if *h <= SIMILAR_VALUES[index_preset][3] { + format!("Small") + } else if *h <= SIMILAR_VALUES[index_preset][4] { + format!("Very Small") + } else if *h <= SIMILAR_VALUES[index_preset][5] { + format!("Minimal") } else { panic!(); } } - 16 => { - if *h <= 2 { - format!("Very High {}", *h) - } else if *h <= 7 { - format!("High {}", *h) - } else if *h <= 11 { - format!("Medium {}", *h) - } else if *h <= 17 { - format!("Small {}", *h) - } else if *h <= 23 { - format!("Very Small {}", *h) - } else if *h <= 44 { - format!("Minimal {}", *h) - } else { - panic!(); - } - } - 32 => { - if *h <= 10 { - format!("Very High {}", *h) - } else if *h <= 30 { - format!("High {}", *h) - } else if *h <= 50 { - format!("Medium {}", *h) - } else if *h <= 90 { - format!("Small {}", *h) - } else if *h <= 120 { - format!("Very Small {}", *h) - } else if *h <= 180 { - format!("Minimal {}", *h) - } else { - panic!(); - } - } - _ => { - panic!("Not supported hash size"); - } - }, + } } } pub fn return_similarity_from_similarity_preset(similarity_preset: &SimilarityPreset, hash_size: u8) -> Similarity { - match hash_size { - 4 => match similarity_preset { - SimilarityPreset::VeryHigh => Similarity::Similar(0), - SimilarityPreset::High => Similarity::Similar(1), - SimilarityPreset::Medium => Similarity::Similar(2), - SimilarityPreset::Small => Similarity::Similar(3), - SimilarityPreset::VerySmall => Similarity::Similar(4), - SimilarityPreset::Minimal => Similarity::Similar(4), - SimilarityPreset::None => panic!(""), - }, - 8 => match similarity_preset { - SimilarityPreset::VeryHigh => Similarity::Similar(0), - SimilarityPreset::High => Similarity::Similar(1), - SimilarityPreset::Medium => Similarity::Similar(3), - SimilarityPreset::Small => Similarity::Similar(5), - SimilarityPreset::VerySmall => Similarity::Similar(8), - SimilarityPreset::Minimal => Similarity::Similar(12), - SimilarityPreset::None => panic!(""), - }, - 16 => match similarity_preset { - SimilarityPreset::VeryHigh => Similarity::Similar(2), - SimilarityPreset::High => Similarity::Similar(7), - SimilarityPreset::Medium => Similarity::Similar(11), - SimilarityPreset::Small => Similarity::Similar(17), - SimilarityPreset::VerySmall => Similarity::Similar(23), - SimilarityPreset::Minimal => Similarity::Similar(44), - SimilarityPreset::None => panic!(""), - }, - 32 => match similarity_preset { - SimilarityPreset::VeryHigh => Similarity::Similar(10), - SimilarityPreset::High => Similarity::Similar(30), - SimilarityPreset::Medium => Similarity::Similar(50), - SimilarityPreset::Small => Similarity::Similar(90), - SimilarityPreset::VerySmall => Similarity::Similar(120), - SimilarityPreset::Minimal => Similarity::Similar(180), - SimilarityPreset::None => panic!(""), - }, + let index_preset = match hash_size { + 4 => 0, + 8 => 1, + 16 => 2, _ => panic!(), + }; + match similarity_preset { + SimilarityPreset::VeryHigh => Similarity::Similar(SIMILAR_VALUES[index_preset][0]), + SimilarityPreset::High => Similarity::Similar(SIMILAR_VALUES[index_preset][1]), + SimilarityPreset::Medium => Similarity::Similar(SIMILAR_VALUES[index_preset][2]), + SimilarityPreset::Small => Similarity::Similar(SIMILAR_VALUES[index_preset][3]), + SimilarityPreset::VerySmall => Similarity::Similar(SIMILAR_VALUES[index_preset][4]), + SimilarityPreset::Minimal => Similarity::Similar(SIMILAR_VALUES[index_preset][5]), + SimilarityPreset::None => panic!(""), } } diff --git a/czkawka_gui/Cargo.toml b/czkawka_gui/Cargo.toml index b9d87bf..1828fc8 100644 --- a/czkawka_gui/Cargo.toml +++ b/czkawka_gui/Cargo.toml @@ -31,6 +31,9 @@ open = "2.0.1" # To get image preview image = "0.23.14" +# To get image_hash types +img_hash = "3.2.0" + # Move files to trash trash = "1.3.0" diff --git a/czkawka_gui/src/connect_about_buttons.rs b/czkawka_gui/src/connect_about_buttons.rs index ad0ba26..6654cc5 100644 --- a/czkawka_gui/src/connect_about_buttons.rs +++ b/czkawka_gui/src/connect_about_buttons.rs @@ -9,22 +9,25 @@ const INSTRUCTION_SITE: &str = "https://github.com/qarmin/czkawka/blob/master/in pub fn connect_about_buttons(gui_data: &GuiData) { let button_donation = gui_data.about.button_donation.clone(); button_donation.connect_clicked(move |_| { - if let Err(e) = open::that(SPONSOR_SITE) { - println!("Failed to open sponsor site: {}, reason {}", SPONSOR_SITE, e) - }; + open::that_in_background(SPONSOR_SITE); + // if let Err(e) = open::that(SPONSOR_SITE) { + // println!("Failed to open sponsor site: {}, reason {}", SPONSOR_SITE, e) + // }; }); let button_instruction = gui_data.about.button_instruction.clone(); button_instruction.connect_clicked(move |_| { - if let Err(e) = open::that(INSTRUCTION_SITE) { - println!("Failed to open instruction site: {}, reason {}", INSTRUCTION_SITE, e) - }; + open::that_in_background(INSTRUCTION_SITE); + // if let Err(e) = open::that(INSTRUCTION_SITE) { + // println!("Failed to open instruction site: {}, reason {}", INSTRUCTION_SITE, e) + // }; }); let button_repository = gui_data.about.button_repository.clone(); button_repository.connect_clicked(move |_| { - if let Err(e) = open::that(REPOSITORY_SITE) { - println!("Failed to open repository site: {}, reason {}", REPOSITORY_SITE, e) - }; + open::that_in_background(REPOSITORY_SITE); + // if let Err(e) = open::that(REPOSITORY_SITE) { + // println!("Failed to open repository site: {}, reason {}", REPOSITORY_SITE, e) + // }; }); } diff --git a/czkawka_gui/src/connect_button_search.rs b/czkawka_gui/src/connect_button_search.rs index 7b482b9..802ce91 100644 --- a/czkawka_gui/src/connect_button_search.rs +++ b/czkawka_gui/src/connect_button_search.rs @@ -17,6 +17,7 @@ use czkawka_core::zeroed::ZeroedFiles; use glib::Sender; use gtk::prelude::*; use gtk::WindowPosition; +use img_hash::{FilterType, HashAlg}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread; @@ -91,6 +92,19 @@ pub fn connect_button_search( let check_button_settings_hide_hard_links = gui_data.settings.check_button_settings_hide_hard_links.clone(); let check_button_settings_use_cache = gui_data.settings.check_button_settings_use_cache.clone(); let entry_settings_cache_file_minimal_size = gui_data.settings.entry_settings_cache_file_minimal_size.clone(); + let radio_button_similar_hash_size_4 = gui_data.main_notebook.radio_button_similar_hash_size_4.clone(); + let radio_button_similar_hash_size_8 = gui_data.main_notebook.radio_button_similar_hash_size_8.clone(); + let radio_button_similar_hash_size_16 = gui_data.main_notebook.radio_button_similar_hash_size_16.clone(); + let radio_button_resize_algorithm_catmullrom = gui_data.main_notebook.radio_button_resize_algorithm_catmullrom.clone(); + let radio_button_resize_algorithm_lanczos3 = gui_data.main_notebook.radio_button_resize_algorithm_lanczos3.clone(); + let radio_button_resize_algorithm_nearest = gui_data.main_notebook.radio_button_resize_algorithm_nearest.clone(); + let radio_button_resize_algorithm_triangle = gui_data.main_notebook.radio_button_resize_algorithm_triangle.clone(); + let radio_button_resize_algorithm_gaussian = gui_data.main_notebook.radio_button_resize_algorithm_gaussian.clone(); + let radio_button_similar_hash_algorithm_gradient = gui_data.main_notebook.radio_button_similar_hash_algorithm_gradient.clone(); + let radio_button_similar_hash_algorithm_blockhash = gui_data.main_notebook.radio_button_similar_hash_algorithm_blockhash.clone(); + let radio_button_similar_hash_algorithm_mean = gui_data.main_notebook.radio_button_similar_hash_algorithm_mean.clone(); + let radio_button_similar_hash_algorithm_vertgradient = gui_data.main_notebook.radio_button_similar_hash_algorithm_vertgradient.clone(); + let radio_button_similar_hash_algorithm_doublegradient = gui_data.main_notebook.radio_button_similar_hash_algorithm_doublegradient.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)); @@ -268,6 +282,47 @@ pub fn connect_button_search( get_list_store(&tree_view_similar_images_finder).clear(); + let hash_size; + if radio_button_similar_hash_size_4.is_active() { + hash_size = 4; + } else if radio_button_similar_hash_size_8.is_active() { + hash_size = 8; + } else if radio_button_similar_hash_size_16.is_active() { + hash_size = 16; + } else { + panic!("No radio button is pressed"); + } + + let image_filter; + if radio_button_resize_algorithm_catmullrom.is_active() { + image_filter = FilterType::CatmullRom; + } else if radio_button_resize_algorithm_lanczos3.is_active() { + image_filter = FilterType::Lanczos3; + } else if radio_button_resize_algorithm_nearest.is_active() { + image_filter = FilterType::Nearest; + } else if radio_button_resize_algorithm_triangle.is_active() { + image_filter = FilterType::Triangle; + } else if radio_button_resize_algorithm_gaussian.is_active() { + image_filter = FilterType::Gaussian; + } else { + panic!("No radio button is pressed"); + } + + let hash_alg; + if radio_button_similar_hash_algorithm_blockhash.is_active() { + hash_alg = HashAlg::Blockhash; + } else if radio_button_similar_hash_algorithm_gradient.is_active() { + hash_alg = HashAlg::Gradient; + } else if radio_button_similar_hash_algorithm_mean.is_active() { + hash_alg = HashAlg::Mean; + } else if radio_button_similar_hash_algorithm_vertgradient.is_active() { + hash_alg = HashAlg::VertGradient; + } else if radio_button_similar_hash_algorithm_doublegradient.is_active() { + hash_alg = HashAlg::DoubleGradient; + } else { + panic!("No radio button is pressed"); + } + let minimal_file_size = entry_similar_images_minimal_size.text().as_str().parse::().unwrap_or(1024 * 16); let maximal_file_size = entry_similar_images_maximal_size.text().as_str().parse::().unwrap_or(1024 * 1024 * 1024 * 1024); @@ -286,6 +341,9 @@ pub fn connect_button_search( sf.set_maximal_file_size(maximal_file_size); sf.set_similarity(similarity); sf.set_use_cache(use_cache); + sf.set_hash_alg(hash_alg); + sf.set_hash_size(hash_size); + sf.set_image_filter(image_filter); sf.find_similar_images(Some(&stop_receiver), Some(&futures_sender_similar_images)); let _ = glib_stop_sender.send(Message::SimilarImages(sf)); }); diff --git a/czkawka_gui/src/connect_compute_results.rs b/czkawka_gui/src/connect_compute_results.rs index 764ec9a..af42a5d 100644 --- a/czkawka_gui/src/connect_compute_results.rs +++ b/czkawka_gui/src/connect_compute_results.rs @@ -42,6 +42,9 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver< let buttons_names = gui_data.bottom_buttons.buttons_names.clone(); let window_progress = gui_data.progress_window.window_progress.clone(); let taskbar_state = gui_data.taskbar_state.clone(); + let radio_button_similar_hash_size_4 = gui_data.main_notebook.radio_button_similar_hash_size_4.clone(); + let radio_button_similar_hash_size_8 = gui_data.main_notebook.radio_button_similar_hash_size_8.clone(); + let radio_button_similar_hash_size_16 = gui_data.main_notebook.radio_button_similar_hash_size_16.clone(); let main_context = glib::MainContext::default(); let _guard = main_context.acquire().unwrap(); @@ -56,6 +59,17 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver< // Restore clickability to main notebook notebook_main.set_sensitive(true); + let hash_size; + if radio_button_similar_hash_size_4.is_active() { + hash_size = 4; + } else if radio_button_similar_hash_size_8.is_active() { + hash_size = 8; + } else if radio_button_similar_hash_size_16.is_active() { + hash_size = 16; + } else { + panic!("No radio button is pressed"); + } + match msg { Message::Duplicates(df) => { if df.get_stopped_search() { @@ -523,7 +537,7 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver< let values: [(u32, &dyn ToValue); 12] = [ (0, &true), (1, &false), - (2, &(similar_images::get_string_from_similarity(&file_entry.similarity, 8).to_string())), // TODO use proper hash value + (2, &(similar_images::get_string_from_similarity(&file_entry.similarity, hash_size).to_string())), (3, &file_entry.size.file_size(options::BINARY).unwrap()), (4, &file_entry.size), (5, &file_entry.dimensions), diff --git a/czkawka_gui/src/connect_similar_image_size_change.rs b/czkawka_gui/src/connect_similar_image_size_change.rs new file mode 100644 index 0000000..529399f --- /dev/null +++ b/czkawka_gui/src/connect_similar_image_size_change.rs @@ -0,0 +1,32 @@ +extern crate gtk; +use crate::gui_data::GuiData; +use czkawka_core::similar_images::SIMILAR_VALUES; +use gtk::prelude::*; + +pub fn connect_similar_image_size_change(gui_data: &GuiData) { + // This should set values to max possible value like in return_similarity_from_similarity_preset and get_string_from_similarity + { + let radio_button_similar_hash_size_4 = gui_data.main_notebook.radio_button_similar_hash_size_4.clone(); + let scale_similarity = gui_data.main_notebook.scale_similarity.clone(); + radio_button_similar_hash_size_4.connect_clicked(move |_| { + scale_similarity.set_range(0_f64, SIMILAR_VALUES[0][5] as f64); + scale_similarity.set_fill_level(SIMILAR_VALUES[0][5] as f64); + }); + } + { + let radio_button_similar_hash_size_8 = gui_data.main_notebook.radio_button_similar_hash_size_8.clone(); + let scale_similarity = gui_data.main_notebook.scale_similarity.clone(); + radio_button_similar_hash_size_8.connect_clicked(move |_| { + scale_similarity.set_range(0_f64, SIMILAR_VALUES[1][5] as f64); + scale_similarity.set_fill_level(SIMILAR_VALUES[1][5] as f64); + }); + } + { + let radio_button_similar_hash_size_16 = gui_data.main_notebook.radio_button_similar_hash_size_16.clone(); + let scale_similarity = gui_data.main_notebook.scale_similarity.clone(); + radio_button_similar_hash_size_16.connect_clicked(move |_| { + scale_similarity.set_range(0_f64, SIMILAR_VALUES[2][5] as f64); + scale_similarity.set_fill_level(SIMILAR_VALUES[2][5] as f64); + }); + } +} diff --git a/czkawka_gui/src/double_click_opening.rs b/czkawka_gui/src/double_click_opening.rs index 4553a97..5cfab70 100644 --- a/czkawka_gui/src/double_click_opening.rs +++ b/czkawka_gui/src/double_click_opening.rs @@ -162,9 +162,11 @@ fn common_open_function(tree_view: >k::TreeView, column_name: i32, column_path } } - if let Err(e) = open::that(&end_path) { - println!("Failed to open {} - Error {}", end_path, e); - } + open::that_in_background(&end_path); + + // if let Err(e) = open::that(&end_path) { + // println!("Failed to open {} - Error {}", end_path, e); + // } } } diff --git a/czkawka_gui/src/gui_main_notebook.rs b/czkawka_gui/src/gui_main_notebook.rs index 8648c98..6df640d 100644 --- a/czkawka_gui/src/gui_main_notebook.rs +++ b/czkawka_gui/src/gui_main_notebook.rs @@ -56,6 +56,22 @@ pub struct GuiMainNotebook { pub radio_button_hash_type_crc32: gtk::RadioButton, pub radio_button_hash_type_xxh3: gtk::RadioButton, + pub radio_button_resize_algorithm_lanczos3: gtk::RadioButton, + pub radio_button_resize_algorithm_nearest: gtk::RadioButton, + pub radio_button_resize_algorithm_triangle: gtk::RadioButton, + pub radio_button_resize_algorithm_gaussian: gtk::RadioButton, + pub radio_button_resize_algorithm_catmullrom: gtk::RadioButton, + + pub radio_button_similar_hash_algorithm_gradient: gtk::RadioButton, + pub radio_button_similar_hash_algorithm_blockhash: gtk::RadioButton, + pub radio_button_similar_hash_algorithm_mean: gtk::RadioButton, + pub radio_button_similar_hash_algorithm_vertgradient: gtk::RadioButton, + pub radio_button_similar_hash_algorithm_doublegradient: gtk::RadioButton, + + pub radio_button_similar_hash_size_4: gtk::RadioButton, + pub radio_button_similar_hash_size_8: gtk::RadioButton, + pub radio_button_similar_hash_size_16: gtk::RadioButton, + pub image_preview_similar_images: gtk::Image, pub image_preview_duplicates: gtk::Image, } @@ -114,6 +130,22 @@ impl GuiMainNotebook { let radio_button_hash_type_crc32: gtk::RadioButton = builder.object("radio_button_hash_type_crc32").unwrap(); let radio_button_hash_type_xxh3: gtk::RadioButton = builder.object("radio_button_hash_type_xxh3").unwrap(); + let radio_button_resize_algorithm_lanczos3: gtk::RadioButton = builder.object("radio_button_resize_algorithm_lanczos3").unwrap(); + let radio_button_resize_algorithm_nearest: gtk::RadioButton = builder.object("radio_button_resize_algorithm_nearest").unwrap(); + let radio_button_resize_algorithm_triangle: gtk::RadioButton = builder.object("radio_button_resize_algorithm_triangle").unwrap(); + let radio_button_resize_algorithm_gaussian: gtk::RadioButton = builder.object("radio_button_resize_algorithm_gaussian").unwrap(); + let radio_button_resize_algorithm_catmullrom: gtk::RadioButton = builder.object("radio_button_resize_algorithm_catmullrom").unwrap(); + + let radio_button_similar_hash_algorithm_gradient: gtk::RadioButton = builder.object("radio_button_similar_hash_algorithm_gradient").unwrap(); + let radio_button_similar_hash_algorithm_blockhash: gtk::RadioButton = builder.object("radio_button_similar_hash_algorithm_blockhash").unwrap(); + let radio_button_similar_hash_algorithm_mean: gtk::RadioButton = builder.object("radio_button_similar_hash_algorithm_mean").unwrap(); + let radio_button_similar_hash_algorithm_vertgradient: gtk::RadioButton = builder.object("radio_button_similar_hash_algorithm_vertgradient").unwrap(); + let radio_button_similar_hash_algorithm_doublegradient: gtk::RadioButton = builder.object("radio_button_similar_hash_algorithm_doublegradient").unwrap(); + + let radio_button_similar_hash_size_4: gtk::RadioButton = builder.object("radio_button_similar_hash_size_4").unwrap(); + let radio_button_similar_hash_size_8: gtk::RadioButton = builder.object("radio_button_similar_hash_size_8").unwrap(); + let radio_button_similar_hash_size_16: gtk::RadioButton = builder.object("radio_button_similar_hash_size_16").unwrap(); + let image_preview_similar_images: gtk::Image = builder.object("image_preview_similar_images").unwrap(); let image_preview_duplicates: gtk::Image = builder.object("image_preview_duplicates").unwrap(); @@ -157,6 +189,19 @@ impl GuiMainNotebook { radio_button_hash_type_blake3, radio_button_hash_type_crc32, radio_button_hash_type_xxh3, + radio_button_resize_algorithm_lanczos3, + radio_button_resize_algorithm_nearest, + radio_button_resize_algorithm_triangle, + radio_button_resize_algorithm_gaussian, + radio_button_resize_algorithm_catmullrom, + radio_button_similar_hash_algorithm_gradient, + radio_button_similar_hash_algorithm_blockhash, + radio_button_similar_hash_algorithm_mean, + radio_button_similar_hash_algorithm_vertgradient, + radio_button_similar_hash_algorithm_doublegradient, + radio_button_similar_hash_size_4, + radio_button_similar_hash_size_8, + radio_button_similar_hash_size_16, image_preview_similar_images, entry_duplicate_maximal_size, entry_same_music_maximal_size, diff --git a/czkawka_gui/src/initialize_gui.rs b/czkawka_gui/src/initialize_gui.rs index 6732c8e..e8494d8 100644 --- a/czkawka_gui/src/initialize_gui.rs +++ b/czkawka_gui/src/initialize_gui.rs @@ -3,6 +3,7 @@ use crate::create_tree_view::*; use crate::double_click_opening::*; use crate::gui_data::*; use crate::help_functions::*; +use czkawka_core::similar_images::SIMILAR_VALUES; use directories_next::ProjectDirs; use gtk::prelude::*; use gtk::{CheckButton, Image, SelectionMode, TextView, TreeView}; @@ -54,7 +55,8 @@ pub fn initialize_gui(gui_data: &mut GuiData) { // Set step increment { - scale_similarity.set_range(0_f64, 12_f64); + scale_similarity.set_range(0_f64, SIMILAR_VALUES[1][5] as f64); // This defaults to value of minimal size of hash 8 + scale_similarity.set_fill_level(SIMILAR_VALUES[1][5] as f64); scale_similarity.adjustment().set_step_increment(1_f64); } diff --git a/czkawka_gui/src/main.rs b/czkawka_gui/src/main.rs index 8e0f355..18c95c2 100644 --- a/czkawka_gui/src/main.rs +++ b/czkawka_gui/src/main.rs @@ -18,6 +18,7 @@ mod connect_popovers; mod connect_progress_window; mod connect_selection_of_directories; mod connect_settings; +mod connect_similar_image_size_change; mod create_tree_view; mod double_click_opening; mod gui_about; @@ -58,6 +59,7 @@ use crate::connect_popovers::*; use crate::connect_progress_window::*; use crate::connect_selection_of_directories::*; use crate::connect_settings::*; +use crate::connect_similar_image_size_change::*; use crate::gui_data::*; use crate::initialize_gui::*; use crate::saving_loading::*; @@ -147,6 +149,7 @@ fn main() { connect_settings(&gui_data); connect_button_about(&gui_data); connect_about_buttons(&gui_data); + connect_similar_image_size_change(&gui_data); // Quit the program when X in main window was clicked { diff --git a/czkawka_gui/ui/main_window.glade b/czkawka_gui/ui/main_window.glade index cb51e70..908f751 100644 --- a/czkawka_gui/ui/main_window.glade +++ b/czkawka_gui/ui/main_window.glade @@ -1,5 +1,5 @@ -