Skip to content

Commit

Permalink
Renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
qarmin committed Oct 9, 2024
1 parent 28b5576 commit e1bf010
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 120 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
- Added ability to show preview of referenced folders - [#1359](https://github.com/qarmin/czkawka/pull/1359)
- Enable selecting with space and jumping over entries with
arrows and opening with enter - [#1359](https://github.com/qarmin/czkawka/pull/1359)
- Added button to rename files with invalid extension -[#1364](https://github.com/qarmin/czkawka/pull/1364)

### GTK GUI

Expand Down
10 changes: 7 additions & 3 deletions czkawka_core/src/bad_extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ pub struct BadFileEntry {
pub modified_date: u64,
pub size: u64,
pub current_extension: String,
pub proper_extensions: String,
pub proper_extensions_group: String,
pub proper_extension: String,
}

impl ResultEntry for BadFileEntry {
Expand Down Expand Up @@ -345,7 +346,8 @@ impl BadExtensions {
modified_date: file_entry.modified_date,
size: file_entry.size,
current_extension,
proper_extensions: valid_extensions,
proper_extensions_group: valid_extensions,
proper_extension: proper_extension.to_string(),
}))
})
.while_some()
Expand Down Expand Up @@ -386,6 +388,8 @@ impl BadExtensions {
proper_extension: &str,
) -> (BTreeSet<String>, String) {
let mut all_available_extensions: BTreeSet<String> = Default::default();
// TODO Isn't this a bug?
// Why to file without extensions we set this as empty
let valid_extensions = if current_extension.is_empty() {
String::new()
} else {
Expand Down Expand Up @@ -443,7 +447,7 @@ impl PrintResults for BadExtensions {
writeln!(writer, "Found {} files with invalid extension.\n", self.information.number_of_files_with_bad_extension)?;

for file_entry in &self.bad_extensions_files {
writeln!(writer, "\"{}\" ----- {}", file_entry.path.to_string_lossy(), file_entry.proper_extensions)?;
writeln!(writer, "\"{}\" ----- {}", file_entry.path.to_string_lossy(), file_entry.proper_extensions_group)?;
}

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion czkawka_gui/src/compute_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ fn compute_bad_extensions(
(ColumnsBadExtensions::Name as u32, &file),
(ColumnsBadExtensions::Path as u32, &directory),
(ColumnsBadExtensions::CurrentExtension as u32, &file_entry.current_extension),
(ColumnsBadExtensions::ValidExtensions as u32, &file_entry.proper_extensions),
(ColumnsBadExtensions::ValidExtensions as u32, &file_entry.proper_extensions_group),
(
ColumnsBadExtensions::Modification as u32,
&(DateTime::from_timestamp(file_entry.modified_date as i64, 0)
Expand Down
9 changes: 9 additions & 0 deletions krokiet/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ pub enum StrDataBadExtensions {
Name,
Path,
CurrentExtension,
ProperExtensionsGroup,
ProperExtension,
}

Expand Down Expand Up @@ -228,6 +229,14 @@ pub fn get_str_name_idx(active_tab: CurrentTab) -> usize {
}
}

pub fn get_str_proper_extension(active_tab: CurrentTab) -> usize {
match active_tab {
CurrentTab::BadExtensions => StrDataBadExtensions::ProperExtension as usize,
CurrentTab::Settings | CurrentTab::About => panic!("Button should be disabled"),
_ => panic!("Unable to get proper extension from this tab"),
}
}

pub fn get_int_modification_date_idx(active_tab: CurrentTab) -> usize {
match active_tab {
CurrentTab::EmptyFiles => IntDataEmptyFiles::ModificationDatePart1 as usize,
Expand Down
119 changes: 21 additions & 98 deletions krokiet/src/connect_rename.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use std::path::{Path, PathBuf};
use std::{fs, path};
use std::fs;
use std::path::{Path, MAIN_SEPARATOR};

use czkawka_core::common_messages::Messages;
use rayon::prelude::*;
use rfd::FileDialog;
use slint::{ComponentHandle, ModelRc, VecModel};

use crate::common::{get_is_header_mode, get_tool_model, set_tool_model};
use crate::model_operations::{collect_path_name_from_model, deselect_all_items, filter_out_checked_items};
use crate::model_operations::{collect_path_name_and_proper_extension_from_model, deselect_all_items, filter_out_checked_items};
use crate::{Callabler, CurrentTab, GuiState, MainListModel, MainWindow};
use slint::Model;

pub fn connect_rename(app: &MainWindow) {
let a = app.as_weak();
Expand All @@ -18,28 +15,21 @@ pub fn connect_rename(app: &MainWindow) {
let active_tab = app.global::<GuiState>().get_active_tab();
let current_model = get_tool_model(&app, active_tab);

dbg!(active_tab, current_model.iter().count());
// let (errors, new_model) = move_operation(&current_model, preserve_structure, copy_mode, &output_folder, active_tab);
// if let Some(new_model) = new_model {
// set_tool_model(&app, active_tab, new_model);
// }
// app.global::<GuiState>().set_info_text(Messages::new_from_errors(errors).create_messages_text().into());
let (errors, new_model) = rename_operation(&current_model, active_tab);
if let Some(new_model) = new_model {
set_tool_model(&app, active_tab, new_model);
}
app.global::<GuiState>().set_info_text(Messages::new_from_errors(errors).create_messages_text().into());
});
}

fn move_operation(
items: &ModelRc<MainListModel>,
preserve_structure: bool,
copy_mode: bool,
output_folder: &str,
active_tab: CurrentTab,
) -> (Vec<String>, Option<ModelRc<MainListModel>>) {
fn rename_operation(items: &ModelRc<MainListModel>, active_tab: CurrentTab) -> (Vec<String>, Option<ModelRc<MainListModel>>) {
assert_eq!(active_tab, CurrentTab::BadExtensions);
let (entries_to_move, mut entries_left) = filter_out_checked_items(items, get_is_header_mode(active_tab));

if !entries_to_move.is_empty() {
let vec_items_to_move = collect_path_name_from_model(&entries_to_move, active_tab);
let errors = move_selected_items(vec_items_to_move, preserve_structure, copy_mode, output_folder);
let vec_items_to_rename = collect_path_name_and_proper_extension_from_model(&entries_to_move, active_tab);
let errors = rename_selected_items(vec_items_to_rename);
deselect_all_items(&mut entries_left);

let r = ModelRc::new(VecModel::from(entries_left));
Expand All @@ -48,82 +38,15 @@ fn move_operation(
(vec![], None)
}

fn move_selected_items(items_to_move: Vec<(String, String)>, preserve_structure: bool, copy_mode: bool, output_folder: &str) -> Vec<String> {
if let Err(err) = fs::create_dir_all(output_folder) {
return vec![format!("Error while creating folder: {err}")];
fn rename_selected_items(files_with_new_extensions: Vec<(String, String, String)>) -> Vec<String> {
let mut errors = vec![];
for (folder, file_name, new_extension) in files_with_new_extensions {
let file_stem = Path::new(&file_name).file_stem().map(|e| e.to_string_lossy().to_string()).unwrap_or_default();
let new_full_path = format!("{}{}{}.{}", folder, MAIN_SEPARATOR, file_stem, new_extension);
let old_full_path = format!("{}{}{}", folder, MAIN_SEPARATOR, file_name);
if let Err(e) = fs::rename(&old_full_path, &new_full_path) {
errors.push(format!("Failed to rename file {} to {} with error {}", old_full_path, new_full_path, e));
}
}

// TODO option to override files
if copy_mode {
items_to_move
.into_par_iter()
.filter_map(|(path, name)| {
let (input_file, output_file) = collect_path_and_create_folders(&path, &name, output_folder, preserve_structure);

if output_file.exists() {
return Some(format!("File {output_file:?} already exists, and will not be overridden"));
}
try_to_copy_item(&input_file, &output_file)?;
None
})
.collect()
} else {
items_to_move
.into_par_iter()
.filter_map(|(path, name)| {
let (input_file, output_file) = collect_path_and_create_folders(&path, &name, output_folder, preserve_structure);

if output_file.exists() {
return Some(format!("File {output_file:?} already exists, and will not be overridden"));
}

// Try to rename file, may fail due various reasons
if fs::rename(&input_file, &output_file).is_ok() {
return None;
}

// It is possible that this failed, because file is on different partition, so
// we need to copy file and then remove old
try_to_copy_item(&input_file, &output_file)?;

if let Err(e) = fs::remove_file(&input_file) {
return Some(format!("Error while removing file {input_file:?}(after copying into different partition), reason {e}"));
}

None
})
.collect()
}
}

// Tries to copy file/folder, and returns error if it fails
fn try_to_copy_item(input_file: &Path, output_file: &Path) -> Option<String> {
let res = if input_file.is_dir() {
let options = fs_extra::dir::CopyOptions::new();
fs_extra::dir::copy(input_file, output_file, &options) // TODO consider to use less buggy library
} else {
let options = fs_extra::file::CopyOptions::new();
fs_extra::file::copy(input_file, output_file, &options)
};
if let Err(e) = res {
return Some(format!("Error while copying {input_file:?} to {output_file:?}, reason {e}"));
}
None
}

// Create input/output paths, and create output folder
fn collect_path_and_create_folders(input_path: &str, input_file: &str, output_path: &str, preserve_structure: bool) -> (PathBuf, PathBuf) {
let mut input_full_path = PathBuf::from(input_path);
input_full_path.push(input_file);

let mut output_full_path = PathBuf::from(output_path);
if preserve_structure {
output_full_path.extend(Path::new(input_path).components().filter(|c| matches!(c, path::Component::Normal(_))));
};
let _ = fs::create_dir_all(&output_full_path);
output_full_path.push(input_file);

println!("input_full_path: {input_full_path:?}, output_full_path: {output_full_path:?}, output_path: {output_path:?}, input_path: {input_path:?}");

(input_full_path, output_full_path)
errors
}
8 changes: 7 additions & 1 deletion krokiet/src/connect_scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,13 @@ fn write_bad_extensions_results(app: &MainWindow, vector: Vec<BadFileEntry>, mes

fn prepare_data_model_bad_extensions(fe: &BadFileEntry) -> (ModelRc<SharedString>, ModelRc<i32>) {
let (directory, file) = split_path(&fe.path);
let data_model_str = VecModel::from_slice(&[file.into(), directory.into(), fe.current_extension.clone().into(), fe.proper_extensions.clone().into()]);
let data_model_str = VecModel::from_slice(&[
file.into(),
directory.into(),
fe.current_extension.clone().into(),
fe.proper_extensions_group.clone().into(),
fe.proper_extension.clone().into(),
]);
let modification_split = split_u64_into_i32s(fe.get_modified_date());
let size_split = split_u64_into_i32s(fe.size);
let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1, size_split.0, size_split.1]);
Expand Down
30 changes: 21 additions & 9 deletions krokiet/src/model_operations.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::path::MAIN_SEPARATOR;

use slint::{Model, ModelRc};
use slint::{Model, ModelRc, SharedString};

use crate::common::{get_str_name_idx, get_str_path_idx};
use crate::common::{get_str_name_idx, get_str_path_idx, get_str_proper_extension};
use crate::{CurrentTab, MainListModel};

pub fn deselect_all_items(items: &mut [MainListModel]) {
Expand All @@ -26,25 +26,37 @@ pub fn collect_full_path_from_model(items: &[MainListModel], active_tab: Current
items
.iter()
.map(|item| {
let path = item.val_str.iter().nth(path_idx).unwrap_or_else(|| panic!("Failed to get {path_idx} element"));
let name = item.val_str.iter().nth(name_idx).unwrap_or_else(|| panic!("Failed to get {name_idx} element"));
let path = get_shared_str_item(item, path_idx);
let name = get_shared_str_item(item, name_idx);
format!("{path}{MAIN_SEPARATOR}{name}")
})
.collect::<Vec<_>>()
}
pub fn collect_path_name_from_model(items: &[MainListModel], active_tab: CurrentTab) -> Vec<(String, String)> {
let path_idx = get_str_path_idx(active_tab);
let name_idx = get_str_name_idx(active_tab);
items.iter().map(|item| (get_str_item(item, path_idx), get_str_item(item, name_idx))).collect::<Vec<_>>()
}

pub fn collect_path_name_and_proper_extension_from_model(items: &[MainListModel], active_tab: CurrentTab) -> Vec<(String, String, String)> {
let path_idx = get_str_path_idx(active_tab);
let name_idx = get_str_name_idx(active_tab);
let ext_idx = get_str_proper_extension(active_tab);
items
.iter()
.map(|item| {
let path = item.val_str.iter().nth(path_idx).unwrap_or_else(|| panic!("Failed to get {path_idx} element"));
let name = item.val_str.iter().nth(name_idx).unwrap_or_else(|| panic!("Failed to get {name_idx} element"));
(path.to_string(), name.to_string())
})
.map(|item| (get_str_item(item, path_idx), get_str_item(item, name_idx), get_str_item(item, ext_idx)))
.collect::<Vec<_>>()
}

#[inline]
pub fn get_str_item(main_list_model: &MainListModel, idx: usize) -> String {
main_list_model.val_str.iter().nth(idx).unwrap_or_else(|| panic!("Failed to get {idx} element")).to_string()
}
#[inline]
pub fn get_shared_str_item(main_list_model: &MainListModel, idx: usize) -> SharedString {
main_list_model.val_str.iter().nth(idx).unwrap_or_else(|| panic!("Failed to get {idx} element"))
}

pub fn filter_out_checked_items(items: &ModelRc<MainListModel>, have_header: bool) -> (Vec<MainListModel>, Vec<MainListModel>) {
if cfg!(debug_assertions) {
check_if_header_is_checked(items);
Expand Down
6 changes: 0 additions & 6 deletions krokiet/ui/main_lists.slint
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,6 @@ export component MainList {
} else {
debug("Non handled key in main_lists.slint", event, GuiState.active_tab);
}



// else {
// debug("Non handled key in main_lists.slint", event, GuiState.active_tab);
// }
accept
}
}
Expand Down
2 changes: 0 additions & 2 deletions krokiet/ui/popup_rename_files.slint
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ export component PopupRenameFiles inherits Rectangle {
clicked => {
popup_window.close();
Callabler.rename_files();
debug("Renaming files");
}
}
Rectangle {
Expand All @@ -71,7 +70,6 @@ export component PopupRenameFiles inherits Rectangle {
}

show_popup() => {
debug("Showing popup");
popup_window.show();
}
}

0 comments on commit e1bf010

Please sign in to comment.