Show how many files will be deleted/moved (#514)

* Print proper character(\) in Windows when printing files

* Show how many files will be moved/deleted.
Don't show dialog when 0 files is selected
This commit is contained in:
Rafał Mikrut 2021-12-21 18:23:17 +01:00 committed by GitHub
parent 71c2e01ce4
commit 878550446d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 151 additions and 46 deletions

View File

@ -1,10 +1,12 @@
## Version 4.0.0 - ? ## Version 4.0.0 - ?
- Multithread support for collecting files to check(2/3x on 4 thread processor) - [#502](https://github.com/qarmin/czkawka/pull/502), [#504](https://github.com/qarmin/czkawka/pull/504)
- Add Polish and Italian translation - [#469](https://github.com/qarmin/czkawka/pull/469), [#508](https://github.com/qarmin/czkawka/pull/508)
- Add support for finding similar videos - [#460](https://github.com/qarmin/czkawka/pull/460) - Add support for finding similar videos - [#460](https://github.com/qarmin/czkawka/pull/460)
- GUI code refactoring(could fix some bugs) - [#462](https://github.com/qarmin/czkawka/pull/462) - GUI code refactoring(could fix some bugs) - [#462](https://github.com/qarmin/czkawka/pull/462)
- Fixed crash when trying to hard/symlink 0 files - [#462](https://github.com/qarmin/czkawka/pull/462) - Fixed crash when trying to hard/symlink 0 files - [#462](https://github.com/qarmin/czkawka/pull/462)
- GTK 4 compatibility improvements for future change of toolkit - [#467](https://github.com/qarmin/czkawka/pull/467), [#468](https://github.com/qarmin/czkawka/pull/468), [#473](https://github.com/qarmin/czkawka/pull/473), [#474](https://github.com/qarmin/czkawka/pull/474) - GTK 4 compatibility improvements for future change of toolkit - [#467](https://github.com/qarmin/czkawka/pull/467), [#468](https://github.com/qarmin/czkawka/pull/468), [#473](https://github.com/qarmin/czkawka/pull/473), [#474](https://github.com/qarmin/czkawka/pull/474), [#503](https://github.com/qarmin/czkawka/pull/503), [#505](https://github.com/qarmin/czkawka/pull/505)
- Change minimal supported OS to Ubuntu 20.04(needed by GTK) - [#468](https://github.com/qarmin/czkawka/pull/468) - Change minimal supported OS to Ubuntu 20.04(needed by GTK) - [#468](https://github.com/qarmin/czkawka/pull/468)
- Increased performance when using image previews - [#468](https://github.com/qarmin/czkawka/pull/468) - Increased performance by avoiding creating unnecessary image previews - [#468](https://github.com/qarmin/czkawka/pull/468)
- Improved performance due caching hash of broken/not supported images/videos = [#471](https://github.com/qarmin/czkawka/pull/471) - Improved performance due caching hash of broken/not supported images/videos = [#471](https://github.com/qarmin/czkawka/pull/471)
- Option to not remove cache from non existent files(e.g. from unplugged pendrive) - [#472](https://github.com/qarmin/czkawka/pull/472) - 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) - Add multiple tooltips with helpful messages - [#472](https://github.com/qarmin/czkawka/pull/472)
@ -13,6 +15,9 @@
- Remove support for finding zeroed files - [#461](https://github.com/qarmin/czkawka/pull/461) - 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) - Remove HashMB mode - [#476](https://github.com/qarmin/czkawka/pull/476)
- Approximate comparison of music - [#483](https://github.com/qarmin/czkawka/pull/483) - Approximate comparison of music - [#483](https://github.com/qarmin/czkawka/pull/483)
- Enable column sorting for simple treeview - [#487](https://github.com/qarmin/czkawka/pull/487)
- Allow to hide upper panel - [#491](https://github.com/qarmin/czkawka/pull/491)
- Make UI take less space - [#500](https://github.com/qarmin/czkawka/pull/500)
## Version 3.3.1 - 22.11.2021r ## Version 3.3.1 - 22.11.2021r
- Fix crash when moving buttons [#457](https://github.com/qarmin/czkawka/pull/457) - Fix crash when moving buttons [#457](https://github.com/qarmin/czkawka/pull/457)

View File

@ -41,31 +41,31 @@ pub async fn delete_things(gui_data: GuiData) {
let preview_path = gui_data.preview_path.clone(); let preview_path = gui_data.preview_path.clone();
let text_view_errors = gui_data.text_view_errors.clone(); let text_view_errors = gui_data.text_view_errors.clone();
if !check_if_can_delete_files(&check_button_settings_confirm_deletion, &window_main).await {
return;
}
let nb_number = notebook_main.current_page().unwrap(); let nb_number = notebook_main.current_page().unwrap();
let tree_view = &main_tree_views[nb_number as usize]; let tree_view = &main_tree_views[nb_number as usize];
let nb_object = &NOTEBOOKS_INFOS[nb_number as usize]; let nb_object = &NOTEBOOKS_INFOS[nb_number as usize];
let (number_of_selected_items, number_of_selected_groups) = check_how_much_elements_is_selected(tree_view, nb_object.column_color, nb_object.column_selection);
// Nothing is selected
if number_of_selected_items == 0 {
return;
}
if !check_if_can_delete_files(&check_button_settings_confirm_deletion, &window_main, number_of_selected_items, number_of_selected_groups).await {
return;
}
if let Some(column_color) = nb_object.column_color { if let Some(column_color) = nb_object.column_color {
if !check_button_settings_confirm_group_deletion.is_active() || !check_if_deleting_all_files_in_group(tree_view, column_color, nb_object.column_selection, &window_main, &check_button_settings_confirm_group_deletion).await { if !check_button_settings_confirm_group_deletion.is_active() || !check_if_deleting_all_files_in_group(tree_view, column_color, nb_object.column_selection, &window_main, &check_button_settings_confirm_group_deletion).await {
tree_remove( tree_remove(tree_view, nb_object.column_name, nb_object.column_path, column_color, nb_object.column_selection, &check_button_settings_use_trash, &text_view_errors);
&tree_view.clone(),
nb_object.column_name,
nb_object.column_path,
column_color,
nb_object.column_selection,
&check_button_settings_use_trash,
&text_view_errors,
);
} }
} else { } else {
if nb_number == NotebookMainEnum::EmptyDirectories as u32 { if nb_number == NotebookMainEnum::EmptyDirectories as u32 {
empty_folder_remover(&tree_view.clone(), nb_object.column_name, nb_object.column_path, nb_object.column_selection, &check_button_settings_use_trash, &text_view_errors); empty_folder_remover(tree_view, nb_object.column_name, nb_object.column_path, nb_object.column_selection, &check_button_settings_use_trash, &text_view_errors);
} else { } else {
basic_remove(&tree_view.clone(), nb_object.column_name, nb_object.column_path, nb_object.column_selection, &check_button_settings_use_trash, &text_view_errors); basic_remove(tree_view, nb_object.column_name, nb_object.column_path, nb_object.column_selection, &check_button_settings_use_trash, &text_view_errors);
} }
} }
@ -82,9 +82,9 @@ pub async fn delete_things(gui_data: GuiData) {
} }
} }
pub async fn check_if_can_delete_files(check_button_settings_confirm_deletion: &gtk::CheckButton, window_main: &gtk::Window) -> bool { pub async fn check_if_can_delete_files(check_button_settings_confirm_deletion: &gtk::CheckButton, window_main: &gtk::Window, number_of_selected_items: u64, number_of_selected_groups: u64) -> bool {
if check_button_settings_confirm_deletion.is_active() { if check_button_settings_confirm_deletion.is_active() {
let (confirmation_dialog_delete, check_button) = create_dialog_ask_for_deletion(window_main); let (confirmation_dialog_delete, check_button) = create_dialog_ask_for_deletion(window_main, number_of_selected_items, number_of_selected_groups);
let response_type = confirmation_dialog_delete.run_future().await; let response_type = confirmation_dialog_delete.run_future().await;
if response_type == gtk::ResponseType::Ok { if response_type == gtk::ResponseType::Ok {
@ -102,12 +102,19 @@ pub async fn check_if_can_delete_files(check_button_settings_confirm_deletion: &
true true
} }
fn create_dialog_ask_for_deletion(window_main: &gtk::Window) -> (Dialog, CheckButton) { fn create_dialog_ask_for_deletion(window_main: &gtk::Window, number_of_selected_items: u64, number_of_selected_groups: u64) -> (Dialog, CheckButton) {
let dialog = gtk::Dialog::builder().title(&fl!("delete_title_dialog")).transient_for(window_main).modal(true).build(); let dialog = gtk::Dialog::builder().title(&fl!("delete_title_dialog")).transient_for(window_main).modal(true).build();
let button_ok = dialog.add_button(&fl!("general_ok_button"), ResponseType::Ok); let button_ok = dialog.add_button(&fl!("general_ok_button"), ResponseType::Ok);
dialog.add_button(&fl!("general_close_button"), ResponseType::Cancel); dialog.add_button(&fl!("general_close_button"), ResponseType::Cancel);
let label: gtk::Label = gtk::Label::new(Some(&fl!("delete_question_label"))); let label: gtk::Label = gtk::Label::new(Some(&fl!("delete_question_label")));
let label2: gtk::Label = match number_of_selected_groups {
0 => gtk::Label::new(Some(&fl!("delete_items_label", generate_translation_hashmap(vec![("items", number_of_selected_items.to_string())])))),
_ => gtk::Label::new(Some(&fl!(
"delete_items_groups_label",
generate_translation_hashmap(vec![("items", number_of_selected_items.to_string()), ("groups", number_of_selected_groups.to_string())])
))),
};
let check_button: gtk::CheckButton = gtk::CheckButton::with_label(&fl!("dialogs_ask_next_time")); let check_button: gtk::CheckButton = gtk::CheckButton::with_label(&fl!("dialogs_ask_next_time"));
check_button.set_active(true); check_button.set_active(true);
check_button.set_halign(Align::Center); check_button.set_halign(Align::Center);
@ -116,7 +123,10 @@ fn create_dialog_ask_for_deletion(window_main: &gtk::Window) -> (Dialog, CheckBu
let internal_box = get_dialog_box_child(&dialog); let internal_box = get_dialog_box_child(&dialog);
internal_box.add(&label); internal_box.add(&label);
internal_box.add(&label2);
internal_box.add(&check_button); internal_box.add(&check_button);
internal_box.set_margin(5);
check_button.set_margin_top(5);
dialog.show_all(); dialog.show_all();
(dialog, check_button) (dialog, check_button)
@ -227,7 +237,7 @@ pub fn empty_folder_remover(tree_view: &gtk::TreeView, column_file_name: i32, co
// We must check if folder is really empty or contains only other empty folders // We must check if folder is really empty or contains only other empty folders
let mut error_happened = false; let mut error_happened = false;
let mut folders_to_check: Vec<String> = vec![format!("{}/{}", path, name)]; let mut folders_to_check: Vec<String> = vec![get_full_name_from_path_name(&path, &name)];
let mut current_folder: String; let mut current_folder: String;
let mut next_folder: String; let mut next_folder: String;
'dir: while !folders_to_check.is_empty() { 'dir: while !folders_to_check.is_empty() {
@ -275,14 +285,14 @@ pub fn empty_folder_remover(tree_view: &gtk::TreeView, column_file_name: i32, co
if !error_happened { if !error_happened {
if !use_trash { if !use_trash {
match fs::remove_dir_all(format!("{}/{}", path, name)) { match fs::remove_dir_all(get_full_name_from_path_name(&path, &name)) {
Ok(_) => { Ok(_) => {
model.remove(&iter); model.remove(&iter);
} }
Err(_inspected) => error_happened = true, Err(_inspected) => error_happened = true,
} }
} else { } else {
match trash::delete(format!("{}/{}", path, name)) { match trash::delete(get_full_name_from_path_name(&path, &name)) {
Ok(_) => { Ok(_) => {
model.remove(&iter); model.remove(&iter);
} }
@ -291,7 +301,7 @@ pub fn empty_folder_remover(tree_view: &gtk::TreeView, column_file_name: i32, co
} }
} }
if error_happened { if error_happened {
messages += &fl!("delete_folder_failed", generate_translation_hashmap(vec![("dir", format!("{}/{}", path, name))])); messages += &fl!("delete_folder_failed", generate_translation_hashmap(vec![("dir", get_full_name_from_path_name(&path, &name))]));
messages += "\n"; messages += "\n";
} }
} }
@ -332,23 +342,23 @@ pub fn basic_remove(tree_view: &gtk::TreeView, column_file_name: i32, column_pat
let path = model.value(&iter, column_path).get::<String>().unwrap(); let path = model.value(&iter, column_path).get::<String>().unwrap();
if !use_trash { if !use_trash {
match fs::remove_file(format!("{}/{}", path, name)) { match fs::remove_file(get_full_name_from_path_name(&path, &name)) {
Ok(_) => { Ok(_) => {
model.remove(&iter); model.remove(&iter);
} }
Err(e) => { Err(e) => {
messages += fl!("delete_file_failed", generate_translation_hashmap(vec![("name", format!("{}/{}", path, name)), ("reason", e.to_string())])).as_str(); messages += fl!("delete_file_failed", generate_translation_hashmap(vec![("name", get_full_name_from_path_name(&path, &name)), ("reason", e.to_string())])).as_str();
messages += "\n"; messages += "\n";
} }
} }
} else { } else {
match trash::delete(format!("{}/{}", path, name)) { match trash::delete(get_full_name_from_path_name(&path, &name)) {
Ok(_) => { Ok(_) => {
model.remove(&iter); model.remove(&iter);
} }
Err(e) => { Err(e) => {
messages += fl!("delete_file_failed", generate_translation_hashmap(vec![("name", format!("{}/{}", path, name)), ("reason", e.to_string())])).as_str(); messages += fl!("delete_file_failed", generate_translation_hashmap(vec![("name", get_full_name_from_path_name(&path, &name)), ("reason", e.to_string())])).as_str();
messages += "\n"; messages += "\n";
} }
} }
@ -410,12 +420,12 @@ pub fn tree_remove(tree_view: &gtk::TreeView, column_file_name: i32, column_path
vec_file_name.dedup(); vec_file_name.dedup();
for file_name in vec_file_name { for file_name in vec_file_name {
if !use_trash { if !use_trash {
if let Err(e) = fs::remove_file(format!("{}/{}", path.clone(), file_name.clone())) { if let Err(e) = fs::remove_file(get_full_name_from_path_name(&path, &file_name)) {
messages += fl!("delete_file_failed", generate_translation_hashmap(vec![("name", format!("{}/{}", path, file_name)), ("reason", e.to_string())])).as_str(); messages += fl!("delete_file_failed", generate_translation_hashmap(vec![("name", get_full_name_from_path_name(&path, &file_name)), ("reason", e.to_string())])).as_str();
messages += "\n"; messages += "\n";
} }
} else if let Err(e) = trash::delete(format!("{}/{}", path.clone(), file_name.clone())) { } else if let Err(e) = trash::delete(get_full_name_from_path_name(&path, &file_name)) {
messages += fl!("delete_file_failed", generate_translation_hashmap(vec![("name", format!("{}/{}", path, file_name)), ("reason", e.to_string())])).as_str(); messages += fl!("delete_file_failed", generate_translation_hashmap(vec![("name", get_full_name_from_path_name(&path, &file_name)), ("reason", e.to_string())])).as_str();
messages += "\n"; messages += "\n";
} }

View File

@ -140,7 +140,7 @@ pub fn hardlink_symlink(tree_view: &gtk::TreeView, column_file_name: i32, column
if model.path(&current_iter).unwrap() == selected_rows[current_selected_index] { if model.path(&current_iter).unwrap() == selected_rows[current_selected_index] {
let file_name = model.value(&current_iter, column_file_name).get::<String>().unwrap(); let file_name = model.value(&current_iter, column_file_name).get::<String>().unwrap();
let path = model.value(&current_iter, column_path).get::<String>().unwrap(); let path = model.value(&current_iter, column_path).get::<String>().unwrap();
let full_file_path = format!("{}/{}", path, file_name); let full_file_path = get_full_name_from_path_name(&path, &file_name);
if current_symhardlink_data.is_some() { if current_symhardlink_data.is_some() {
vec_tree_path_to_remove.push(model.path(&current_iter).unwrap()); vec_tree_path_to_remove.push(model.path(&current_iter).unwrap());

View File

@ -31,6 +31,12 @@ pub fn connect_button_move(gui_data: &GuiData) {
let tree_view = &main_tree_views[nb_number as usize]; let tree_view = &main_tree_views[nb_number as usize];
let nb_object = &NOTEBOOKS_INFOS[nb_number as usize]; let nb_object = &NOTEBOOKS_INFOS[nb_number as usize];
let (number_of_selected_items, _number_of_selected_groups) = check_how_much_elements_is_selected(tree_view, nb_object.column_color, nb_object.column_selection);
// Nothing is selected
if number_of_selected_items == 0 {
return;
}
move_things( move_things(
tree_view, tree_view,
nb_object.column_name, nb_object.column_name,
@ -174,7 +180,7 @@ fn move_files_common(selected_rows: &[TreePath], model: &gtk::ListStore, column_
let file_name = model.value(&iter, column_file_name).get::<String>().unwrap(); let file_name = model.value(&iter, column_file_name).get::<String>().unwrap();
let path = model.value(&iter, column_path).get::<String>().unwrap(); let path = model.value(&iter, column_path).get::<String>().unwrap();
let thing = format!("{}/{}", path, file_name); let thing = get_full_name_from_path_name(&path, &file_name);
let destination_file = destination_folder.join(file_name); let destination_file = destination_folder.join(file_name);
if Path::new(&thing).is_dir() { if Path::new(&thing).is_dir() {
if let Err(e) = fs_extra::dir::move_dir(&thing, &destination_file, &fs_extra::dir::CopyOptions::new()) { if let Err(e) = fs_extra::dir::move_dir(&thing, &destination_file, &fs_extra::dir::CopyOptions::new()) {

View File

@ -401,11 +401,8 @@ fn popover_custom_select_unselect(popover: &gtk::Popover, window_main: &Window,
let is_selected = model.value(&iter, column_button_selection as i32).get::<bool>().unwrap(); let is_selected = model.value(&iter, column_button_selection as i32).get::<bool>().unwrap();
let path = model.value(&iter, column_path).get::<String>().unwrap(); let path = model.value(&iter, column_path).get::<String>().unwrap();
let name = model.value(&iter, column_file_name).get::<String>().unwrap(); let name = model.value(&iter, column_file_name).get::<String>().unwrap();
#[cfg(not(target_family = "windows"))]
let character = "/"; let path_and_name = get_full_name_from_path_name(&path, &name);
#[cfg(target_family = "windows")]
let character = "\\";
let path_and_name = format!("{}{}{}", path, character, name);
let mut need_to_change_thing: bool = false; let mut need_to_change_thing: bool = false;

View File

@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{ListStore, TextView, Widget}; use gtk::{ListStore, TextView, TreeView, Widget};
use czkawka_core::big_file::BigFile; use czkawka_core::big_file::BigFile;
use czkawka_core::broken_files::BrokenFiles; use czkawka_core::broken_files::BrokenFiles;
@ -19,6 +19,11 @@ use czkawka_core::{fl, invalid_symlinks};
use crate::notebook_enums::{NotebookMainEnum, NUMBER_OF_NOTEBOOK_MAIN_TABS}; use crate::notebook_enums::{NotebookMainEnum, NUMBER_OF_NOTEBOOK_MAIN_TABS};
#[cfg(not(target_family = "windows"))]
pub const CHARACTER: char = '/';
#[cfg(target_family = "windows")]
pub const CHARACTER: char = '\\';
pub const KEY_DELETE: u32 = 119; pub const KEY_DELETE: u32 = 119;
pub const KEY_ENTER: u32 = 36; pub const KEY_ENTER: u32 = 36;
pub const KEY_SPACE: u32 = 65; pub const KEY_SPACE: u32 = 65;
@ -470,6 +475,14 @@ pub fn get_notebook_object_from_tree_view(tree_view: &gtk::TreeView) -> &Noteboo
&NOTEBOOKS_INFOS[nb_enum as usize] &NOTEBOOKS_INFOS[nb_enum as usize]
} }
pub fn get_full_name_from_path_name(path: &str, name: &str) -> String {
let mut string = String::with_capacity(path.len() + name.len() + 1);
string.push_str(path);
string.push(CHARACTER);
string.push_str(name);
string
}
// After e.g. deleting files, header may become orphan or have one child, so should be deleted in this case // 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: &gtk::ListStore, column_color: i32) { pub fn clean_invalid_headers(model: &gtk::ListStore, column_color: i32) {
// Remove only child from header // Remove only child from header
@ -541,6 +554,52 @@ pub fn clean_invalid_headers(model: &gtk::ListStore, column_color: i32) {
} }
} }
} }
pub fn check_how_much_elements_is_selected(tree_view: &TreeView, column_color: Option<i32>, column_selection: i32) -> (u64, u64) {
let mut number_of_selected_items: u64 = 0;
let mut number_of_selected_groups: u64 = 0;
let model = get_list_store(tree_view);
let mut is_item_currently_selected_in_group: bool = false;
// First iter
if let Some(iter) = model.iter_first() {
if let Some(column_color) = column_color {
assert_eq!(model.value(&iter, column_color).get::<String>().unwrap(), HEADER_ROW_COLOR); // First element should be header
loop {
if !model.iter_next(&iter) {
break;
}
if model.value(&iter, column_color).get::<String>().unwrap() == HEADER_ROW_COLOR {
is_item_currently_selected_in_group = false;
} else {
if model.value(&iter, column_selection).get::<bool>().unwrap() {
number_of_selected_items += 1;
if !is_item_currently_selected_in_group {
number_of_selected_groups += 1;
}
is_item_currently_selected_in_group = true;
}
}
}
} else {
loop {
if !model.iter_next(&iter) {
break;
}
if model.value(&iter, column_selection).get::<bool>().unwrap() {
number_of_selected_items += 1;
}
}
}
}
(number_of_selected_items, number_of_selected_groups)
}
pub fn get_custom_label_from_button_with_image(button: &gtk::Bin) -> gtk::Label { pub fn get_custom_label_from_button_with_image(button: &gtk::Bin) -> gtk::Label {
let internal_box = button.child().unwrap().downcast::<gtk::Box>().unwrap(); let internal_box = button.child().unwrap().downcast::<gtk::Box>().unwrap();

View File

@ -673,7 +673,7 @@ fn show_preview(tree_view: &TreeView, text_view_errors: &TextView, check_button_
let path = tree_model.value(&tree_model.iter(&tree_path).unwrap(), column_path).get::<String>().unwrap(); let path = tree_model.value(&tree_model.iter(&tree_path).unwrap(), column_path).get::<String>().unwrap();
let name = tree_model.value(&tree_model.iter(&tree_path).unwrap(), column_name).get::<String>().unwrap(); let name = tree_model.value(&tree_model.iter(&tree_path).unwrap(), column_name).get::<String>().unwrap();
let file_name = format!("{}/{}", path, name); let file_name = get_full_name_from_path_name(&path, &name);
let file_name = file_name.as_str(); let file_name = file_name.as_str();
if let Some(extension) = Path::new(file_name).extension() { if let Some(extension) = Path::new(file_name).extension() {

View File

@ -80,9 +80,7 @@ fn common_open_function(tree_view: &gtk::TreeView, column_name: i32, column_path
let end_path = match opening_mode { let end_path = match opening_mode {
OpenMode::OnlyPath => path, OpenMode::OnlyPath => path,
OpenMode::PathAndName => { OpenMode::PathAndName => get_full_name_from_path_name(&path, &name),
format!("{}/{}", path, name)
}
}; };
open::that_in_background(&end_path); open::that_in_background(&end_path);
@ -96,11 +94,9 @@ fn common_open_function(tree_view: &gtk::TreeView, column_name: i32, column_path
fn handle_tree_keypress(tree_view: &gtk::TreeView, key_code: u32, name_column: i32, path_column: i32, mark_column: i32) { fn handle_tree_keypress(tree_view: &gtk::TreeView, key_code: u32, name_column: i32, path_column: i32, mark_column: i32) {
match key_code { match key_code {
KEY_ENTER => { KEY_ENTER => {
// Enter
common_open_function(tree_view, name_column, path_column, OpenMode::PathAndName); common_open_function(tree_view, name_column, path_column, OpenMode::PathAndName);
} }
KEY_SPACE => { KEY_SPACE => {
// Space
common_mark_function(tree_view, mark_column); common_mark_function(tree_view, mark_column);
} }
_ => {} _ => {}

View File

@ -424,6 +424,9 @@ delete_all_files_in_group_label1 = In some groups there are selected all records
delete_all_files_in_group_label2 = Are you sure that you want to delete them? delete_all_files_in_group_label2 = Are you sure that you want to delete them?
delete_folder_failed = Failed to remove folder {$dir} because folder doesn't exists, you don't have permissions or isn't empty. delete_folder_failed = Failed to remove folder {$dir} because folder doesn't exists, you don't have permissions or isn't empty.
delete_items_label = { $items } files will be removed.
delete_items_groups_label = { $items } files from { $groups } groups will be removed.
hardlink_failed = Failed to hardlink hardlink_failed = Failed to hardlink
hard_sym_invalid_selection_title_dialog = Invalid selection with some groups hard_sym_invalid_selection_title_dialog = Invalid selection with some groups
hard_sym_invalid_selection_label_1 = In some groups there is only 1 record selected and it will be ignored. hard_sym_invalid_selection_label_1 = In some groups there is only 1 record selected and it will be ignored.

View File

@ -367,6 +367,8 @@ delete_all_files_in_group_title = Potwierdzenie usunięcia wszystkich plików w
delete_all_files_in_group_label1 = W niektórych grupach zaznaczono wszystkie rekordy. delete_all_files_in_group_label1 = W niektórych grupach zaznaczono wszystkie rekordy.
delete_all_files_in_group_label2 = Czy na pewno je usunąć? delete_all_files_in_group_label2 = Czy na pewno je usunąć?
delete_folder_failed = Nie udało się usunąć folderu { $name } ponieważ nie istnieje, uprawnienia nie są wystarczające lub nie jest pusty. delete_folder_failed = Nie udało się usunąć folderu { $name } ponieważ nie istnieje, uprawnienia nie są wystarczające lub nie jest pusty.
delete_items_label = { $items } plików zostanie usuniętych.
delete_items_groups_label = { $items } plików z { $groups } grup będzie usuniętych.
hardlink_failed = Nie udało się utworzyć twardego dowiązania hardlink_failed = Nie udało się utworzyć twardego dowiązania
hard_sym_invalid_selection_title_dialog = Niepoprawne zaznaczenie w niektórych grupach hard_sym_invalid_selection_title_dialog = Niepoprawne zaznaczenie w niektórych grupach
hard_sym_invalid_selection_label_1 = W niektórych grupach zaznaczono tylko 1 rekord który zostanie zignorowany. hard_sym_invalid_selection_label_1 = W niektórych grupach zaznaczono tylko 1 rekord który zostanie zignorowany.

View File

@ -63,4 +63,31 @@ Next new record must be added to array.
combo_box_text: "Polski (Polish)", combo_box_text: "Polski (Polish)",
short_text: "pl", short_text: "pl",
}, },
``` ```
# Validating translation offline
When trying to translate objects offline, due renames, adding and removing elements, may happen that translations will contain outdated entries.
To help find such keywords, special python script can be used.
To be able to use it, be sure that you are directly inside main `czkawka` folder.
Next, be sure that your language is available in array/list and also in i18n folder and then run python script `python3 misc/translation_test.py`.
Then results should be visible in console:
```
Checking pl language
Missing keyword - duplicate_mode_name_combo_box
Missing keyword - duplicate_mode_size_combo_box
Missing keyword - duplicate_mode_hash_combo_box
Missing keyword - settings_language_label_tooltip
Missing keyword - settings_language_label
Unused keyword - duplicate_mode_name_checkbox
Unused keyword - duplicate_mode_size_checkbox
Unused keyword - duplicate_mode_hash_checkbox
Unused keyword - duplicate_mode_name_checkbox_tooltip
Unused keyword - duplicate_mode_size_checkbox_tooltip
Unused keyword - duplicate_mode_hash_checkbox_tooltip
```
`Missing keyword` means that some keywords exists in base translations and texts needs to be translated.
`Unused keyword` means that keyword is no longer used. It can be renamed or entirely removed from file.
When script will not print anything except "Checking language", then this means that translation file have exactly same keys as base one.