diff --git a/Cargo.lock b/Cargo.lock index f1bb0d4..494e7c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -186,6 +186,17 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "chrono" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -302,6 +313,7 @@ dependencies = [ name = "czkawka_gui" version = "0.1.1" dependencies = [ + "chrono", "czkawka_core", "gdk", "gio", @@ -614,7 +626,7 @@ checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -1532,9 +1544,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0" +checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1925,6 +1937,17 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "toml" version = "0.5.6" @@ -1988,6 +2011,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasm-bindgen" version = "0.2.68" diff --git a/czkawka_core/Cargo.lock b/czkawka_core/Cargo.lock index 7cccb82..db84ec1 100644 --- a/czkawka_core/Cargo.lock +++ b/czkawka_core/Cargo.lock @@ -1,4 +1,5 @@ -# This file is automatically @generated by Cargo. +# This file is automatically @generated by Cargo. pub fn get_string_from_files_sizes(&self) -> BTreeMap>{ + # It is not intended for manual editing. [[package]] name = "czkawka_core" diff --git a/czkawka_core/src/duplicate.rs b/czkawka_core/src/duplicate.rs index a4f65e2..a0a7c4c 100644 --- a/czkawka_core/src/duplicate.rs +++ b/czkawka_core/src/duplicate.rs @@ -25,7 +25,7 @@ pub enum DeleteMethod { } #[derive(Clone)] -struct FileEntry { +pub struct FileEntry { pub path: String, pub size: u64, pub created_date: SystemTime, @@ -103,13 +103,61 @@ impl DuplicateFinder { excluded_directories: vec![], included_directories: vec![], recursive_search: true, - min_file_size: 1024, allowed_extensions: vec![], check_method: CheckingMethod::NONE, delete_method: DeleteMethod::None, + min_file_size: 0, } } + pub fn get_files_sorted_by_size(&self) -> &BTreeMap> { + &self.files_with_identical_size + } + + pub fn get_files_sorted_by_hash(&self) -> &BTreeMap>> { + &self.files_with_identical_hashes + } + + // pub fn get_string_from_files_hashes(&self) -> BTreeMap>> { + // let mut row_data: BTreeMap>> = Default::default(); + // for (size, vector) in &self.files_with_identical_hashes { + // let mut vector_of_vectors: Vec> = Vec::new(); + // for files in vector { + // let mut data_vector: Vec = Vec::new(); + // for file in files { + // let name = file.path.clone(); + // let index = name.rfind('/').unwrap(); + // + // data_vector.push(GtkRowData { + // name: name[index + 1..].to_string(), + // directory: name[..index].to_string(), + // modification_date: NaiveDateTime::from_timestamp(file.modified_date.duration_since(UNIX_EPOCH).expect("Invalid file date").as_secs() as i64, 0).to_string(), + // }); + // } + // vector_of_vectors.push(data_vector); + // } + // row_data.insert(*size, vector_of_vectors); + // } + // row_data + // } + // pub fn get_string_from_files_sizes(&self) -> BTreeMap> { + // let mut row_data: BTreeMap> = Default::default(); + // for (size, vector) in &self.files_with_identical_size { + // let mut data_vector: Vec = Vec::new(); + // for file in vector { + // let name = file.path.clone(); + // let index = name.rfind('/').unwrap(); + // + // data_vector.push(GtkRowData { + // name: name[index + 1..].to_string(), + // directory: name[..index].to_string(), + // modification_date: NaiveDateTime::from_timestamp(file.modified_date.duration_since(UNIX_EPOCH).expect("Invalid file date").as_secs() as i64, 0).to_string(), + // }); + // } + // row_data.insert(*size, data_vector); + // } + // row_data + // } pub fn get_text_messages(&self) -> &Messages { &self.text_messages } diff --git a/czkawka_gui/Cargo.toml b/czkawka_gui/Cargo.toml index 19ef94c..cb897d3 100644 --- a/czkawka_gui/Cargo.toml +++ b/czkawka_gui/Cargo.toml @@ -11,7 +11,9 @@ czkawka_core = { path = "../czkawka_core" } gdk = "0.13.2" gio = "0.9.1" glib = "0.10.1" + humansize = "1" +chrono = "0.4" [dependencies.gtk] diff --git a/czkawka_gui/src/main.rs b/czkawka_gui/src/main.rs index e7dc270..153efc3 100644 --- a/czkawka_gui/src/main.rs +++ b/czkawka_gui/src/main.rs @@ -3,10 +3,13 @@ use czkawka_core::{duplicate, empty_folder}; use humansize::{file_size_opts as options, FileSize}; extern crate gtk; +use chrono::NaiveDateTime; +use czkawka_core::duplicate::CheckingMethod; use duplicate::DuplicateFinder; use gtk::prelude::*; use gtk::{Builder, TreeView, TreeViewColumn}; use std::collections::HashMap; +use std::time::UNIX_EPOCH; #[derive(Debug)] #[repr(i32)] @@ -16,6 +19,10 @@ enum ColumnsDuplicate { Modification, } +thread_local! { +pub static CHECK_TYPE: duplicate::CheckingMethod = duplicate::CheckingMethod::NONE; +} + fn main() { gtk::init().expect("Failed to initialize GTK."); @@ -47,6 +54,12 @@ fn main() { let buttons_delete: gtk::Button = builder.get_object("buttons_delete").unwrap(); let buttons_save: gtk::Button = builder.get_object("buttons_save").unwrap(); + // Not used buttons for now + buttons_stop.hide(); + buttons_resume.hide(); + buttons_pause.hide(); + buttons_select.hide(); + // Notebooks let notebook_chooser_tool: gtk::Notebook = builder.get_object("notebook_chooser_tool").unwrap(); let mut notebook_chooser_tool_children_names: Vec = Vec::new(); @@ -65,94 +78,12 @@ fn main() { // Set starting intro // Duplicate Finder - let col_types: [glib::types::Type; 3] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; - let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); - - let tree_view_duplicate_finder: gtk::TreeView = TreeView::with_model(&list_store); - - let renderer = gtk::CellRendererText::new(); - let name_column: gtk::TreeViewColumn = TreeViewColumn::new(); - name_column.pack_start(&renderer, true); - name_column.set_title("File Name"); - name_column.set_resizable(true); - name_column.set_min_width(50); - name_column.add_attribute(&renderer, "text", ColumnsDuplicate::Name as i32); - tree_view_duplicate_finder.append_column(&name_column); - - let renderer = gtk::CellRendererText::new(); - let path_column: gtk::TreeViewColumn = TreeViewColumn::new(); - path_column.pack_start(&renderer, true); - path_column.set_title("Path"); - path_column.set_resizable(true); - path_column.set_min_width(100); - path_column.add_attribute(&renderer, "text", ColumnsDuplicate::Path as i32); - tree_view_duplicate_finder.append_column(&path_column); - - let renderer = gtk::CellRendererText::new(); - let modification_date_column: gtk::TreeViewColumn = TreeViewColumn::new(); - modification_date_column.pack_start(&renderer, true); - modification_date_column.set_title("Modification Date"); - modification_date_column.set_resizable(true); - modification_date_column.set_min_width(100); - modification_date_column.add_attribute(&renderer, "text", ColumnsDuplicate::Modification as i32); - tree_view_duplicate_finder.append_column(&modification_date_column); - - tree_view_duplicate_finder.set_vexpand(true); - - let col_indices = [0, 1, 2]; - - for _i in 1..10 { - let values: [&dyn ToValue; 3] = [&"Roman".to_string(), &"Waq".to_string(), &"QQ".to_string()]; - list_store.set(&list_store.append(), &col_indices, &values); - } - - scrolled_window_duplicate_finder.add(&tree_view_duplicate_finder); - scrolled_window_duplicate_finder.show_all(); - - // let model = Rc::new(create_duplicate_model()); - // let treeview = gtk::TreeView::with_model(&*model); - // - // - // let data: [DataDuplicate; 2] = [ - // DataDuplicate { - // name: "Roman".to_string(), - // path: "/home/rr".to_string(), - // modification: "2010-10-10".to_string(), - // }, - // DataDuplicate { - // name: "Herman".to_string(), - // path: "/etc".to_string(), - // modification: "2020-10-10".to_string(), - // }, - // ]; - // - // - // let col_indices: [u32; 3] = [0, 1, 2]; - // - // for d in data.iter() { - // let values: [&dyn ToValue; 3] = [ - // &d.name, - // &d.path, - // &d.modification, - // ]; - // model.set(&model.append(), &col_indices, &values); - // } - // - // scrolled_window_duplicate_finder.add(&treeview); - // - // - // add_columns_duplicate(&model, &treeview); - info_entry.set_text("Duplicated Files"); // // Disable all unused buttons buttons_search.show(); - buttons_stop.hide(); - buttons_resume.hide(); - buttons_pause.hide(); - buttons_select.show(); - buttons_delete.hide(); buttons_save.hide(); + buttons_delete.hide(); } { // Connect Buttons @@ -181,57 +112,104 @@ fn main() { let duplicates_size: u64; let duplicates_group: usize; - if check_method == duplicate::CheckingMethod::HASH { - duplicates_number = information.number_of_duplicated_files_by_hash; - duplicates_size = information.lost_space_by_hash; - duplicates_group = information.number_of_groups_by_hash; - } else { - duplicates_number = information.number_of_duplicated_files_by_size; - duplicates_size = information.lost_space_by_size; - duplicates_group = information.number_of_groups_by_size; + match check_method { + duplicate::CheckingMethod::HASH => { + duplicates_number = information.number_of_duplicated_files_by_hash; + duplicates_size = information.lost_space_by_hash; + duplicates_group = information.number_of_groups_by_hash; + } + duplicate::CheckingMethod::SIZE => { + duplicates_number = information.number_of_duplicated_files_by_size; + duplicates_size = information.lost_space_by_size; + duplicates_group = information.number_of_groups_by_size; + } + duplicate::CheckingMethod::NONE => { + panic!(); + } } info_entry.set_text(format!("Found {} duplicates files in {} groups which took {}.", duplicates_number, duplicates_group, duplicates_size.file_size(options::BINARY).unwrap()).as_str()); - // Buttons - buttons_select.show(); - buttons_delete.show(); - // TODO Add buttons - // if *hashmap_buttons.get("duplicate").unwrap() { - // buttons_select.show(); - // buttons_delete.show(); - // } - // else{ - // buttons_select.hide(); - // buttons_delete.hide(); - // } + // Set Scrolled window + //let results =df. + + // Remove scrolled window from before - BUG - when doing it when view is scrolled, then scroll button disappears + for i in &scrolled_window_duplicate_finder.get_children() { + scrolled_window_duplicate_finder.remove(i); + } + + let col_types: [glib::types::Type; 3] = [glib::types::Type::String, glib::types::Type::String, glib::types::Type::String]; + let list_store: gtk::ListStore = gtk::ListStore::new(&col_types); + + let mut tree_view_duplicate_finder: gtk::TreeView = TreeView::with_model(&list_store); + + create_tree_view_duplicates(&mut tree_view_duplicate_finder); + + let col_indices = [0, 1, 2]; + match check_method { + CheckingMethod::HASH => { + let hashmap = df.get_files_sorted_by_hash(); + + for (size, vectors_vector) in hashmap { + for vector in vectors_vector { + let values: [&dyn ToValue; 3] = [ + &(vector.len().to_string() + " x " + size.to_string().as_str()), + &("(".to_string() + ((vector.len() - 1) as u64 * *size as u64).to_string().as_str() + ")"), + &"Bytes lost".to_string(), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + for entry in vector { + let path = &entry.path; + let index = path.rfind('/').unwrap(); + + let values: [&dyn ToValue; 3] = [ + &(path[index + 1..].to_string()), + &(path[..index].to_string()), + &(NaiveDateTime::from_timestamp(entry.modified_date.duration_since(UNIX_EPOCH).expect("Invalid file date").as_secs() as i64, 0).to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + } + } + } + } + CheckingMethod::SIZE => { + let hashmap = df.get_files_sorted_by_size(); + + for (size, vector) in hashmap { + let values: [&dyn ToValue; 3] = [ + &(vector.len().to_string() + " x " + size.to_string().as_str()), + &("(".to_string() + ((vector.len() - 1) as u64 * *size as u64).to_string().as_str() + ")"), + &"Bytes lost".to_string(), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + for entry in vector { + let path = &entry.path; + let index = path.rfind('/').unwrap(); + + let values: [&dyn ToValue; 3] = [ + &(path[index + 1..].to_string()), + &(path[..index].to_string()), + &(NaiveDateTime::from_timestamp(entry.modified_date.duration_since(UNIX_EPOCH).expect("Invalid file date").as_secs() as i64, 0).to_string()), + ]; + list_store.set(&list_store.append(), &col_indices, &values); + } + } + } + CheckingMethod::NONE => { + panic!(); + } + } + + scrolled_window_duplicate_finder.add(&tree_view_duplicate_finder); + scrolled_window_duplicate_finder.show_all(); + + // Buttons buttons_search_clone.show(); - buttons_stop.hide(); - buttons_resume.hide(); - buttons_pause.hide(); buttons_save.hide(); + buttons_delete.hide(); } - "notebook_empty_folders_label" => { - // let mut ef = empty_folder::EmptyFolder::new(); - // let mut delete_folders: bool = false; - // - // ef.set_include_directory("/home/rafal/Pulpit".to_string()); - // - // ef.find_empty_folders(false); - // - // - // info_entry.set_text(format!("Found {} empty folders.",duplicates_number).as_str()); - // - // - // buttons_select.show(); - // buttons_delete.show(); - // buttons_search_clone.show(); - // buttons_stop.hide(); - // buttons_resume.hide(); - // buttons_pause.hide(); - // buttons_save.hide(); - } + "notebook_empty_folders_label" => {} e => panic!("Not existent {}", e), } }); @@ -246,3 +224,33 @@ fn main() { // We start the gtk main loop. gtk::main(); } +pub fn create_tree_view_duplicates(tree_view_duplicate_finder: &mut gtk::TreeView) { + let renderer = gtk::CellRendererText::new(); + let name_column: gtk::TreeViewColumn = TreeViewColumn::new(); + name_column.pack_start(&renderer, true); + name_column.set_title("File Name"); + name_column.set_resizable(true); + name_column.set_min_width(50); + name_column.add_attribute(&renderer, "text", ColumnsDuplicate::Name as i32); + tree_view_duplicate_finder.append_column(&name_column); + + let renderer = gtk::CellRendererText::new(); + let path_column: gtk::TreeViewColumn = TreeViewColumn::new(); + path_column.pack_start(&renderer, true); + path_column.set_title("Path"); + path_column.set_resizable(true); + path_column.set_min_width(100); + path_column.add_attribute(&renderer, "text", ColumnsDuplicate::Path as i32); + tree_view_duplicate_finder.append_column(&path_column); + + let renderer = gtk::CellRendererText::new(); + let modification_date_column: gtk::TreeViewColumn = TreeViewColumn::new(); + modification_date_column.pack_start(&renderer, true); + modification_date_column.set_title("Modification Date"); + modification_date_column.set_resizable(true); + modification_date_column.set_min_width(100); + modification_date_column.add_attribute(&renderer, "text", ColumnsDuplicate::Modification as i32); + tree_view_duplicate_finder.append_column(&modification_date_column); + + tree_view_duplicate_finder.set_vexpand(true); +}