use gtk4::prelude::*; use gtk4::{ResponseType, TreeIter, Window}; use regex::Regex; use czkawka_core::common::Common; use crate::flg; use crate::gui_structs::gui_data::GuiData; use crate::help_functions::*; use crate::notebook_info::NOTEBOOKS_INFO; // File length variable allows users to choose duplicates which have shorter file name // e.g. 'tar.gz' will be selected instead 'tar.gz (copy)' etc. fn popover_select_all(popover: >k4::Popover, tree_view: >k4::TreeView, column_button_selection: u32, column_header: Option) { let model = get_list_store(tree_view); if let Some(iter) = model.iter_first() { if let Some(column_header) = column_header { loop { if !model.get::(&iter, column_header) { model.set_value(&iter, column_button_selection, &true.to_value()); } if !model.iter_next(&iter) { break; } } } else { loop { model.set_value(&iter, column_button_selection, &true.to_value()); if !model.iter_next(&iter) { break; } } } } popover.popdown(); } fn popover_unselect_all(popover: >k4::Popover, tree_view: >k4::TreeView, column_button_selection: u32) { let model = get_list_store(tree_view); if let Some(iter) = model.iter_first() { loop { model.set_value(&iter, column_button_selection, &false.to_value()); if !model.iter_next(&iter) { break; } } } popover.popdown(); } fn popover_reverse(popover: >k4::Popover, tree_view: >k4::TreeView, column_button_selection: u32, column_header: Option) { let model = get_list_store(tree_view); if let Some(iter) = model.iter_first() { if let Some(column_header) = column_header { loop { if !model.get::(&iter, column_header) { let current_value: bool = model.get::(&iter, column_button_selection as i32); model.set_value(&iter, column_button_selection, &(!current_value).to_value()); } if !model.iter_next(&iter) { break; } } } else { loop { let current_value: bool = model.get::(&iter, column_button_selection as i32); model.set_value(&iter, column_button_selection, &(!current_value).to_value()); if !model.iter_next(&iter) { break; } } } } popover.popdown(); } fn popover_all_except_oldest_newest( popover: >k4::Popover, tree_view: >k4::TreeView, column_header: i32, column_modification_as_secs: i32, column_file_name: i32, column_button_selection: u32, except_oldest: bool, ) { let model = get_list_store(tree_view); if let Some(iter) = model.iter_first() { let mut end: bool = false; loop { let mut tree_iter_array: Vec = Vec::new(); let mut used_index: Option = None; let mut current_index: usize = 0; let mut modification_time_min_max: u64 = if except_oldest { u64::MAX } else { 0 }; let mut file_length: usize = 0; loop { if model.get::(&iter, column_header) { if !model.iter_next(&iter) { end = true; } break; } tree_iter_array.push(iter); let modification = model.get::(&iter, column_modification_as_secs); let current_file_length = model.get::(&iter, column_file_name).len(); if except_oldest { if modification < modification_time_min_max || (modification == modification_time_min_max && current_file_length < file_length) { file_length = current_file_length; modification_time_min_max = modification; used_index = Some(current_index); } } else { if modification > modification_time_min_max || (modification == modification_time_min_max && current_file_length < file_length) { file_length = current_file_length; modification_time_min_max = modification; used_index = Some(current_index); } } current_index += 1; if !model.iter_next(&iter) { end = true; break; } } if used_index.is_none() { continue; } for (index, tree_iter) in tree_iter_array.iter().enumerate() { if index != used_index.unwrap() { model.set_value(tree_iter, column_button_selection, &true.to_value()); } else { model.set_value(tree_iter, column_button_selection, &false.to_value()); } } if end { break; } } } popover.popdown(); } fn popover_one_oldest_newest( popover: >k4::Popover, tree_view: >k4::TreeView, column_header: i32, column_modification_as_secs: i32, column_file_name: i32, column_button_selection: u32, check_oldest: bool, ) { let model = get_list_store(tree_view); if let Some(iter) = model.iter_first() { let mut end: bool = false; loop { let mut tree_iter_array: Vec = Vec::new(); let mut used_index: Option = None; let mut current_index: usize = 0; let mut modification_time_min_max: u64 = if check_oldest { u64::MAX } else { 0 }; let mut file_length: usize = 0; loop { if model.get::(&iter, column_header) { if !model.iter_next(&iter) { end = true; } break; } tree_iter_array.push(iter); let modification = model.get::(&iter, column_modification_as_secs); let current_file_length = model.get::(&iter, column_file_name).len(); if check_oldest { if modification < modification_time_min_max || (modification == modification_time_min_max && current_file_length > file_length) { file_length = current_file_length; modification_time_min_max = modification; used_index = Some(current_index); } } else { if modification > modification_time_min_max || (modification == modification_time_min_max && current_file_length > file_length) { file_length = current_file_length; modification_time_min_max = modification; used_index = Some(current_index); } } current_index += 1; if !model.iter_next(&iter) { end = true; break; } } if used_index.is_none() { continue; } for (index, tree_iter) in tree_iter_array.iter().enumerate() { if index == used_index.unwrap() { model.set_value(tree_iter, column_button_selection, &true.to_value()); } else { model.set_value(tree_iter, column_button_selection, &false.to_value()); } } if end { break; } } } popover.popdown(); } fn popover_custom_select_unselect( popover: >k4::Popover, window_main: &Window, tree_view: >k4::TreeView, column_header: Option, column_file_name: i32, column_path: i32, column_button_selection: u32, select_things: bool, ) { popover.popdown(); let window_title = if select_things { flg!("popover_custom_mode_select") } else { flg!("popover_custom_mode_unselect") }; // Dialog for select/unselect items { let dialog = gtk4::Dialog::builder().title(&window_title).transient_for(window_main).modal(true).build(); dialog.add_button(&flg!("general_ok_button"), ResponseType::Ok); dialog.add_button(&flg!("general_close_button"), ResponseType::Cancel); let check_button_path = gtk4::CheckButton::builder().label(&flg!("popover_custom_regex_path_label")).build(); let check_button_name = gtk4::CheckButton::builder().label(&flg!("popover_custom_regex_name_label")).build(); let check_button_rust_regex = gtk4::CheckButton::builder().label(&flg!("popover_custom_regex_regex_label")).build(); let check_button_case_sensitive = gtk4::CheckButton::builder().label(&flg!("popover_custom_case_sensitive_check_button")).build(); check_button_case_sensitive.set_active(false); let check_button_select_not_all_results = gtk4::CheckButton::builder().label(&flg!("popover_custom_all_in_group_label")).build(); check_button_select_not_all_results.set_active(true); let entry_path = gtk4::Entry::new(); let entry_name = gtk4::Entry::new(); let entry_rust_regex = gtk4::Entry::new(); entry_rust_regex.set_sensitive(false); // By default check button regex is disabled let label_regex_valid = gtk4::Label::new(None); // Tooltips { check_button_path.set_tooltip_text(Some(&flg!("popover_custom_path_check_button_entry_tooltip"))); entry_path.set_tooltip_text(Some(&flg!("popover_custom_path_check_button_entry_tooltip"))); check_button_name.set_tooltip_text(Some(&flg!("popover_custom_name_check_button_entry_tooltip"))); entry_name.set_tooltip_text(Some(&flg!("popover_custom_name_check_button_entry_tooltip"))); check_button_rust_regex.set_tooltip_text(Some(&flg!("popover_custom_regex_check_button_entry_tooltip"))); entry_rust_regex.set_tooltip_text(Some(&flg!("popover_custom_regex_check_button_entry_tooltip"))); check_button_case_sensitive.set_tooltip_text(Some(&flg!("popover_custom_case_sensitive_check_button_tooltip"))); check_button_select_not_all_results.set_tooltip_text(Some(&flg!("popover_custom_not_all_check_button_tooltip"))); } { let label_regex_valid = label_regex_valid.clone(); entry_rust_regex.connect_changed(move |entry_rust_regex| { let message; let text_to_check = entry_rust_regex.text().to_string(); if text_to_check.is_empty() { message = String::new(); } else { match Regex::new(&text_to_check) { Ok(_) => message = flg!("popover_valid_regex"), Err(_) => message = flg!("popover_invalid_regex"), } } // TODO add red and green color to text // let attributes_list = AttrList::new(); // let p_a = PangoAttribute::init(); // let attribute = PangoAttrFontDesc { attr }; // attributes_list.insert(attribute); // label_regex_valid.set_attributes(Some(&attributes_list)); label_regex_valid.set_text(&message); }); } // Disable other modes when Rust Regex is enabled { let check_button_path = check_button_path.clone(); let check_button_name = check_button_name.clone(); let check_button_rust_regex = check_button_rust_regex.clone(); let entry_path = entry_path.clone(); let entry_name = entry_name.clone(); let entry_rust_regex = entry_rust_regex.clone(); check_button_rust_regex.connect_toggled(move |check_button_rust_regex| { if check_button_rust_regex.is_active() { check_button_path.set_sensitive(false); check_button_name.set_sensitive(false); entry_path.set_sensitive(false); entry_name.set_sensitive(false); entry_rust_regex.set_sensitive(true); } else { check_button_path.set_sensitive(true); check_button_name.set_sensitive(true); entry_path.set_sensitive(true); entry_name.set_sensitive(true); entry_rust_regex.set_sensitive(false); } }); } // Configure look of things { // TODO Label should have const width, and rest should fill entry, but for now is 50%-50% let grid = gtk4::Grid::new(); grid.set_row_homogeneous(true); grid.set_column_homogeneous(true); grid.attach(&check_button_name, 0, 1, 1, 1); grid.attach(&check_button_path, 0, 2, 1, 1); grid.attach(&check_button_rust_regex, 0, 3, 1, 1); grid.attach(&entry_name, 1, 1, 1, 1); grid.attach(&entry_path, 1, 2, 1, 1); grid.attach(&entry_rust_regex, 1, 3, 1, 1); grid.attach(&label_regex_valid, 0, 4, 2, 1); grid.attach(&check_button_case_sensitive, 0, 5, 2, 1); if select_things { grid.attach(&check_button_select_not_all_results, 0, 6, 2, 1); } let box_widget = get_dialog_box_child(&dialog); box_widget.append(&grid); dialog.show(); } let tree_view = tree_view.clone(); dialog.connect_response(move |confirmation_dialog_select_unselect, response_type| { let name_wildcard = entry_name.text().trim().to_string(); let path_wildcard = entry_path.text().trim().to_string(); let regex_wildcard = entry_rust_regex.text().trim().to_string(); #[cfg(target_family = "windows")] let name_wildcard = name_wildcard.replace("/", "\\"); #[cfg(target_family = "windows")] let path_wildcard = path_wildcard.replace("/", "\\"); if response_type == ResponseType::Ok { let check_path = check_button_path.is_active(); let check_name = check_button_name.is_active(); let check_regex = check_button_rust_regex.is_active(); let case_sensitive = check_button_case_sensitive.is_active(); let check_all_selected = check_button_select_not_all_results.is_active(); if check_button_path.is_active() || check_button_name.is_active() || check_button_rust_regex.is_active() { let compiled_regex = if check_regex { if let Ok(t) = Regex::new(®ex_wildcard) { t } else { eprintln!("What? Regex should compile properly."); confirmation_dialog_select_unselect.close(); return; } } else { Regex::new("").unwrap() }; let model = get_list_store(&tree_view); let iter = if let Some(t) = model.iter_first() { t } else { confirmation_dialog_select_unselect.close(); return; }; let mut number_of_all_things = 0; let mut number_of_already_selected_things = 0; let mut vec_of_iters: Vec = Vec::new(); loop { if let Some(column_header) = column_header { if model.get::(&iter, column_header) { if select_things { if check_all_selected && (number_of_all_things - number_of_already_selected_things == vec_of_iters.len()) { vec_of_iters.pop(); } for iter in vec_of_iters { model.set_value(&iter, column_button_selection, &true.to_value()); } } else { for iter in vec_of_iters { model.set_value(&iter, column_button_selection, &false.to_value()); } } if !model.iter_next(&iter) { break; } number_of_all_things = 0; number_of_already_selected_things = 0; vec_of_iters = Vec::new(); continue; } } let is_selected = model.get::(&iter, column_button_selection as i32); let path = model.get::(&iter, column_path); let name = model.get::(&iter, column_file_name); let path_and_name = get_full_name_from_path_name(&path, &name); let mut need_to_change_thing: bool = false; number_of_all_things += 1; if check_regex && compiled_regex.find(&path_and_name).is_some() { need_to_change_thing = true; } else { if check_name { if case_sensitive { if Common::regex_check(&name_wildcard, &name) { need_to_change_thing = true; } } else { if Common::regex_check(&name_wildcard.to_lowercase(), name.to_lowercase()) { need_to_change_thing = true; } } } if check_path { if case_sensitive { if Common::regex_check(&path_wildcard, &path) { need_to_change_thing = true; } } else { if Common::regex_check(&path_wildcard.to_lowercase(), path.to_lowercase()) { need_to_change_thing = true; } } } } if select_things { if is_selected { number_of_already_selected_things += 1; } else { if need_to_change_thing { vec_of_iters.push(iter); } } } else { if need_to_change_thing { vec_of_iters.push(iter); } } if !model.iter_next(&iter) { if select_things { if check_all_selected && (number_of_all_things - number_of_already_selected_things == vec_of_iters.len()) { vec_of_iters.pop(); } for iter in vec_of_iters { model.set_value(&iter, column_button_selection, &true.to_value()); } } else { for iter in vec_of_iters { model.set_value(&iter, column_button_selection, &false.to_value()); } } break; } } } } confirmation_dialog_select_unselect.close(); }); } } fn popover_all_except_biggest_smallest( popover: >k4::Popover, tree_view: >k4::TreeView, column_header: i32, column_size_as_bytes: i32, column_dimensions: Option, column_button_selection: u32, except_biggest: bool, ) { let model = get_list_store(tree_view); if let Some(iter) = model.iter_first() { let mut end: bool = false; loop { let mut tree_iter_array: Vec = Vec::new(); let mut used_index: Option = None; let mut current_index: usize = 0; let mut size_as_bytes_min_max: u64 = if except_biggest { 0 } else { u64::MAX }; let mut number_of_pixels_min_max: u64 = if except_biggest { 0 } else { u64::MAX }; loop { if model.get::(&iter, column_header) { if !model.iter_next(&iter) { end = true; } break; } tree_iter_array.push(iter); let size_as_bytes = model.get::(&iter, column_size_as_bytes); // If dimension exists, then needs to be checked images if let Some(column_dimensions) = column_dimensions { let dimensions_string = model.get::(&iter, column_dimensions); let dimensions = change_dimension_to_krotka(&dimensions_string); let number_of_pixels = dimensions.0 * dimensions.1; if except_biggest { if number_of_pixels > number_of_pixels_min_max || (number_of_pixels == number_of_pixels_min_max && size_as_bytes > size_as_bytes_min_max) { number_of_pixels_min_max = number_of_pixels; size_as_bytes_min_max = size_as_bytes; used_index = Some(current_index); } } else { if number_of_pixels < number_of_pixels_min_max || (number_of_pixels == number_of_pixels_min_max && size_as_bytes < size_as_bytes_min_max) { number_of_pixels_min_max = number_of_pixels; size_as_bytes_min_max = size_as_bytes; used_index = Some(current_index); } } } else { if except_biggest { if size_as_bytes > size_as_bytes_min_max { size_as_bytes_min_max = size_as_bytes; used_index = Some(current_index); } } else { if size_as_bytes < size_as_bytes_min_max { size_as_bytes_min_max = size_as_bytes; used_index = Some(current_index); } } } current_index += 1; if !model.iter_next(&iter) { end = true; break; } } if used_index.is_none() { continue; } for (index, tree_iter) in tree_iter_array.iter().enumerate() { if index != used_index.unwrap() { model.set_value(tree_iter, column_button_selection, &true.to_value()); } else { model.set_value(tree_iter, column_button_selection, &false.to_value()); } } if end { break; } } } popover.popdown(); } pub fn connect_popovers(gui_data: &GuiData) { let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_select_all = gui_data.popovers.buttons_popover_select_all.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); buttons_popover_select_all.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_INFO[nb_number as usize]; popover_select_all(&popover_select, tree_view, nb_object.column_selection as u32, nb_object.column_header); }); let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_unselect_all = gui_data.popovers.buttons_popover_unselect_all.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); buttons_popover_unselect_all.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_INFO[nb_number as usize]; popover_unselect_all(&popover_select, tree_view, nb_object.column_selection as u32); }); let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_reverse = gui_data.popovers.buttons_popover_reverse.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); buttons_popover_reverse.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_INFO[nb_number as usize]; popover_reverse(&popover_select, tree_view, nb_object.column_selection as u32, nb_object.column_header); }); let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_select_all_except_oldest = gui_data.popovers.buttons_popover_select_all_except_oldest.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); buttons_popover_select_all_except_oldest.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_INFO[nb_number as usize]; popover_all_except_oldest_newest( &popover_select, tree_view, nb_object.column_header.expect("AEO can't be used without headers"), nb_object.column_modification_as_secs.expect("AEO needs modification as secs column"), nb_object.column_name, nb_object.column_selection as u32, true, ); }); let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_select_all_except_newest = gui_data.popovers.buttons_popover_select_all_except_newest.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); buttons_popover_select_all_except_newest.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_INFO[nb_number as usize]; popover_all_except_oldest_newest( &popover_select, tree_view, nb_object.column_header.expect("AEN can't be used without headers"), nb_object.column_modification_as_secs.expect("AEN needs modification as secs column"), nb_object.column_name, nb_object.column_selection as u32, false, ); }); let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_select_one_oldest = gui_data.popovers.buttons_popover_select_one_oldest.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); buttons_popover_select_one_oldest.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_INFO[nb_number as usize]; popover_one_oldest_newest( &popover_select, tree_view, nb_object.column_header.expect("OO can't be used without headers"), nb_object.column_modification_as_secs.expect("OO needs modification as secs column"), nb_object.column_name, nb_object.column_selection as u32, true, ); }); let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_select_one_newest = gui_data.popovers.buttons_popover_select_one_newest.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); buttons_popover_select_one_newest.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_INFO[nb_number as usize]; popover_one_oldest_newest( &popover_select, tree_view, nb_object.column_header.expect("ON can't be used without headers"), nb_object.column_modification_as_secs.expect("ON needs modification as secs column"), nb_object.column_name, nb_object.column_selection as u32, false, ); }); let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_select_custom = gui_data.popovers.buttons_popover_select_custom.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); let window_main = gui_data.window_main.clone(); buttons_popover_select_custom.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_INFO[nb_number as usize]; popover_custom_select_unselect( &popover_select, &window_main, tree_view, nb_object.column_header, nb_object.column_name, nb_object.column_path, nb_object.column_selection as u32, true, ); }); let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_unselect_custom = gui_data.popovers.buttons_popover_unselect_custom.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); let window_main = gui_data.window_main.clone(); buttons_popover_unselect_custom.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_INFO[nb_number as usize]; popover_custom_select_unselect( &popover_select, &window_main, tree_view, nb_object.column_header, nb_object.column_name, nb_object.column_path, nb_object.column_selection as u32, false, ); }); let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_select_all_images_except_biggest = gui_data.popovers.buttons_popover_select_all_images_except_biggest.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); buttons_popover_select_all_images_except_biggest.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_INFO[nb_number as usize]; popover_all_except_biggest_smallest( &popover_select, tree_view, nb_object.column_header.expect("AEB can't be used without headers"), nb_object.column_size_as_bytes.expect("AEB needs size as bytes column"), nb_object.column_dimensions, nb_object.column_selection as u32, true, ); }); let popover_select = gui_data.popovers.popover_select.clone(); let buttons_popover_select_all_images_except_smallest = gui_data.popovers.buttons_popover_select_all_images_except_smallest.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let main_tree_views = gui_data.main_notebook.get_main_tree_views(); buttons_popover_select_all_images_except_smallest.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_INFO[nb_number as usize]; popover_all_except_biggest_smallest( &popover_select, tree_view, nb_object.column_header.expect("AES can't be used without headers"), nb_object.column_size_as_bytes.expect("AES needs size as bytes column"), nb_object.column_dimensions, nb_object.column_selection as u32, false, ); }); }