mirror of https://github.com/qarmin/czkawka
Add compare images tool (#568)
* Add compare images tool * Crash lord, crashes more than needed * Kluchy piertuchy * Głowa mała * Troszkę * Czokoszoki * Ajzenbiśla * Środa * Piątek * Stonk * TreePath * Czego, śpiem przecież * Mój tród skończon * Aktualizacja nazwpull/571/head
parent
f6599f51ae
commit
5dab9286cb
@ -0,0 +1,603 @@
|
||||
use czkawka_core::common::get_dynamic_image_from_raw_image;
|
||||
use czkawka_core::fl;
|
||||
use czkawka_core::similar_images::RAW_IMAGE_EXTENSIONS;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{CheckButton, Image, ListStore, Orientation, ScrolledWindow, TreeIter, TreeModel, TreePath, TreeSelection};
|
||||
use image::imageops::FilterType;
|
||||
use image::DynamicImage;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::gui_data::GuiData;
|
||||
use crate::help_functions::{
|
||||
count_number_of_groups, get_full_name_from_path_name, get_image_path_temporary, get_max_file_name, resize_dynamic_image_dimension, NotebookObject, HEADER_ROW_COLOR,
|
||||
NOTEBOOKS_INFOS,
|
||||
};
|
||||
use crate::localizer::generate_translation_hashmap;
|
||||
|
||||
const BIG_PREVIEW_SIZE: u32 = 600;
|
||||
const SMALL_PREVIEW_SIZE: u32 = 100;
|
||||
|
||||
pub fn connect_button_compare(gui_data: &GuiData) {
|
||||
let button_compare = gui_data.bottom_buttons.buttons_compare.clone();
|
||||
let window_compare = gui_data.compare_images.window_compare.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let main_tree_views = gui_data.main_notebook.get_main_tree_views();
|
||||
let scrolled_window_compare_choose_images = gui_data.compare_images.scrolled_window_compare_choose_images.clone();
|
||||
|
||||
let label_group_info = gui_data.compare_images.label_group_info.clone();
|
||||
|
||||
let button_go_previous_compare_group = gui_data.compare_images.button_go_previous_compare_group.clone();
|
||||
let button_go_next_compare_group = gui_data.compare_images.button_go_next_compare_group.clone();
|
||||
|
||||
let check_button_left_preview_text = gui_data.compare_images.check_button_left_preview_text.clone();
|
||||
let check_button_right_preview_text = gui_data.compare_images.check_button_right_preview_text.clone();
|
||||
|
||||
let shared_numbers_of_groups = gui_data.compare_images.shared_numbers_of_groups.clone();
|
||||
let shared_current_of_groups = gui_data.compare_images.shared_current_of_groups.clone();
|
||||
let shared_current_iter = gui_data.compare_images.shared_current_iter.clone();
|
||||
let shared_image_cache = gui_data.compare_images.shared_image_cache.clone();
|
||||
let shared_using_for_preview = gui_data.compare_images.shared_using_for_preview.clone();
|
||||
|
||||
let image_compare_left = gui_data.compare_images.image_compare_left.clone();
|
||||
let image_compare_right = gui_data.compare_images.image_compare_right.clone();
|
||||
|
||||
button_compare.connect_clicked(move |_| {
|
||||
let nb_number = notebook_main.current_page().unwrap();
|
||||
let tree_view = &main_tree_views[nb_number as usize];
|
||||
let nb_object = &NOTEBOOKS_INFOS[nb_number as usize];
|
||||
let model = tree_view.model().unwrap();
|
||||
|
||||
let group_number = count_number_of_groups(tree_view, nb_object.column_color.unwrap());
|
||||
|
||||
if group_number == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
*shared_current_of_groups.borrow_mut() = 1;
|
||||
*shared_numbers_of_groups.borrow_mut() = group_number;
|
||||
|
||||
button_go_previous_compare_group.set_sensitive(false);
|
||||
if group_number == 1 {
|
||||
button_go_next_compare_group.set_sensitive(false);
|
||||
} else {
|
||||
button_go_next_compare_group.set_sensitive(true);
|
||||
}
|
||||
|
||||
// Check selected items
|
||||
let (current_group, tree_iter) = get_current_group_and_iter_from_selection(&model, tree_view.selection(), nb_object.column_color.unwrap());
|
||||
|
||||
populate_groups_at_start(
|
||||
nb_object,
|
||||
&model,
|
||||
shared_current_iter.clone(),
|
||||
tree_iter,
|
||||
&image_compare_left,
|
||||
&image_compare_right,
|
||||
current_group,
|
||||
group_number,
|
||||
&check_button_left_preview_text,
|
||||
&check_button_right_preview_text,
|
||||
&scrolled_window_compare_choose_images,
|
||||
&label_group_info,
|
||||
shared_image_cache.clone(),
|
||||
shared_using_for_preview.clone(),
|
||||
);
|
||||
|
||||
window_compare.show();
|
||||
});
|
||||
|
||||
let shared_image_cache = gui_data.compare_images.shared_image_cache.clone();
|
||||
let shared_current_iter = gui_data.compare_images.shared_current_iter.clone();
|
||||
let shared_using_for_preview = gui_data.compare_images.shared_using_for_preview.clone();
|
||||
let shared_current_of_groups = gui_data.compare_images.shared_current_of_groups.clone();
|
||||
let shared_numbers_of_groups = gui_data.compare_images.shared_numbers_of_groups.clone();
|
||||
let window_compare = gui_data.compare_images.window_compare.clone();
|
||||
let image_compare_left = gui_data.compare_images.image_compare_left.clone();
|
||||
let image_compare_right = gui_data.compare_images.image_compare_right.clone();
|
||||
window_compare.connect_delete_event(move |window_compare, _| {
|
||||
window_compare.hide();
|
||||
*shared_image_cache.borrow_mut() = Vec::new();
|
||||
*shared_current_iter.borrow_mut() = None;
|
||||
*shared_current_of_groups.borrow_mut() = 0;
|
||||
*shared_numbers_of_groups.borrow_mut() = 0;
|
||||
*shared_using_for_preview.borrow_mut() = (None, None);
|
||||
image_compare_left.set_from_pixbuf(None);
|
||||
image_compare_right.set_from_pixbuf(None);
|
||||
gtk::Inhibit(true)
|
||||
});
|
||||
|
||||
let button_go_previous_compare_group = gui_data.compare_images.button_go_previous_compare_group.clone();
|
||||
let button_go_next_compare_group = gui_data.compare_images.button_go_next_compare_group.clone();
|
||||
let label_group_info = gui_data.compare_images.label_group_info.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let main_tree_views = gui_data.main_notebook.get_main_tree_views();
|
||||
let scrolled_window_compare_choose_images = gui_data.compare_images.scrolled_window_compare_choose_images.clone();
|
||||
|
||||
let check_button_left_preview_text = gui_data.compare_images.check_button_left_preview_text.clone();
|
||||
let check_button_right_preview_text = gui_data.compare_images.check_button_right_preview_text.clone();
|
||||
|
||||
let shared_current_of_groups = gui_data.compare_images.shared_current_of_groups.clone();
|
||||
let shared_numbers_of_groups = gui_data.compare_images.shared_numbers_of_groups.clone();
|
||||
let shared_current_iter = gui_data.compare_images.shared_current_iter.clone();
|
||||
let shared_image_cache = gui_data.compare_images.shared_image_cache.clone();
|
||||
let shared_using_for_preview = gui_data.compare_images.shared_using_for_preview.clone();
|
||||
|
||||
let image_compare_left = gui_data.compare_images.image_compare_left.clone();
|
||||
let image_compare_right = gui_data.compare_images.image_compare_right.clone();
|
||||
|
||||
button_go_previous_compare_group.connect_clicked(move |button_go_previous_compare_group| {
|
||||
let nb_number = notebook_main.current_page().unwrap();
|
||||
let tree_view = &main_tree_views[nb_number as usize];
|
||||
let nb_object = &NOTEBOOKS_INFOS[nb_number as usize];
|
||||
let model = tree_view.model().unwrap();
|
||||
|
||||
*shared_current_of_groups.borrow_mut() -= 1;
|
||||
|
||||
let current_group = *shared_current_of_groups.borrow();
|
||||
let group_number = *shared_numbers_of_groups.borrow();
|
||||
|
||||
if current_group == 1 {
|
||||
button_go_previous_compare_group.set_sensitive(false);
|
||||
}
|
||||
button_go_next_compare_group.set_sensitive(true);
|
||||
|
||||
let tree_iter = move_iter(&model, shared_current_iter.borrow().as_ref().unwrap(), nb_object.column_color.unwrap(), false);
|
||||
|
||||
populate_groups_at_start(
|
||||
nb_object,
|
||||
&model,
|
||||
shared_current_iter.clone(),
|
||||
tree_iter,
|
||||
&image_compare_left,
|
||||
&image_compare_right,
|
||||
current_group,
|
||||
group_number,
|
||||
&check_button_left_preview_text,
|
||||
&check_button_right_preview_text,
|
||||
&scrolled_window_compare_choose_images,
|
||||
&label_group_info,
|
||||
shared_image_cache.clone(),
|
||||
shared_using_for_preview.clone(),
|
||||
);
|
||||
});
|
||||
|
||||
let button_go_previous_compare_group = gui_data.compare_images.button_go_previous_compare_group.clone();
|
||||
let button_go_next_compare_group = gui_data.compare_images.button_go_next_compare_group.clone();
|
||||
let label_group_info = gui_data.compare_images.label_group_info.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let main_tree_views = gui_data.main_notebook.get_main_tree_views();
|
||||
let scrolled_window_compare_choose_images = gui_data.compare_images.scrolled_window_compare_choose_images.clone();
|
||||
|
||||
let check_button_left_preview_text = gui_data.compare_images.check_button_left_preview_text.clone();
|
||||
let check_button_right_preview_text = gui_data.compare_images.check_button_right_preview_text.clone();
|
||||
|
||||
let shared_current_of_groups = gui_data.compare_images.shared_current_of_groups.clone();
|
||||
let shared_numbers_of_groups = gui_data.compare_images.shared_numbers_of_groups.clone();
|
||||
let shared_current_iter = gui_data.compare_images.shared_current_iter.clone();
|
||||
let shared_image_cache = gui_data.compare_images.shared_image_cache.clone();
|
||||
let shared_using_for_preview = gui_data.compare_images.shared_using_for_preview.clone();
|
||||
|
||||
let image_compare_left = gui_data.compare_images.image_compare_left.clone();
|
||||
let image_compare_right = gui_data.compare_images.image_compare_right.clone();
|
||||
|
||||
button_go_next_compare_group.connect_clicked(move |button_go_next_compare_group| {
|
||||
let nb_number = notebook_main.current_page().unwrap();
|
||||
let tree_view = &main_tree_views[nb_number as usize];
|
||||
let nb_object = &NOTEBOOKS_INFOS[nb_number as usize];
|
||||
let model = tree_view.model().unwrap();
|
||||
|
||||
*shared_current_of_groups.borrow_mut() += 1;
|
||||
|
||||
let current_group = *shared_current_of_groups.borrow();
|
||||
let group_number = *shared_numbers_of_groups.borrow();
|
||||
|
||||
if group_number == current_group {
|
||||
button_go_next_compare_group.set_sensitive(false);
|
||||
}
|
||||
button_go_previous_compare_group.set_sensitive(true);
|
||||
|
||||
let tree_iter = move_iter(&model, shared_current_iter.borrow().as_ref().unwrap(), nb_object.column_color.unwrap(), true);
|
||||
|
||||
populate_groups_at_start(
|
||||
nb_object,
|
||||
&model,
|
||||
shared_current_iter.clone(),
|
||||
tree_iter,
|
||||
&image_compare_left,
|
||||
&image_compare_right,
|
||||
current_group,
|
||||
group_number,
|
||||
&check_button_left_preview_text,
|
||||
&check_button_right_preview_text,
|
||||
&scrolled_window_compare_choose_images,
|
||||
&label_group_info,
|
||||
shared_image_cache.clone(),
|
||||
shared_using_for_preview.clone(),
|
||||
);
|
||||
});
|
||||
|
||||
let check_button_left_preview_text = gui_data.compare_images.check_button_left_preview_text.clone();
|
||||
let shared_using_for_preview = gui_data.compare_images.shared_using_for_preview.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let shared_current_iter = gui_data.compare_images.shared_current_iter.clone();
|
||||
let main_tree_views = gui_data.main_notebook.get_main_tree_views();
|
||||
check_button_left_preview_text.connect_clicked(move |check_button_left_preview_text| {
|
||||
let nb_number = notebook_main.current_page().unwrap();
|
||||
let tree_view = &main_tree_views[nb_number as usize];
|
||||
let nb_object = &NOTEBOOKS_INFOS[nb_number as usize];
|
||||
let model = tree_view.model().unwrap().downcast::<ListStore>().unwrap();
|
||||
|
||||
let main_tree_path = model.path(shared_current_iter.borrow().as_ref().unwrap()).unwrap();
|
||||
let this_tree_path = shared_using_for_preview.borrow().0.clone().unwrap();
|
||||
if main_tree_path == this_tree_path {
|
||||
return; // Selected header, so we don't need to select result in treeview
|
||||
// TODO this should be handled by disabling entirely check box
|
||||
}
|
||||
|
||||
let is_active = check_button_left_preview_text.is_active();
|
||||
model.set_value(&model.iter(&this_tree_path).unwrap(), nb_object.column_selection as u32, &is_active.to_value());
|
||||
});
|
||||
|
||||
let check_button_right_preview_text = gui_data.compare_images.check_button_right_preview_text.clone();
|
||||
let shared_using_for_preview = gui_data.compare_images.shared_using_for_preview.clone();
|
||||
let shared_current_iter = gui_data.compare_images.shared_current_iter.clone();
|
||||
let notebook_main = gui_data.main_notebook.notebook_main.clone();
|
||||
let main_tree_views = gui_data.main_notebook.get_main_tree_views();
|
||||
check_button_right_preview_text.connect_clicked(move |check_button_right_preview_text| {
|
||||
let nb_number = notebook_main.current_page().unwrap();
|
||||
let tree_view = &main_tree_views[nb_number as usize];
|
||||
let nb_object = &NOTEBOOKS_INFOS[nb_number as usize];
|
||||
let model = tree_view.model().unwrap().downcast::<ListStore>().unwrap();
|
||||
|
||||
let main_tree_path = model.path(shared_current_iter.borrow().as_ref().unwrap()).unwrap();
|
||||
let this_tree_path = shared_using_for_preview.borrow().1.clone().unwrap();
|
||||
if main_tree_path == this_tree_path {
|
||||
return; // Selected header, so we don't need to select result in treeview
|
||||
// TODO this should be handled by disabling entirely check box
|
||||
}
|
||||
|
||||
let is_active = check_button_right_preview_text.is_active();
|
||||
model.set_value(&model.iter(&this_tree_path).unwrap(), nb_object.column_selection as u32, &is_active.to_value());
|
||||
});
|
||||
}
|
||||
|
||||
/// Populate all parameters for current group, it is used at start and when changing groups
|
||||
fn populate_groups_at_start(
|
||||
nb_object: &NotebookObject,
|
||||
model: &TreeModel,
|
||||
shared_current_iter: Rc<RefCell<Option<TreeIter>>>,
|
||||
tree_iter: TreeIter,
|
||||
image_compare_left: >k::Image,
|
||||
image_compare_right: >k::Image,
|
||||
current_group: u32,
|
||||
group_number: u32,
|
||||
check_button_left_preview_text: >k::CheckButton,
|
||||
check_button_right_preview_text: >k::CheckButton,
|
||||
scrolled_window_compare_choose_images: >k::ScrolledWindow,
|
||||
label_group_info: >k::Label,
|
||||
shared_image_cache: Rc<RefCell<Vec<(String, String, gtk::Image, gtk::Image, gtk::TreePath)>>>,
|
||||
shared_using_for_preview: Rc<RefCell<(Option<TreePath>, Option<TreePath>)>>,
|
||||
) {
|
||||
let all_vec = get_all_path(model, &tree_iter, nb_object.column_color.unwrap(), nb_object.column_path, nb_object.column_name);
|
||||
*shared_current_iter.borrow_mut() = Some(tree_iter);
|
||||
|
||||
let cache_all_images = generate_cache_for_results(all_vec);
|
||||
|
||||
// This is safe, because cache have at least 2 results
|
||||
image_compare_left.set_from_pixbuf(cache_all_images[0].2.pixbuf().as_ref());
|
||||
image_compare_right.set_from_pixbuf(cache_all_images[1].2.pixbuf().as_ref());
|
||||
|
||||
*shared_using_for_preview.borrow_mut() = (Some(cache_all_images[0].4.clone()), Some(cache_all_images[1].4.clone()));
|
||||
|
||||
check_button_left_preview_text.set_label(&format!("1. {}", get_max_file_name(&cache_all_images[0].0, 70)));
|
||||
check_button_right_preview_text.set_label(&format!("2. {}", get_max_file_name(&cache_all_images[1].0, 70)));
|
||||
|
||||
label_group_info.set_text(
|
||||
fl!(
|
||||
"compare_groups_number",
|
||||
generate_translation_hashmap(vec![
|
||||
("current_group", current_group.to_string()),
|
||||
("all_groups", group_number.to_string()),
|
||||
("images_in_group", cache_all_images.len().to_string())
|
||||
])
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
populate_similar_scrolled_view(
|
||||
scrolled_window_compare_choose_images,
|
||||
&cache_all_images,
|
||||
image_compare_left,
|
||||
image_compare_right,
|
||||
shared_using_for_preview.clone(),
|
||||
shared_image_cache.clone(),
|
||||
check_button_left_preview_text,
|
||||
check_button_right_preview_text,
|
||||
model,
|
||||
nb_object.column_selection,
|
||||
);
|
||||
|
||||
*shared_image_cache.borrow_mut() = cache_all_images.clone();
|
||||
|
||||
let mut found = false;
|
||||
for i in scrolled_window_compare_choose_images.child().unwrap().downcast::<gtk::Viewport>().unwrap().children() {
|
||||
if i.widget_name() == "all_box" {
|
||||
let gtk_box = i.downcast::<gtk::Box>().unwrap();
|
||||
update_bottom_buttons(>k_box, shared_using_for_preview, shared_image_cache);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(found);
|
||||
|
||||
let is_active = model.value(&model.iter(&cache_all_images[0].4).unwrap(), nb_object.column_selection).get::<bool>().unwrap();
|
||||
check_button_left_preview_text.set_active(is_active);
|
||||
let is_active = model.value(&model.iter(&cache_all_images[1].4).unwrap(), nb_object.column_selection).get::<bool>().unwrap();
|
||||
check_button_right_preview_text.set_active(is_active);
|
||||
}
|
||||
|
||||
/// Generate images which will be used later as preview images without needing to open them again and again
|
||||
fn generate_cache_for_results(vector_with_path: Vec<(String, String, gtk::TreePath)>) -> Vec<(String, String, gtk::Image, gtk::Image, gtk::TreePath)> {
|
||||
// TODO use here threads,
|
||||
// For now threads cannot be used because Image and TreeIter cannot be used in threads
|
||||
let mut cache_all_images = Vec::new();
|
||||
for (full_path, name, tree_path) in vector_with_path {
|
||||
let name_lowercase = name.to_lowercase();
|
||||
let dynamic_image = if RAW_IMAGE_EXTENSIONS.iter().any(|f| name_lowercase.ends_with(f)) {
|
||||
match get_dynamic_image_from_raw_image(&full_path) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
println!("Failed to convert rawimage {}", full_path);
|
||||
DynamicImage::new_rgb8(1, 1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match image::open(&full_path) {
|
||||
Ok(t) => t,
|
||||
Err(_) => {
|
||||
println!("Failed to open image {}", full_path);
|
||||
DynamicImage::new_rgb8(1, 1)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let big_thumbnail = resize_dynamic_image_dimension(dynamic_image, (BIG_PREVIEW_SIZE, BIG_PREVIEW_SIZE), &FilterType::Triangle);
|
||||
let big_path = get_image_path_temporary("roman", 1, "jpg");
|
||||
let _ = big_thumbnail.save(&big_path);
|
||||
let big_img = gtk::Image::new();
|
||||
big_img.set_from_file(big_path);
|
||||
|
||||
let small_thumbnail = resize_dynamic_image_dimension(big_thumbnail, (SMALL_PREVIEW_SIZE, SMALL_PREVIEW_SIZE), &FilterType::Triangle);
|
||||
let small_path = get_image_path_temporary("roman", 1, "jpg");
|
||||
let _ = small_thumbnail.save(&small_path);
|
||||
let small_img = gtk::Image::new();
|
||||
small_img.set_from_file(small_path);
|
||||
|
||||
cache_all_images.push((full_path, name, big_img, small_img, tree_path));
|
||||
}
|
||||
cache_all_images
|
||||
}
|
||||
|
||||
/// Takes info about current items in groups like path
|
||||
fn get_all_path(model: &TreeModel, current_iter: &TreeIter, column_color: i32, column_path: i32, column_name: i32) -> Vec<(String, String, gtk::TreePath)> {
|
||||
let used_iter = current_iter.clone();
|
||||
|
||||
assert_eq!(model.value(&used_iter, column_color).get::<String>().unwrap(), HEADER_ROW_COLOR);
|
||||
let using_reference = !model.value(&used_iter, column_path).get::<String>().unwrap().is_empty();
|
||||
|
||||
let mut returned_vector = Vec::new();
|
||||
|
||||
if using_reference {
|
||||
let name = model.value(&used_iter, column_name).get::<String>().unwrap();
|
||||
let path = model.value(&used_iter, column_path).get::<String>().unwrap();
|
||||
|
||||
let full_name = get_full_name_from_path_name(&path, &name);
|
||||
|
||||
returned_vector.push((full_name, name, model.path(&used_iter).unwrap()));
|
||||
}
|
||||
|
||||
if !model.iter_next(&used_iter) {
|
||||
panic!("Found only header!");
|
||||
}
|
||||
|
||||
loop {
|
||||
let name = model.value(&used_iter, column_name).get::<String>().unwrap();
|
||||
let path = model.value(&used_iter, column_path).get::<String>().unwrap();
|
||||
|
||||
let full_name = get_full_name_from_path_name(&path, &name);
|
||||
|
||||
returned_vector.push((full_name, name, model.path(&used_iter).unwrap()));
|
||||
|
||||
if !model.iter_next(&used_iter) {
|
||||
break;
|
||||
}
|
||||
|
||||
let color = model.value(&used_iter, column_color).get::<String>().unwrap();
|
||||
|
||||
if color == HEADER_ROW_COLOR {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert!(returned_vector.len() > 1);
|
||||
|
||||
returned_vector
|
||||
}
|
||||
|
||||
/// Moves iterator to previous/next header
|
||||
fn move_iter(model: >k::TreeModel, tree_iter: &TreeIter, column_color: i32, go_next: bool) -> TreeIter {
|
||||
assert_eq!(model.value(tree_iter, column_color).get::<String>().unwrap(), HEADER_ROW_COLOR);
|
||||
|
||||
if go_next {
|
||||
if !model.iter_next(tree_iter) {
|
||||
panic!("Found only header!");
|
||||
}
|
||||
} else {
|
||||
if !model.iter_previous(tree_iter) {
|
||||
panic!("Found only header!");
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
if go_next {
|
||||
if !model.iter_next(tree_iter) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if !model.iter_previous(tree_iter) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let color = model.value(tree_iter, column_color).get::<String>().unwrap();
|
||||
|
||||
if color == HEADER_ROW_COLOR {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tree_iter.clone()
|
||||
}
|
||||
|
||||
/// Populate bottom Scrolled View with small thumbnails
|
||||
fn populate_similar_scrolled_view(
|
||||
scrolled_window: &ScrolledWindow,
|
||||
image_cache: &[(String, String, Image, Image, TreePath)],
|
||||
image_compare_left: &Image,
|
||||
image_compare_right: &Image,
|
||||
shared_using_for_preview: Rc<RefCell<(Option<TreePath>, Option<TreePath>)>>,
|
||||
shared_image_cache: Rc<RefCell<Vec<(String, String, gtk::Image, gtk::Image, gtk::TreePath)>>>,
|
||||
check_button_left_preview_text: &CheckButton,
|
||||
check_button_right_preview_text: &CheckButton,
|
||||
model: &TreeModel,
|
||||
column_selection: i32,
|
||||
) {
|
||||
if let Some(child) = scrolled_window.child() {
|
||||
scrolled_window.remove(&child);
|
||||
};
|
||||
scrolled_window.set_propagate_natural_height(true);
|
||||
|
||||
let all_gtk_box = gtk::Box::new(Orientation::Horizontal, 5);
|
||||
all_gtk_box.set_widget_name("all_box");
|
||||
|
||||
for (number, (path, _name, big_thumbnail, small_thumbnail, tree_path)) in image_cache.iter().enumerate() {
|
||||
let small_box = gtk::Box::new(Orientation::Vertical, 3);
|
||||
|
||||
let smaller_box = gtk::Box::new(Orientation::Horizontal, 2);
|
||||
|
||||
let button_left = gtk::Button::builder().label(&fl!("compare_move_left_button")).build();
|
||||
let label = gtk::Label::builder().label(&(number + 1).to_string()).build();
|
||||
let button_right = gtk::Button::builder().label(&fl!("compare_move_right_button")).build();
|
||||
|
||||
let image_compare_left = image_compare_left.clone();
|
||||
let image_compare_right = image_compare_right.clone();
|
||||
|
||||
let big_thumbnail_clone = big_thumbnail.clone();
|
||||
let tree_path_clone = tree_path.clone();
|
||||
let all_gtk_box_clone = all_gtk_box.clone();
|
||||
let shared_using_for_preview_clone = shared_using_for_preview.clone();
|
||||
let shared_image_cache_clone = shared_image_cache.clone();
|
||||
let check_button_left_preview_text_clone = check_button_left_preview_text.clone();
|
||||
let model_clone = model.clone();
|
||||
let path_clone = path.clone();
|
||||
|
||||
button_left.connect_clicked(move |_button_left| {
|
||||
shared_using_for_preview_clone.borrow_mut().0 = Some(tree_path_clone.clone());
|
||||
update_bottom_buttons(&all_gtk_box_clone, shared_using_for_preview_clone.clone(), shared_image_cache_clone.clone());
|
||||
image_compare_left.set_from_pixbuf(big_thumbnail_clone.pixbuf().as_ref());
|
||||
|
||||
let is_active = model_clone.value(&model_clone.iter(&tree_path_clone).unwrap(), column_selection).get::<bool>().unwrap();
|
||||
check_button_left_preview_text_clone.set_active(is_active);
|
||||
check_button_left_preview_text_clone.set_label(&format!("{}. {}", number + 1, get_max_file_name(&path_clone, 70)));
|
||||
});
|
||||
|
||||
let big_thumbnail_clone = big_thumbnail.clone();
|
||||
let tree_path_clone = tree_path.clone();
|
||||
let all_gtk_box_clone = all_gtk_box.clone();
|
||||
let shared_using_for_preview_clone = shared_using_for_preview.clone();
|
||||
let shared_image_cache_clone = shared_image_cache.clone();
|
||||
let check_button_right_preview_text_clone = check_button_right_preview_text.clone();
|
||||
let model_clone = model.clone();
|
||||
let path_clone = path.clone();
|
||||
|
||||
button_right.connect_clicked(move |_button_right| {
|
||||
shared_using_for_preview_clone.borrow_mut().1 = Some(tree_path_clone.clone());
|
||||
update_bottom_buttons(&all_gtk_box_clone, shared_using_for_preview_clone.clone(), shared_image_cache_clone.clone());
|
||||
image_compare_right.set_from_pixbuf(big_thumbnail_clone.pixbuf().as_ref());
|
||||
|
||||
let is_active = model_clone.value(&model_clone.iter(&tree_path_clone).unwrap(), column_selection).get::<bool>().unwrap();
|
||||
check_button_right_preview_text_clone.set_active(is_active);
|
||||
check_button_right_preview_text_clone.set_label(&format!("{}. {}", number + 1, get_max_file_name(&path_clone, 70)));
|
||||
});
|
||||
|
||||
smaller_box.add(&button_left);
|
||||
smaller_box.add(&label);
|
||||
smaller_box.add(&button_right);
|
||||
|
||||
small_box.add(&smaller_box);
|
||||
small_box.add(small_thumbnail);
|
||||
|
||||
all_gtk_box.add(&small_box);
|
||||
}
|
||||
|
||||
all_gtk_box.show_all();
|
||||
scrolled_window.add(&all_gtk_box);
|
||||
}
|
||||
|
||||
/// Disables/Enables L/R buttons at the bottom scrolled view
|
||||
fn update_bottom_buttons(
|
||||
all_gtk_box: >k::Box,
|
||||
shared_using_for_preview: Rc<RefCell<(Option<TreePath>, Option<TreePath>)>>,
|
||||
image_cache: Rc<RefCell<Vec<(String, String, Image, Image, TreePath)>>>,
|
||||
) {
|
||||
let left_tree_view = (*shared_using_for_preview.borrow()).0.clone().unwrap();
|
||||
let right_tree_view = (*shared_using_for_preview.borrow()).1.clone().unwrap();
|
||||
|
||||
for (number, i) in all_gtk_box.children().into_iter().enumerate() {
|
||||
let cache_tree_path = (*image_cache.borrow())[number].4.clone();
|
||||
let is_chosen = cache_tree_path != right_tree_view && cache_tree_path != left_tree_view;
|
||||
|
||||
let bx = i.downcast::<gtk::Box>().unwrap();
|
||||
let smaller_bx = bx.children()[0].clone().downcast::<gtk::Box>().unwrap();
|
||||
for items in smaller_bx.children() {
|
||||
if let Ok(btn) = items.downcast::<gtk::Button>() {
|
||||
btn.set_sensitive(is_chosen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_current_group_and_iter_from_selection(model: &TreeModel, selection: TreeSelection, column_color: i32) -> (u32, TreeIter) {
|
||||
let mut current_group = 1;
|
||||
let mut possible_group = 1;
|
||||
let mut header_clone: TreeIter;
|
||||
let mut possible_header: TreeIter;
|
||||
|
||||
let selected_records = selection.selected_rows().0;
|
||||
|
||||
let iter = model.iter_first().unwrap(); // Checking that treeview is not empty should be done before
|
||||
header_clone = iter.clone(); // if nothing selected, use first group
|
||||
possible_header = iter.clone(); // if nothing selected, use first group
|
||||
assert_eq!(model.value(&iter, column_color).get::<String>().unwrap(), HEADER_ROW_COLOR); // First element should be header
|
||||
|
||||
if !selected_records.is_empty() {
|
||||
let first_selected_record = selected_records[0].clone();
|
||||
loop {
|
||||
if !model.iter_next(&iter) {
|
||||
break;
|
||||
}
|
||||
|
||||
if model.value(&iter, column_color).get::<String>().unwrap() == HEADER_ROW_COLOR {
|
||||
possible_group += 1;
|
||||
possible_header = iter.clone();
|
||||
}
|
||||
|
||||
if model.path(&iter).unwrap() == first_selected_record {
|
||||
header_clone = possible_header.clone();
|
||||
current_group = possible_group;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(current_group, header_clone)
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
use czkawka_core::fl;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Builder, TreeIter};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GuiCompareImages {
|
||||
pub window_compare: gtk::Window,
|
||||
|
||||
pub label_group_info: gtk::Label,
|
||||
|
||||
pub button_go_previous_compare_group: gtk::Button,
|
||||
pub button_go_next_compare_group: gtk::Button,
|
||||
|
||||
pub check_button_left_preview_text: gtk::CheckButton,
|
||||
pub check_button_right_preview_text: gtk::CheckButton,
|
||||
|
||||
pub image_compare_left: gtk::Image,
|
||||
pub image_compare_right: gtk::Image,
|
||||
|
||||
pub scrolled_window_compare_choose_images: gtk::ScrolledWindow,
|
||||
|
||||
pub shared_numbers_of_groups: Rc<RefCell<u32>>,
|
||||
pub shared_current_of_groups: Rc<RefCell<u32>>,
|
||||
pub shared_current_iter: Rc<RefCell<Option<TreeIter>>>,
|
||||
pub shared_image_cache: Rc<RefCell<Vec<(String, String, gtk::Image, gtk::Image, gtk::TreePath)>>>,
|
||||
pub shared_using_for_preview: Rc<RefCell<(Option<gtk::TreePath>, Option<gtk::TreePath>)>>,
|
||||
}
|
||||
|
||||
impl GuiCompareImages {
|
||||
pub fn create_from_builder(window_main: >k::Window) -> Self {
|
||||
let glade_src = include_str!("../ui/compare_images.glade").to_string();
|
||||
let builder = Builder::from_string(glade_src.as_str());
|
||||
|
||||
let window_compare: gtk::Window = builder.object("window_compare").unwrap();
|
||||
window_compare.set_title(&fl!("window_compare_images"));
|
||||
window_compare.set_modal(true);
|
||||
window_compare.set_transient_for(Some(window_main));
|
||||
|
||||
let label_group_info: gtk::Label = builder.object("label_group_info").unwrap();
|
||||
|
||||
let button_go_previous_compare_group: gtk::Button = builder.object("button_go_previous_compare_group").unwrap();
|
||||
let button_go_next_compare_group: gtk::Button = builder.object("button_go_next_compare_group").unwrap();
|
||||
|
||||
let check_button_left_preview_text: gtk::CheckButton = builder.object("check_button_left_preview_text").unwrap();
|
||||
let check_button_right_preview_text: gtk::CheckButton = builder.object("check_button_right_preview_text").unwrap();
|
||||
|
||||
let image_compare_left: gtk::Image = builder.object("image_compare_left").unwrap();
|
||||
let image_compare_right: gtk::Image = builder.object("image_compare_right").unwrap();
|
||||
|
||||
let scrolled_window_compare_choose_images: gtk::ScrolledWindow = builder.object("scrolled_window_compare_choose_images").unwrap();
|
||||
|
||||
let shared_numbers_of_groups = Rc::new(RefCell::new(0));
|
||||
let shared_current_of_groups = Rc::new(RefCell::new(0));
|
||||
let shared_current_iter = Rc::new(RefCell::new(None));
|
||||
let shared_image_cache = Rc::new(RefCell::new(Vec::new()));
|
||||
let shared_using_for_preview = Rc::new(RefCell::new((None, None)));
|
||||
|
||||
Self {
|
||||
window_compare,
|
||||
label_group_info,
|
||||
button_go_previous_compare_group,
|
||||
button_go_next_compare_group,
|
||||
check_button_left_preview_text,
|
||||
check_button_right_preview_text,
|
||||
image_compare_left,
|
||||
image_compare_right,
|
||||
scrolled_window_compare_choose_images,
|
||||
shared_numbers_of_groups,
|
||||
shared_current_of_groups,
|
||||
shared_current_iter,
|
||||
shared_image_cache,
|
||||
shared_using_for_preview,
|
||||
}
|
||||
}
|
||||
pub fn update_language(&self) {
|
||||
self.window_compare.set_title(&fl!("window_compare_images"));
|
||||
}
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.39.0 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.24"/>
|
||||
<object class="GtkDialog" id="window_compare">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="type-hint">dialog</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="layout-style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_group_info">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="label" translatable="yes">Group XD/PER XD (99 images in current group)</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">-1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_go_next_compare_group">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">go-next</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_go_previous_compare_group">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">go-previous</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="check_button_left_preview_text">
|
||||
<property name="label" translatable="yes">First Game</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="check_button_right_preview_text">
|
||||
<property name="label" translatable="yes">Second Game</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="image_compare_left">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="stock">gtk-missing-image</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage" id="image_compare_right">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="stock">gtk-missing-image</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window_compare_choose_images">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
Loading…
Reference in New Issue