From 527230934149cdfcb4df493a86b9061ef6bcb43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= <41945903+qarmin@users.noreply.github.com> Date: Wed, 5 Apr 2023 08:08:43 +0200 Subject: [PATCH] Implement finding duplicates by size/name (#956) * Implementing Size+Name method * Partial hashing * Move hashing into different functions * Update * Add some code * Split code into parts * Entry size * Simplify code * Bottom Buttons * Bottom Buttons * Confusion * Libheif * Simplified sorting * Revert libheif change --- Cargo.lock | 748 +++-- czkawka_cli/Cargo.toml | 2 +- czkawka_cli/src/commands.rs | 1 + czkawka_core/Cargo.toml | 20 +- czkawka_core/src/broken_files.rs | 1 + czkawka_core/src/common.rs | 4 + czkawka_core/src/common_dir_traversal.rs | 1 + czkawka_core/src/duplicate.rs | 636 ++-- czkawka_core/src/same_music.rs | 1 + czkawka_core/src/similar_images.rs | 4 +- czkawka_gui/Cargo.toml | 20 +- czkawka_gui/i18n/en/czkawka_gui.ftl | 2 + czkawka_gui/src/compute_results.rs | 2897 ++++++++--------- .../connect_things/connect_button_compare.rs | 12 +- .../src/connect_things/connect_button_sort.rs | 3 +- .../connect_duplicate_buttons.rs | 2 +- .../connect_things/connect_popovers_sort.rs | 6 +- .../connect_things/connect_progress_window.rs | 10 + .../src/gui_structs/gui_main_notebook.rs | 5 +- czkawka_gui/src/help_combo_box.rs | 6 +- czkawka_gui/src/help_functions.rs | 4 +- czkawka_gui/src/initialize_gui.rs | 1 - czkawka_gui/src/notebook_info.rs | 49 +- czkawka_gui/src/saving_loading.rs | 4 +- 24 files changed, 2397 insertions(+), 2042 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 32b538b..93610ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,10 +45,50 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.69" +name = "anstream" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-wincon", + "concolor-override", + "concolor-query", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" + +[[package]] +name = "anstyle-parse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-wincon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" +dependencies = [ + "anstyle", + "windows-sys 0.45.0", +] + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "arc-swap" @@ -58,9 +98,9 @@ checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -70,13 +110,13 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "async-trait" -version = "0.1.65" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "095183a3539c7c7649b2beb87c2d3f0591f3a7fed07761cc546d244e27e0238c" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -134,10 +174,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "bk-tree" -version = "0.4.0" +name = "bitflags" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6121f6e107e94d717b5ca2631d56e7c2ed1542a21b2eb87b4bda1d6c1420ef3f" +checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" + +[[package]] +name = "bk-tree" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8283fb8e64b873918f8bc527efa6aff34956296e48ea750a9c909cd47c01546" dependencies = [ "fnv", "triple_accel", @@ -153,7 +199,7 @@ dependencies = [ "arrayvec", "cc", "cfg-if", - "constant_time_eq 0.2.4", + "constant_time_eq 0.2.5", "digest", ] @@ -165,9 +211,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] @@ -233,7 +279,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8af54f5d48af1226928adc1f57edd22f5df1349e7da1fc96ae15cf43db0e871" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", "glib", "libc", @@ -271,9 +317,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.11.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" +checksum = "a35b255461940a32985c627ce82900867c61db1659764d3675ea81963f72a4c6" dependencies = [ "smallvec", ] @@ -286,9 +332,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "js-sys", @@ -310,40 +356,45 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.8" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", - "clap_lex", - "is-terminal", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" +dependencies = [ + "anstream", + "anstyle", + "bitflags 1.3.2", + "clap_lex", "strsim", - "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.8" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] name = "clap_lex" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "codespan-reporting" @@ -361,6 +412,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "concolor-override" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" + +[[package]] +name = "concolor-query" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" +dependencies = [ + "windows-sys 0.45.0", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -369,21 +435,21 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "constant_time_eq" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" dependencies = [ "libc", ] @@ -458,9 +524,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -470,9 +536,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -480,24 +546,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 2.0.13", ] [[package]] name = "cxxbridge-flags" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -516,7 +582,7 @@ dependencies = [ "anyhow", "audio_checker", "bincode", - "bitflags", + "bitflags 2.0.2", "bk-tree", "blake3", "crc32fast", @@ -591,22 +657,22 @@ dependencies = [ [[package]] name = "datasize" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3319c13ed12c1ce89494db62541bc66759c8870c3562bdf7b25b930420a00432" +checksum = "c88ad90721dc8e2ebe1430ac2f59c5bdcd74478baa68da26f30f33b0fe997f11" dependencies = [ "datasize_derive", ] [[package]] name = "datasize_derive" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abd50b37ab87677c31190aad6b4186be9993a618ff753c4b007551de6841ee8" +checksum = "8b0415ec81945214410892a00d4b5dd4566f6263205184248e018a3fe384a61e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -658,7 +724,7 @@ checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -684,24 +750,24 @@ dependencies = [ [[package]] name = "enumn" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88bcb3a067a6555d577aba299e75eff9942da276e6506fc6274327daa026132" +checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] name = "errno" -version = "0.2.8" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.45.0", ] [[package]] @@ -716,17 +782,17 @@ dependencies = [ [[package]] name = "exr" -version = "1.5.3" +version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8af5ef47e2ed89d23d0ecbc1b681b30390069de70260937877514377fc24feb" +checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" dependencies = [ "bit_field", "flume", "half", "lebe", "miniz_oxide", + "rayon-core", "smallvec", - "threadpool", "zune-inflate", ] @@ -756,7 +822,7 @@ checksum = "3c1d7ffc9f2dc8316348c75281a99c8fdc60c1ddf4f82a366d117bf1b74d5a39" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -789,7 +855,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] @@ -888,9 +954,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -903,9 +969,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -913,15 +979,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -930,38 +996,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -981,7 +1047,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b023fbe0c6b407bd3d9805d107d9800da3829dc5a676653210f1d5f16d7f59bf" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", @@ -1004,11 +1070,11 @@ dependencies = [ [[package]] name = "gdk4" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5042053ee765aeef08d9d7e3f0f1e36a4d37f1659b3f93ad3d6997515dbb64a" +checksum = "c3abf96408a26e3eddf881a7f893a1e111767137136e347745e8ea6ed12731ff" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk4-sys", @@ -1020,9 +1086,9 @@ dependencies = [ [[package]] name = "gdk4-sys" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f0fb00507af1e9299681dd09965f720e2b5ea95536d49a5681e8994ef10c7a" +checksum = "1bc92aa1608c089c49393d014c38ac0390d01e4841e1fedaa75dbcef77aaed64" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1045,14 +1111,14 @@ dependencies = [ "libc", "log", "rustversion", - "windows", + "windows 0.44.0", ] [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1073,9 +1139,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.11.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" dependencies = [ "color_quant", "weezl", @@ -1083,11 +1149,11 @@ dependencies = [ [[package]] name = "gio" -version = "0.17.2" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65acfc24267314eee46f49e0a531e08fd6c3025040d1cfb4a7cd8e41c5e06116" +checksum = "2261a3b4e922ec676d1c27ac466218c38cf5dcb49a759129e54bb5046e442125" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", @@ -1103,9 +1169,9 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.17.0" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d3076ecb86c8c3a672c9843d6232b3a344fb81d304d0ba1ac64b23343efa46" +checksum = "6b1d43b0d7968b48455244ecafe41192871257f5740aa6b095eb19db78e362a5" dependencies = [ "glib-sys", "gobject-sys", @@ -1116,11 +1182,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.17.2" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78b6a0901e258cb03c761ca94c84d519427ede489cae12cd5ba0d7d584e69e9" +checksum = "cfb53061756195d76969292c2d2e329e01259276524a9bae6c9b73af62854773" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-executor", @@ -1139,9 +1205,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.17.2" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e93d79ed130f0f0b58bc0aa29fb0e40c9dfd63997fec51f8adf780d1520bc4" +checksum = "bc4cf346122086f196260783aa58987190dbd5f43bfab01946d2bf9786e8d9ef" dependencies = [ "anyhow", "heck", @@ -1149,14 +1215,14 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "glib-sys" -version = "0.17.2" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a0985cf568e18cf63b443c9a14f4bdaa947fed7437476000dba84926a20b25" +checksum = "49f00ad0a1bf548e61adfff15d83430941d9e1bb620e334f779edd1c745680a5" dependencies = [ "libc", "system-deps", @@ -1179,9 +1245,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.17.0" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a0155d388840c77d61b033b66ef4f9bc7f4133d83df83572d6b4fb234a3be7d" +checksum = "15e75b0000a64632b2d8ca3cf856af9308e3a970844f6e9659bd197f026793d0" dependencies = [ "glib-sys", "libc", @@ -1213,11 +1279,11 @@ dependencies = [ [[package]] name = "gsk4" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa9cd285a72a95124b65c069a9cb1b8fb8e310be71783404c39fccf3bf7774c" +checksum = "6f01ef44fa7cac15e2da9978529383e6bee03e570ba5bf7036b4c10a15cc3a3c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk4", "glib", @@ -1229,9 +1295,9 @@ dependencies = [ [[package]] name = "gsk4-sys" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a445ae1e50cbf181a1d5c61b920a7e7e8657b96e0ecdbbf8911a86fad462a32" +checksum = "c07a84fb4dcf1323d29435aa85e2f5f58bef564342bef06775ec7bd0da1f01b0" dependencies = [ "cairo-sys-rs", "gdk4-sys", @@ -1245,11 +1311,11 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47dca53cb1a8ae3006e869b5711ae7370180db537f6d98e3bcaf23fabfd911f" +checksum = "1e30e124b5a605f6f5513db13958bfcd51d746607b20bc7bb718b33e303274ed" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -1268,23 +1334,23 @@ dependencies = [ [[package]] name = "gtk4-macros" -version = "0.6.0" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4676c4f90d8b010e88cb4558f61f47d76d6f6b8e6f6b89e62640f443907f61" +checksum = "f041a797fb098bfb06e432c61738133604bfa3af57f13f1da3b9d46271422ef0" dependencies = [ "anyhow", "proc-macro-crate", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "gtk4-sys" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65463dc801460e498d5e7ffa6e9ae2cfbed7d05fabd1ca5a8d024adbc89eeda6" +checksum = "5f8283f707b07e019e76c7f2934bdd4180c277e08aa93f4c0d8dd07b7a34e22f" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1369,7 +1435,7 @@ dependencies = [ "serde", "serde_derive", "thiserror", - "toml", + "toml 0.5.11", "unic-langid", ] @@ -1397,9 +1463,9 @@ dependencies = [ [[package]] name = "i18n-embed-fl" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a425b9bbdc2e4cd797a2a79528662cb61894bd36db582e48da2c56c28eb727cd" +checksum = "4b5809e2295beeb55013705c3b947cbbe83b8cadf3c73a1e6dca06381927212a" dependencies = [ "dashmap", "find-crate", @@ -1412,7 +1478,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 1.0.109", "unic-langid", ] @@ -1426,21 +1492,21 @@ dependencies = [ "i18n-config", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows 0.48.0", ] [[package]] @@ -1465,9 +1531,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.5" +version = "0.24.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" dependencies = [ "bytemuck", "byteorder", @@ -1478,7 +1544,7 @@ dependencies = [ "num-rational", "num-traits", "png", - "scoped_threadpool", + "qoi", "tiff", ] @@ -1517,9 +1583,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -1573,19 +1639,20 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.5" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ + "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "is-terminal" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", @@ -1649,9 +1716,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libheif-rs" @@ -1696,9 +1763,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" [[package]] name = "locale_config" @@ -1725,13 +1792,12 @@ dependencies = [ [[package]] name = "lofty" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed685b48b30ef8f5213a32422d08c80b765f954ad5b6f6b634f901e7844ca52" +checksum = "5d8d7482b0444750bb69ce3abaae2178e68cf8bbd85019ae7f19c404b816c7bd" dependencies = [ "base64 0.21.0", "byteorder", - "cfg-if", "flate2", "lofty_attr", "log", @@ -1748,7 +1814,7 @@ checksum = "336dfabb2fdfd932cebfcaa5d0fc57abac0d49f6ae9ddaa7c47a51bf9f74f966" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1816,9 +1882,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -1977,20 +2043,13 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "open" -version = "3.2.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" +checksum = "075c5203b3a2b698bc72c6c10b1f6263182135751d5013ea66e8a4b3d0562a43" dependencies = [ "pathdiff", - "windows-sys 0.42.0", ] -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - [[package]] name = "overload" version = "0.1.1" @@ -1999,11 +2058,11 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pango" -version = "0.17.0" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243c048be90312220fb3bd578176eed8290568274a93c95040289d39349384bc" +checksum = "52c280b82a881e4208afb3359a8e7fde27a1b272280981f1f34610bed5770d37" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gio", "glib", "libc", @@ -2041,7 +2100,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -2059,9 +2118,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pathdiff" @@ -2088,7 +2147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fbf9b11d32e9323b219368cc9a858485f3408901721a28b1b7b1aa18a747d69" dependencies = [ "aes", - "bitflags", + "bitflags 1.3.2", "block-modes", "datasize", "deflate", @@ -2116,7 +2175,7 @@ checksum = "7f4007262775d0798de87b15cbc64cf1aed5f7ee87eec847e297b69d8ed4b4f8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2142,7 +2201,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2169,7 +2228,7 @@ version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", "flate2", "miniz_oxide", @@ -2209,7 +2268,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -2226,18 +2285,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] -name = "quote" -version = "1.0.23" +name = "qoi" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -2284,7 +2352,7 @@ dependencies = [ "lazy_static", "rayon", "rustc_version", - "toml", + "toml 0.5.11", ] [[package]] @@ -2315,7 +2383,16 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -2325,15 +2402,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "aho-corasick", "memchr", @@ -2351,15 +2428,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rust-embed" -version = "6.6.0" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb133b9a38b5543fad3807fb2028ea47c5f2b566f4f5e28a11902f1a358348b6" +checksum = "1b68543d5527e158213414a92832d2aab11a84d2571a5eb021ebe22c43aab066" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -2375,7 +2452,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn", + "syn 1.0.109", "walkdir", ] @@ -2453,11 +2530,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.9" +version = "0.37.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -2467,9 +2544,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "ryu" @@ -2492,12 +2569,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" -[[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" - [[package]] name = "scopeguard" version = "1.1.0" @@ -2506,9 +2577,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5e082f6ea090deaf0e6dd04b68360fd5cddb152af6ce8927c9d25db299f98c" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "self_cell" @@ -2518,41 +2589,50 @@ checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + [[package]] name = "serde_yaml" version = "0.8.26" @@ -2598,9 +2678,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a5df39617d7c8558154693a1bb8157a4aab8179209540cc0b10e5dc24e0b18" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" [[package]] name = "slab" @@ -2636,14 +2716,14 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "spin" -version = "0.9.5" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ "lock_api", ] @@ -2725,7 +2805,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55a0846e7a2c9a8081ff799fc83a975170417ad2a143f644a77ec2e3e82a2b73" dependencies = [ - "bitflags", + "bitflags 1.3.2", "lazy_static", "log", "symphonia-core", @@ -2791,7 +2871,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b9567e2d8a5f866b2f94f5d366d811e0c6826babcff6d37de9e1a6690d38869" dependencies = [ "arrayvec", - "bitflags", + "bitflags 1.3.2", "bytemuck", "lazy_static", "log", @@ -2880,29 +2960,40 @@ dependencies = [ ] [[package]] -name = "system-deps" -version = "6.0.3" +name = "syn" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555fc8147af6256f3931a36bb83ad0023240ce9cf2b319dec8236fd1f220b05f" dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml", + "toml 0.7.3", "version-compare", ] [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -2916,22 +3007,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] @@ -2944,15 +3035,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - [[package]] name = "tiff" version = "0.8.1" @@ -3024,19 +3106,36 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" -version = "0.19.4" +version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -3061,7 +3160,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -3126,7 +3225,7 @@ dependencies = [ "once_cell", "scopeguard", "url", - "windows", + "windows 0.44.0", ] [[package]] @@ -3180,15 +3279,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775c11906edafc97bc378816b94585fbd9a054eabaf86fdd0ced94af449efab7" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -3216,6 +3315,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.3.0" @@ -3259,12 +3364,11 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -3301,7 +3405,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -3323,7 +3427,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3377,22 +3481,16 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", ] [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-targets 0.48.0", ] [[package]] @@ -3401,71 +3499,137 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95fb4ff192527911dd18eb138ac30908e7165b8944e528b6af93aa4c842d345" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" dependencies = [ "memchr", ] @@ -3506,9 +3670,9 @@ dependencies = [ [[package]] name = "zune-inflate" -version = "0.2.50" +version = "0.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589245df6230839c305984dcc0a8385cc72af1fd223f360ffd5d65efa4216d40" +checksum = "440a08fd59c6442e4b846ea9b10386c38307eae728b216e1ab2c305d1c9daaf8" dependencies = [ "simd-adler32", ] diff --git a/czkawka_cli/Cargo.toml b/czkawka_cli/Cargo.toml index 2a53c87..2f806c1 100644 --- a/czkawka_cli/Cargo.toml +++ b/czkawka_cli/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/qarmin/czkawka" repository = "https://github.com/qarmin/czkawka" [dependencies] -clap = { version = "4.1", features = ["derive"] } +clap = { version = "4.2", features = ["derive"] } # For enum types image_hasher = "1.1.2" diff --git a/czkawka_cli/src/commands.rs b/czkawka_cli/src/commands.rs index dca1cc0..0e907b7 100644 --- a/czkawka_cli/src/commands.rs +++ b/czkawka_cli/src/commands.rs @@ -584,6 +584,7 @@ fn parse_checking_method(src: &str) -> Result { match src.to_ascii_lowercase().as_str() { "name" => Ok(CheckingMethod::Name), "size" => Ok(CheckingMethod::Size), + "size_name" => Ok(CheckingMethod::SizeName), "hash" => Ok(CheckingMethod::Hash), _ => Err("Couldn't parse the search method (allowed: NAME, SIZE, HASH)"), } diff --git a/czkawka_core/Cargo.toml b/czkawka_core/Cargo.toml index 74ade62..165eb79 100644 --- a/czkawka_core/Cargo.toml +++ b/czkawka_core/Cargo.toml @@ -20,16 +20,16 @@ directories-next = "2.0.0" # Needed by similar images image_hasher = "1.1.2" -bk-tree = "0.4.0" -image = "0.24.5" +bk-tree = "0.5.0" +image = "0.24.6" hamming = "0.1.3" # Needed by same music -bitflags = "1.3.2" -lofty = "0.11.0" +bitflags = "2.0.2" +lofty = "0.12.0" # Futures - needed by async progress sender -futures = "0.3.26" +futures = "0.3.28" # Needed by broken files zip = { version = "0.6.4", features = ["aes-crypto", "bzip2", "deflate", "time"], default-features = false } @@ -41,7 +41,7 @@ blake3 = "1.3.3" crc32fast = "1.3.2" xxhash-rust = { version = "0.8.6", features = ["xxh3"] } -tempfile = "3.4.0" +tempfile = "3.5.0" # Video Duplicates vid_dup_finder_lib = "0.1.1" @@ -54,8 +54,8 @@ serde_json = "1.0" # Language i18n-embed = { version = "0.13.8", features = ["fluent-system", "desktop-requester"] } -i18n-embed-fl = "0.6.5" -rust-embed = "6.6.0" +i18n-embed-fl = "0.6.6" +rust-embed = "6.6.1" once_cell = "1.17.1" # Raw image files @@ -69,10 +69,10 @@ infer = "0.13.0" num_cpus = "1.15.0" # Heif/Heic -libheif-rs = { version = "0.18.0", optional = true } +libheif-rs = { version = "0.18.0", optional = true } # Do not upgrade now, since Ubuntu 22.04 not works with newer version anyhow = { version = "1.0", optional = true } -state="0.5.3" +state = "0.5.3" [features] default = [] diff --git a/czkawka_core/src/broken_files.rs b/czkawka_core/src/broken_files.rs index 3fda348..850093f 100644 --- a/czkawka_core/src/broken_files.rs +++ b/czkawka_core/src/broken_files.rs @@ -59,6 +59,7 @@ pub enum TypeOfFile { } bitflags! { + #[derive(PartialEq, Copy, Clone)] pub struct CheckedTypes : u32 { const NONE = 0; diff --git a/czkawka_core/src/common.rs b/czkawka_core/src/common.rs index 32b6206..753c332 100644 --- a/czkawka_core/src/common.rs +++ b/czkawka_core/src/common.rs @@ -10,6 +10,8 @@ use anyhow::Result; use directories_next::ProjectDirs; use image::{DynamicImage, ImageBuffer, Rgb}; use imagepipe::{ImageSource, Pipeline}; +// #[cfg(feature = "heif")] +// use libheif_rs::LibHeif; #[cfg(feature = "heif")] use libheif_rs::{ColorSpace, HeifContext, RgbChroma}; @@ -126,8 +128,10 @@ pub fn open_cache_folder(cache_file_name: &str, save_to_cache: bool, use_json: b #[cfg(feature = "heif")] pub fn get_dynamic_image_from_heic(path: &str) -> Result { + // let libheif = LibHeif::new(); let im = HeifContext::read_from_file(path)?; let handle = im.primary_image_handle()?; + // let image = libheif.decode(&handle, ColorSpace::Rgb(RgbChroma::Rgb), None)?; // Enable when using libheif 0.19 let image = handle.decode(ColorSpace::Rgb(RgbChroma::Rgb), None)?; let width = image.width(); let height = image.height(); diff --git a/czkawka_core/src/common_dir_traversal.rs b/czkawka_core/src/common_dir_traversal.rs index 462e6df..bf18029 100644 --- a/czkawka_core/src/common_dir_traversal.rs +++ b/czkawka_core/src/common_dir_traversal.rs @@ -30,6 +30,7 @@ pub struct ProgressData { pub enum CheckingMethod { None, Name, + SizeName, Size, Hash, } diff --git a/czkawka_core/src/duplicate.rs b/czkawka_core/src/duplicate.rs index 616b425..77edae1 100644 --- a/czkawka_core/src/duplicate.rs +++ b/czkawka_core/src/duplicate.rs @@ -11,7 +11,7 @@ use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; -use std::thread::sleep; +use std::thread::{sleep, JoinHandle}; use std::time::{Duration, SystemTime}; use std::{fs, mem, thread}; @@ -67,6 +67,8 @@ pub struct Info { pub number_of_duplicated_files_by_hash: usize, pub number_of_groups_by_name: usize, pub number_of_duplicated_files_by_name: usize, + pub number_of_groups_by_size_name: usize, + pub number_of_duplicated_files_by_size_name: usize, pub lost_space_by_size: u64, pub lost_space_by_hash: u64, } @@ -81,11 +83,13 @@ impl Info { pub struct DuplicateFinder { text_messages: Messages, information: Info, - files_with_identical_names: BTreeMap>, // File Size, File Entry - files_with_identical_size: BTreeMap>, // File Size, File Entry - files_with_identical_hashes: BTreeMap>>, // File Size, next grouped by file size, next grouped by hash - files_with_identical_names_referenced: BTreeMap)>, // File Size, File Entry - files_with_identical_size_referenced: BTreeMap)>, // File Size, File Entry + files_with_identical_names: BTreeMap>, // File Size, File Entry + files_with_identical_size_names: BTreeMap<(u64, String), Vec>, // File (Size, Name), File Entry + files_with_identical_size: BTreeMap>, // File Size, File Entry + files_with_identical_hashes: BTreeMap>>, // File Size, next grouped by file size, next grouped by hash + files_with_identical_names_referenced: BTreeMap)>, // File Size, File Entry + files_with_identical_size_names_referenced: BTreeMap<(u64, String), (FileEntry, Vec)>, // File (Size, Name), File Entry + files_with_identical_size_referenced: BTreeMap)>, // File Size, File Entry files_with_identical_hashes_referenced: BTreeMap)>>, // File Size, next grouped by file size, next grouped by hash directories: Directories, allowed_extensions: Extensions, @@ -116,8 +120,10 @@ impl DuplicateFinder { information: Info::new(), files_with_identical_names: Default::default(), files_with_identical_size: Default::default(), + files_with_identical_size_names: Default::default(), files_with_identical_hashes: Default::default(), files_with_identical_names_referenced: Default::default(), + files_with_identical_size_names_referenced: Default::default(), files_with_identical_size_referenced: Default::default(), files_with_identical_hashes_referenced: Default::default(), recursive_search: true, @@ -148,24 +154,30 @@ impl DuplicateFinder { match self.check_method { CheckingMethod::Name => { - if !self.check_files_name(stop_receiver, progress_sender) { - self.stopped_search = true; + self.stopped_search = !self.check_files_name(stop_receiver, progress_sender); // TODO restore this to name + if self.stopped_search { + return; + } + } + CheckingMethod::SizeName => { + self.stopped_search = !self.check_files_size_name(stop_receiver, progress_sender); + if self.stopped_search { return; } } CheckingMethod::Size => { - if !self.check_files_size(stop_receiver, progress_sender) { - self.stopped_search = true; + self.stopped_search = !self.check_files_size(stop_receiver, progress_sender); + if self.stopped_search { return; } } CheckingMethod::Hash => { - if !self.check_files_size(stop_receiver, progress_sender) { - self.stopped_search = true; + self.stopped_search = !self.check_files_size(stop_receiver, progress_sender); + if self.stopped_search { return; } - if !self.check_files_hash(stop_receiver, progress_sender) { - self.stopped_search = true; + self.stopped_search = !self.check_files_hash(stop_receiver, progress_sender); + if self.stopped_search { return; } } @@ -221,6 +233,11 @@ impl DuplicateFinder { &self.files_with_identical_size } + #[must_use] + pub const fn get_files_sorted_by_size_name(&self) -> &BTreeMap<(u64, String), Vec> { + &self.files_with_identical_size_names + } + #[must_use] pub const fn get_files_sorted_by_hash(&self) -> &BTreeMap>> { &self.files_with_identical_hashes @@ -319,6 +336,11 @@ impl DuplicateFinder { &self.files_with_identical_size_referenced } + #[must_use] + pub fn get_files_with_identical_size_names_referenced(&self) -> &BTreeMap<(u64, String), (FileEntry, Vec)> { + &self.files_with_identical_size_names_referenced + } + fn check_files_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender>) -> bool { let group_by_func = if self.case_sensitive_name_comparison { |fe: &FileEntry| fe.path.file_name().unwrap().to_string_lossy().to_string() @@ -388,18 +410,7 @@ impl DuplicateFinder { self.files_with_identical_names_referenced.insert(fe.path.to_string_lossy().to_string(), (fe, vec_fe)); } } - - if self.use_reference_folders { - for (_fe, vector) in self.files_with_identical_names_referenced.values() { - self.information.number_of_duplicated_files_by_name += vector.len(); - self.information.number_of_groups_by_name += 1; - } - } else { - for vector in self.files_with_identical_names.values() { - self.information.number_of_duplicated_files_by_name += vector.len() - 1; - self.information.number_of_groups_by_name += 1; - } - } + self.calculate_name_stats(); Common::print_time(start_time, SystemTime::now(), "check_files_name"); true @@ -411,6 +422,118 @@ impl DuplicateFinder { } } + fn calculate_name_stats(&mut self) { + if self.use_reference_folders { + for (_fe, vector) in self.files_with_identical_names_referenced.values() { + self.information.number_of_duplicated_files_by_name += vector.len(); + self.information.number_of_groups_by_name += 1; + } + } else { + for vector in self.files_with_identical_names.values() { + self.information.number_of_duplicated_files_by_name += vector.len() - 1; + self.information.number_of_groups_by_name += 1; + } + } + } + + fn check_files_size_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender>) -> bool { + let group_by_func = if self.case_sensitive_name_comparison { + |fe: &FileEntry| (fe.size, fe.path.file_name().unwrap().to_string_lossy().to_string()) + } else { + |fe: &FileEntry| (fe.size, fe.path.file_name().unwrap().to_string_lossy().to_lowercase()) + }; + + let result = DirTraversalBuilder::new() + .root_dirs(self.directories.included_directories.clone()) + .group_by(group_by_func) + .stop_receiver(stop_receiver) + .progress_sender(progress_sender) + .checking_method(CheckingMethod::Name) + .directories(self.directories.clone()) + .allowed_extensions(self.allowed_extensions.clone()) + .excluded_items(self.excluded_items.clone()) + .recursive_search(self.recursive_search) + .minimal_file_size(self.minimal_file_size) + .maximal_file_size(self.maximal_file_size) + .build() + .run(); + match result { + DirTraversalResult::SuccessFiles { + start_time, + grouped_file_entries, + warnings, + } => { + self.files_with_identical_size_names = grouped_file_entries; + self.text_messages.warnings.extend(warnings); + + // Create new BTreeMap without single size entries(files have not duplicates) + let mut new_map: BTreeMap<(u64, String), Vec> = Default::default(); + + for (name_size, vector) in &self.files_with_identical_size_names { + if vector.len() > 1 { + new_map.insert(name_size.clone(), vector.clone()); + } + } + self.files_with_identical_size_names = new_map; + + // Reference - only use in size, because later hash will be counted differently + if self.use_reference_folders { + let mut btree_map = Default::default(); + mem::swap(&mut self.files_with_identical_size_names, &mut btree_map); + let reference_directories = self.directories.reference_directories.clone(); + let vec = btree_map + .into_iter() + .filter_map(|(_size, vec_file_entry)| { + let mut files_from_referenced_folders = Vec::new(); + let mut normal_files = Vec::new(); + for file_entry in vec_file_entry { + if reference_directories.iter().any(|e| file_entry.path.starts_with(e)) { + files_from_referenced_folders.push(file_entry); + } else { + normal_files.push(file_entry); + } + } + + if files_from_referenced_folders.is_empty() || normal_files.is_empty() { + None + } else { + Some((files_from_referenced_folders.pop().unwrap(), normal_files)) + } + }) + .collect::)>>(); + for (fe, vec_fe) in vec { + self.files_with_identical_size_names_referenced + .insert((fe.size, fe.path.to_string_lossy().to_string()), (fe, vec_fe)); + } + } + self.calculate_size_name_stats(); + + Common::print_time(start_time, SystemTime::now(), "check_files_size_name"); + true + } + DirTraversalResult::SuccessFolders { .. } => { + unreachable!() + } + DirTraversalResult::Stopped => false, + } + } + + fn calculate_size_name_stats(&mut self) { + if self.use_reference_folders { + for ((size, _name), (_fe, vector)) in &self.files_with_identical_size_names_referenced { + self.information.number_of_duplicated_files_by_size_name += vector.len(); + self.information.number_of_groups_by_size_name += 1; + self.information.lost_space_by_size += (vector.len() as u64) * size; + } + } else { + for ((size, _name), vector) in &self.files_with_identical_size_names { + self.information.number_of_duplicated_files_by_size_name += vector.len() - 1; + self.information.number_of_groups_by_size_name += 1; + self.information.lost_space_by_size += (vector.len() as u64 - 1) * size; + } + } + } + /// Read file length and puts it to different boxes(each for different lengths) /// If in box is only 1 result, then it is removed fn check_files_size(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender>) -> bool { @@ -459,49 +582,8 @@ impl DuplicateFinder { } } - // Reference - only use in size, because later hash will be counted differently - if self.use_reference_folders && self.check_method == CheckingMethod::Size { - let mut btree_map = Default::default(); - mem::swap(&mut self.files_with_identical_size, &mut btree_map); - let reference_directories = self.directories.reference_directories.clone(); - let vec = btree_map - .into_iter() - .filter_map(|(_size, vec_file_entry)| { - let mut files_from_referenced_folders = Vec::new(); - let mut normal_files = Vec::new(); - for file_entry in vec_file_entry { - if reference_directories.iter().any(|e| file_entry.path.starts_with(e)) { - files_from_referenced_folders.push(file_entry); - } else { - normal_files.push(file_entry); - } - } - - if files_from_referenced_folders.is_empty() || normal_files.is_empty() { - None - } else { - Some((files_from_referenced_folders.pop().unwrap(), normal_files)) - } - }) - .collect::)>>(); - for (fe, vec_fe) in vec { - self.files_with_identical_size_referenced.insert(fe.size, (fe, vec_fe)); - } - } - - if self.use_reference_folders { - for (size, (_fe, vector)) in &self.files_with_identical_size_referenced { - self.information.number_of_duplicated_files_by_size += vector.len(); - self.information.number_of_groups_by_size += 1; - self.information.lost_space_by_size += (vector.len() as u64) * size; - } - } else { - for (size, vector) in &self.files_with_identical_size { - self.information.number_of_duplicated_files_by_size += vector.len() - 1; - self.information.number_of_groups_by_size += 1; - self.information.lost_space_by_size += (vector.len() as u64 - 1) * size; - } - } + self.filter_reference_folders_by_size(); + self.calculate_size_stats(); Common::print_time(start_time, SystemTime::now(), "check_files_size"); true @@ -513,35 +595,78 @@ impl DuplicateFinder { } } - /// The slowest checking type, which must be applied after checking for size - fn check_files_hash(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender>) -> bool { - assert_eq!(self.check_method, CheckingMethod::Hash); + fn calculate_size_stats(&mut self) { + if self.use_reference_folders { + for (size, (_fe, vector)) in &self.files_with_identical_size_referenced { + self.information.number_of_duplicated_files_by_size += vector.len(); + self.information.number_of_groups_by_size += 1; + self.information.lost_space_by_size += (vector.len() as u64) * size; + } + } else { + for (size, vector) in &self.files_with_identical_size { + self.information.number_of_duplicated_files_by_size += vector.len() - 1; + self.information.number_of_groups_by_size += 1; + self.information.lost_space_by_size += (vector.len() as u64 - 1) * size; + } + } + } - let check_type = Arc::new(self.hash_type); + /// This step check for references, only when checking for size. + /// This is needed, because later reference folders looks for hashes, not size + fn filter_reference_folders_by_size(&mut self) { + if self.use_reference_folders && self.check_method == CheckingMethod::Size { + let mut btree_map = Default::default(); + mem::swap(&mut self.files_with_identical_size, &mut btree_map); + let reference_directories = self.directories.reference_directories.clone(); + let vec = btree_map + .into_iter() + .filter_map(|(_size, vec_file_entry)| { + let mut files_from_referenced_folders = Vec::new(); + let mut normal_files = Vec::new(); + for file_entry in vec_file_entry { + if reference_directories.iter().any(|e| file_entry.path.starts_with(e)) { + files_from_referenced_folders.push(file_entry); + } else { + normal_files.push(file_entry); + } + } - let start_time: SystemTime = SystemTime::now(); - let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread - let mut pre_checked_map: BTreeMap> = Default::default(); + if files_from_referenced_folders.is_empty() || normal_files.is_empty() { + None + } else { + Some((files_from_referenced_folders.pop().unwrap(), normal_files)) + } + }) + .collect::)>>(); + for (fe, vec_fe) in vec { + self.files_with_identical_size_referenced.insert(fe.size, (fe, vec_fe)); + } + } + } - //// PROGRESS THREAD START - let progress_thread_run = Arc::new(AtomicBool::new(true)); - - let atomic_file_counter = Arc::new(AtomicUsize::new(0)); - - let progress_thread_handle = if let Some(progress_sender) = progress_sender { + // TODO Generalize this if possible with different tools + fn prepare_hash_thread_handler( + &self, + progress_sender: Option<&futures::channel::mpsc::UnboundedSender>, + progress_thread_run: Arc, + atomic_counter: Arc, + current_stage: u8, + max_stage: u8, + max_value: usize, + ) -> JoinHandle<()> { + if let Some(progress_sender) = progress_sender { let progress_send = progress_sender.clone(); - let progress_thread_run = progress_thread_run.clone(); - let atomic_file_counter = atomic_file_counter.clone(); - let files_to_check = self.files_with_identical_size.values().map(Vec::len).sum(); + let progress_thread_run = progress_thread_run; + let atomic_counter = atomic_counter; let checking_method = self.check_method; thread::spawn(move || loop { progress_send .unbounded_send(ProgressData { checking_method, - current_stage: 1, - max_stage: 2, - entries_checked: atomic_file_counter.load(Ordering::Relaxed), - entries_to_check: files_to_check, + current_stage, + max_stage, + entries_checked: atomic_counter.load(Ordering::Relaxed), + entries_to_check: max_value, }) .unwrap(); if !progress_thread_run.load(Ordering::Relaxed) { @@ -551,166 +676,174 @@ impl DuplicateFinder { }) } else { thread::spawn(|| {}) - }; + } + } - //// PROGRESS THREAD END + fn prehashing( + &mut self, + stop_receiver: Option<&Receiver<()>>, + progress_sender: Option<&futures::channel::mpsc::UnboundedSender>, + pre_checked_map: &mut BTreeMap>, + ) -> Option<()> { + let start_time: SystemTime = SystemTime::now(); + let check_type = self.hash_type; + let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread - ///////////////////////////////////////////////////////////////////////////// PREHASHING START - { - let loaded_hash_map; - let mut records_already_cached: BTreeMap> = Default::default(); - let mut non_cached_files_to_check: BTreeMap> = Default::default(); + let progress_thread_run = Arc::new(AtomicBool::new(true)); + let atomic_file_counter = Arc::new(AtomicUsize::new(0)); + let progress_thread_handle = self.prepare_hash_thread_handler( + progress_sender, + progress_thread_run.clone(), + atomic_file_counter.clone(), + 1, + 2, + self.files_with_identical_size.values().map(Vec::len).sum(), + ); - // Cache algorithm - // - Load data from cache - // - Convert from BT> to BT - // - Save to proper values - if self.use_prehash_cache { - loaded_hash_map = match load_hashes_from_file(&mut self.text_messages, self.delete_outdated_cache, &self.hash_type, true) { - Some(t) => t, - None => Default::default(), - }; + let loaded_hash_map; + let mut records_already_cached: BTreeMap> = Default::default(); + let mut non_cached_files_to_check: BTreeMap> = Default::default(); - let mut loaded_hash_map2: BTreeMap = Default::default(); - for vec_file_entry in loaded_hash_map.values() { - for file_entry in vec_file_entry { - loaded_hash_map2.insert(file_entry.path.to_string_lossy().to_string(), file_entry.clone()); + // Cache algorithm + // - Load data from cache + // - Convert from BT> to BT + // - Save to proper values + if self.use_prehash_cache { + loaded_hash_map = match load_hashes_from_file(&mut self.text_messages, self.delete_outdated_cache, &self.hash_type, true) { + Some(t) => t, + None => Default::default(), + }; + + let mut loaded_hash_map2: BTreeMap = Default::default(); + for vec_file_entry in loaded_hash_map.values() { + for file_entry in vec_file_entry { + loaded_hash_map2.insert(file_entry.path.to_string_lossy().to_string(), file_entry.clone()); + } + } + + #[allow(clippy::if_same_then_else)] + for vec_file_entry in self.files_with_identical_size.values() { + for file_entry in vec_file_entry { + let name = file_entry.path.to_string_lossy().to_string(); + if !loaded_hash_map2.contains_key(&name) { + // If loaded data doesn't contains current image info + non_cached_files_to_check.entry(file_entry.size).or_insert_with(Vec::new).push(file_entry.clone()); + } else if file_entry.size != loaded_hash_map2.get(&name).unwrap().size || file_entry.modified_date != loaded_hash_map2.get(&name).unwrap().modified_date { + // When size or modification date of image changed, then it is clear that is different image + non_cached_files_to_check.entry(file_entry.size).or_insert_with(Vec::new).push(file_entry.clone()); + } else { + // Checking may be omitted when already there is entry with same size and modification date + records_already_cached.entry(file_entry.size).or_insert_with(Vec::new).push(file_entry.clone()); } } + } + } else { + loaded_hash_map = Default::default(); + mem::swap(&mut self.files_with_identical_size, &mut non_cached_files_to_check); + } - #[allow(clippy::if_same_then_else)] - for vec_file_entry in self.files_with_identical_size.values() { - for file_entry in vec_file_entry { - let name = file_entry.path.to_string_lossy().to_string(); - if !loaded_hash_map2.contains_key(&name) { - // If loaded data doesn't contains current image info - non_cached_files_to_check.entry(file_entry.size).or_insert_with(Vec::new).push(file_entry.clone()); - } else if file_entry.size != loaded_hash_map2.get(&name).unwrap().size || file_entry.modified_date != loaded_hash_map2.get(&name).unwrap().modified_date { - // When size or modification date of image changed, then it is clear that is different image - non_cached_files_to_check.entry(file_entry.size).or_insert_with(Vec::new).push(file_entry.clone()); - } else { - // Checking may be omitted when already there is entry with same size and modification date - records_already_cached.entry(file_entry.size).or_insert_with(Vec::new).push(file_entry.clone()); + #[allow(clippy::type_complexity)] + let pre_hash_results: Vec<(u64, BTreeMap>, Vec)> = non_cached_files_to_check + .par_iter() + .map(|(size, vec_file_entry)| { + let mut hashmap_with_hash: BTreeMap> = Default::default(); + let mut errors: Vec = Vec::new(); + let mut buffer = [0u8; 1024 * 2]; + + atomic_file_counter.fetch_add(vec_file_entry.len(), Ordering::Relaxed); + for file_entry in vec_file_entry { + if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { + check_was_stopped.store(true, Ordering::Relaxed); + return None; + } + match hash_calculation(&mut buffer, file_entry, &check_type, 0) { + Ok(hash_string) => { + hashmap_with_hash.entry(hash_string.clone()).or_insert_with(Vec::new).push(file_entry.clone()); } + Err(s) => errors.push(s), } } - } else { - loaded_hash_map = Default::default(); - mem::swap(&mut self.files_with_identical_size, &mut non_cached_files_to_check); + Some((*size, hashmap_with_hash, errors)) + }) + .while_some() + .collect(); + + // End thread which send info to gui + progress_thread_run.store(false, Ordering::Relaxed); + progress_thread_handle.join().unwrap(); + + // Check if user aborted search(only from GUI) + if check_was_stopped.load(Ordering::Relaxed) { + return None; + } + + // Add data from cache + for (size, vec_file_entry) in &records_already_cached { + pre_checked_map.entry(*size).or_insert_with(Vec::new).append(&mut vec_file_entry.clone()); + } + + // Check results + for (size, hash_map, errors) in &pre_hash_results { + self.text_messages.warnings.append(&mut errors.clone()); + for vec_file_entry in hash_map.values() { + if vec_file_entry.len() > 1 { + pre_checked_map.entry(*size).or_insert_with(Vec::new).append(&mut vec_file_entry.clone()); + } } + } - #[allow(clippy::type_complexity)] - let pre_hash_results: Vec<(u64, BTreeMap>, Vec)> = non_cached_files_to_check - .par_iter() - .map(|(size, vec_file_entry)| { - let mut hashmap_with_hash: BTreeMap> = Default::default(); - let mut errors: Vec = Vec::new(); - let mut buffer = [0u8; 1024 * 2]; + if self.use_prehash_cache { + // All results = records already cached + computed results + let mut save_cache_to_hashmap: BTreeMap = Default::default(); - atomic_file_counter.fetch_add(vec_file_entry.len(), Ordering::Relaxed); + for (size, vec_file_entry) in loaded_hash_map { + if size >= self.minimal_prehash_cache_file_size { for file_entry in vec_file_entry { - if stop_receiver.is_some() && stop_receiver.unwrap().try_recv().is_ok() { - check_was_stopped.store(true, Ordering::Relaxed); - return None; - } - match hash_calculation(&mut buffer, file_entry, &check_type, 0) { - Ok(hash_string) => { - hashmap_with_hash.entry(hash_string.clone()).or_insert_with(Vec::new).push(file_entry.clone()); - } - Err(s) => errors.push(s), - } - } - Some((*size, hashmap_with_hash, errors)) - }) - .while_some() - .collect(); - - // End thread which send info to gui - progress_thread_run.store(false, Ordering::Relaxed); - progress_thread_handle.join().unwrap(); - - // Check if user aborted search(only from GUI) - if check_was_stopped.load(Ordering::Relaxed) { - return false; - } - - // Add data from cache - for (size, vec_file_entry) in &records_already_cached { - pre_checked_map.entry(*size).or_insert_with(Vec::new).append(&mut vec_file_entry.clone()); - } - - // Check results - for (size, hash_map, errors) in &pre_hash_results { - self.text_messages.warnings.append(&mut errors.clone()); - for vec_file_entry in hash_map.values() { - if vec_file_entry.len() > 1 { - pre_checked_map.entry(*size).or_insert_with(Vec::new).append(&mut vec_file_entry.clone()); + save_cache_to_hashmap.insert(file_entry.path.to_string_lossy().to_string(), file_entry.clone()); } } } - if self.use_prehash_cache { - // All results = records already cached + computed results - let mut save_cache_to_hashmap: BTreeMap = Default::default(); - - for (size, vec_file_entry) in loaded_hash_map { - if size >= self.minimal_prehash_cache_file_size { + for (size, hash_map, _errors) in &pre_hash_results { + if *size >= self.minimal_prehash_cache_file_size { + for vec_file_entry in hash_map.values() { for file_entry in vec_file_entry { save_cache_to_hashmap.insert(file_entry.path.to_string_lossy().to_string(), file_entry.clone()); } } } - - for (size, hash_map, _errors) in &pre_hash_results { - if *size >= self.minimal_prehash_cache_file_size { - for vec_file_entry in hash_map.values() { - for file_entry in vec_file_entry { - save_cache_to_hashmap.insert(file_entry.path.to_string_lossy().to_string(), file_entry.clone()); - } - } - } - } - - save_hashes_to_file(&save_cache_to_hashmap, &mut self.text_messages, &self.hash_type, true, self.minimal_prehash_cache_file_size); } + + save_hashes_to_file(&save_cache_to_hashmap, &mut self.text_messages, &self.hash_type, true, self.minimal_prehash_cache_file_size); } - ///////////////////////////////////////////////////////////////////////////// PREHASHING END - Common::print_time(start_time, SystemTime::now(), "check_files_hash - prehash"); + + Some(()) + } + + fn full_hashing( + &mut self, + stop_receiver: Option<&Receiver<()>>, + progress_sender: Option<&futures::channel::mpsc::UnboundedSender>, + mut pre_checked_map: BTreeMap>, + ) -> Option<()> { + let check_was_stopped = AtomicBool::new(false); // Used for breaking from GUI and ending check thread + + let check_type = self.hash_type; let start_time: SystemTime = SystemTime::now(); - - ///////////////////////// - //// PROGRESS THREAD START let progress_thread_run = Arc::new(AtomicBool::new(true)); - let atomic_file_counter = Arc::new(AtomicUsize::new(0)); - let progress_thread_handle = if let Some(progress_sender) = progress_sender { - let progress_send = progress_sender.clone(); - let progress_thread_run = progress_thread_run.clone(); - let atomic_file_counter = atomic_file_counter.clone(); - let files_to_check = pre_checked_map.values().map(Vec::len).sum(); - let checking_method = self.check_method; - thread::spawn(move || loop { - progress_send - .unbounded_send(ProgressData { - checking_method, - current_stage: 2, - max_stage: 2, - entries_checked: atomic_file_counter.load(Ordering::Relaxed), - entries_to_check: files_to_check, - }) - .unwrap(); - if !progress_thread_run.load(Ordering::Relaxed) { - break; - } - sleep(Duration::from_millis(LOOP_DURATION as u64)); - }) - } else { - thread::spawn(|| {}) - }; + let progress_thread_handle = self.prepare_hash_thread_handler( + progress_sender, + progress_thread_run.clone(), + atomic_file_counter.clone(), + 2, + 2, + pre_checked_map.values().map(Vec::len).sum(), + ); //// PROGRESS THREAD END @@ -828,7 +961,7 @@ impl DuplicateFinder { // Break if stop was clicked after saving to cache if check_was_stopped.load(Ordering::Relaxed) { - return false; + return None; } for (size, hash_map, mut errors) in full_hash_results { @@ -840,9 +973,11 @@ impl DuplicateFinder { } } } + Common::print_time(start_time, SystemTime::now(), "delete_files"); + Some(()) + } - ///////////////////////////////////////////////////////////////////////////// HASHING END - + fn hash_reference_folders(&mut self) { // Reference - only use in size, because later hash will be counted differently if self.use_reference_folders { let mut btree_map = Default::default(); @@ -897,8 +1032,24 @@ impl DuplicateFinder { } } } + } - Common::print_time(start_time, SystemTime::now(), "check_files_hash - full hash"); + /// The slowest checking type, which must be applied after checking for size + fn check_files_hash(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&futures::channel::mpsc::UnboundedSender>) -> bool { + assert_eq!(self.check_method, CheckingMethod::Hash); + + let mut pre_checked_map: BTreeMap> = Default::default(); + let ret = self.prehashing(stop_receiver, progress_sender, &mut pre_checked_map); + if ret.is_none() { + return false; + } + + let ret = self.full_hashing(stop_receiver, progress_sender, pre_checked_map); + if ret.is_none() { + return false; + } + + self.hash_reference_folders(); // Clean unused data self.files_with_identical_size = Default::default(); @@ -920,6 +1071,11 @@ impl DuplicateFinder { let _tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun); } } + CheckingMethod::SizeName => { + for vector in self.files_with_identical_size_names.values() { + let _tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun); + } + } CheckingMethod::Hash => { for vector_vectors in self.files_with_identical_hashes.values() { for vector in vector_vectors.iter() { @@ -1053,6 +1209,30 @@ impl SaveResults for DuplicateFinder { write!(writer, "Not found any files with same names.").unwrap(); } } + CheckingMethod::SizeName => { + if !self.files_with_identical_names.is_empty() { + writeln!( + writer, + "-------------------------------------------------Files with same size and names-------------------------------------------------" + ) + .unwrap(); + writeln!( + writer, + "Found {} files in {} groups with same size and name(may have different content)", + self.information.number_of_duplicated_files_by_size_name, self.information.number_of_groups_by_size_name, + ) + .unwrap(); + for ((size, name), vector) in self.files_with_identical_size_names.iter().rev() { + writeln!(writer, "Name - {}, {} - {} files ", name, format_size(*size, BINARY), vector.len()).unwrap(); + for j in vector { + writeln!(writer, "{}", j.path.display()).unwrap(); + } + writeln!(writer).unwrap(); + } + } else { + write!(writer, "Not found any files with same size and names.").unwrap(); + } + } CheckingMethod::Size => { if !self.files_with_identical_size.is_empty() { writeln!( @@ -1137,6 +1317,20 @@ impl PrintResults for DuplicateFinder { println!(); } } + CheckingMethod::SizeName => { + for i in &self.files_with_identical_size_names { + number_of_files += i.1.len() as u64; + number_of_groups += 1; + } + println!("Found {number_of_files} files in {number_of_groups} groups with same size and name(may have different content)",); + for ((size, name), vector) in &self.files_with_identical_size_names { + println!("Name - {}, {} - {} files ", name, format_size(*size, BINARY), vector.len()); + for j in vector { + println!("{}", j.path.display()); + } + println!(); + } + } CheckingMethod::Hash => { for vector in self.files_with_identical_hashes.values() { for j in vector { diff --git a/czkawka_core/src/same_music.rs b/czkawka_core/src/same_music.rs index d828454..83ccf5d 100644 --- a/czkawka_core/src/same_music.rs +++ b/czkawka_core/src/same_music.rs @@ -31,6 +31,7 @@ pub enum DeleteMethod { } bitflags! { + #[derive(PartialEq, Copy, Clone, Debug)] pub struct MusicSimilarity : u32 { const NONE = 0; diff --git a/czkawka_core/src/similar_images.rs b/czkawka_core/src/similar_images.rs index 83c6559..9f52409 100644 --- a/czkawka_core/src/similar_images.rs +++ b/czkawka_core/src/similar_images.rs @@ -965,7 +965,7 @@ impl SimilarImages { let mut found = false; for vec_file_entry in collected_similar_images.values() { if vec_file_entry.is_empty() { - println!("Empty Element {vec_file_entry:?}"); + println!("Empty group"); found = true; continue; } @@ -984,7 +984,7 @@ impl SimilarImages { } } } - assert!(!found, "Found Invalid entries"); + assert!(!found, "Found Invalid entries, verify errors before"); // TODO crashes with empty result with reference folder, verify why } self.similar_vectors = collected_similar_images.into_values().collect(); diff --git a/czkawka_gui/Cargo.toml b/czkawka_gui/Cargo.toml index c6f30ec..ba16383 100644 --- a/czkawka_gui/Cargo.toml +++ b/czkawka_gui/Cargo.toml @@ -10,29 +10,29 @@ homepage = "https://github.com/qarmin/czkawka" repository = "https://github.com/qarmin/czkawka" [dependencies] -gdk4 = "0.6.2" -glib = "0.17.2" +gdk4 = "0.6.3" +glib = "0.17.5" humansize = "2.1.3" -chrono = "0.4.23" +chrono = "0.4.24" # Used for sending stop signal across threads crossbeam-channel = "0.5.7" # To get information about progress -futures = "0.3.26" +futures = "0.3.28" # For saving/loading config files to specific directories directories-next = "2.0.0" # For opening files -open = "3.2.0" +open = "4.0.1" # To get image preview -image = "0.24.5" +image = "0.24.6" # To be able to use custom select -regex = "1.7.1" +regex = "1.7.3" # To get image_hasher types image_hasher = "1.1.2" @@ -45,15 +45,15 @@ fs_extra = "1.3.0" # Language i18n-embed = { version = "0.13.8", features = ["fluent-system", "desktop-requester"] } -i18n-embed-fl = "0.6.5" -rust-embed = "6.6.0" +i18n-embed-fl = "0.6.6" +rust-embed = "6.6.1" once_cell = "1.17.1" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = ["combaseapi", "objbase", "shobjidl_core", "windef", "winerror", "wtypesbase", "winuser"] } [dependencies.gtk4] -version = "0.6.2" +version = "0.6.4" default-features = false features = ["v4_6"] diff --git a/czkawka_gui/i18n/en/czkawka_gui.ftl b/czkawka_gui/i18n/en/czkawka_gui.ftl index a0074f2..f645088 100644 --- a/czkawka_gui/i18n/en/czkawka_gui.ftl +++ b/czkawka_gui/i18n/en/czkawka_gui.ftl @@ -28,6 +28,7 @@ duplicate_case_sensitive_name_tooltip = Disabling such option will group names without checking if each letter is same size e.g. żoŁD <-> Żołd +duplicate_mode_size_name_combo_box = Size and Name duplicate_mode_name_combo_box = Name duplicate_mode_size_combo_box = Size duplicate_mode_hash_combo_box = Hash @@ -447,6 +448,7 @@ progress_scanning_music_tags_end = Comparing tags of {$file_checked}/{$all_files progress_scanning_music_tags = Reading tags of {$file_checked}/{$all_files} music file progress_scanning_empty_folders = Scanning {$folder_number} folder progress_scanning_size = Scanning size of {$file_number} file +progress_scanning_size_name = Scanning name and size of {$file_number} file progress_scanning_name = Scanning name of {$file_number} file progress_analyzed_partial_hash = Analyzed partial hash of {$file_checked}/{$all_files} files progress_analyzed_full_hash = Analyzed full hash of {$file_checked}/{$all_files} files diff --git a/czkawka_gui/src/compute_results.rs b/czkawka_gui/src/compute_results.rs index 550f417..e1f3a8e 100644 --- a/czkawka_gui/src/compute_results.rs +++ b/czkawka_gui/src/compute_results.rs @@ -6,20 +6,32 @@ use std::rc::Rc; use chrono::NaiveDateTime; use glib::Receiver; use gtk4::prelude::*; +use gtk4::{Entry, ListStore, TextView, TreeView, Widget}; use humansize::format_size; use humansize::BINARY; +use czkawka_core::bad_extensions::BadExtensions; +use czkawka_core::big_file::BigFile; +use czkawka_core::broken_files::BrokenFiles; use czkawka_core::common::split_path; -use czkawka_core::common_dir_traversal::CheckingMethod; +use czkawka_core::common_dir_traversal::{CheckingMethod, FileEntry}; +use czkawka_core::duplicate::DuplicateFinder; +use czkawka_core::empty_files::EmptyFiles; +use czkawka_core::empty_folder::EmptyFolder; +use czkawka_core::invalid_symlinks::InvalidSymlinks; use czkawka_core::localizer_core::generate_translation_hashmap; -use czkawka_core::same_music::MusicSimilarity; +use czkawka_core::same_music::{MusicSimilarity, SameMusic}; use czkawka_core::similar_images; +use czkawka_core::similar_images::SimilarImages; +use czkawka_core::similar_videos::SimilarVideos; +use czkawka_core::temporary::Temporary; use crate::flg; use crate::gui_structs::gui_data::GuiData; use crate::help_combo_box::IMAGES_HASH_SIZE_COMBO_BOX; use crate::help_functions::*; use crate::notebook_enums::*; +use crate::notebook_info::NOTEBOOKS_INFO; use crate::opening_selecting_records::*; pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver) { @@ -79,1502 +91,137 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver< match msg { Message::Duplicates(df) => { - const COLUMNS_NUMBER: usize = 11; - if df.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - if df.get_use_reference() { - tree_view_duplicate_finder.selection().set_select_function(select_function_always_true); - } else { - tree_view_duplicate_finder.selection().set_select_function(select_function_duplicates); - } - - let information = df.get_information(); - let text_messages = df.get_text_messages(); - - let duplicates_number: usize; - let duplicates_size: u64; - let duplicates_group: usize; - - match df.get_check_method() { - CheckingMethod::Name => { - duplicates_number = information.number_of_duplicated_files_by_name; - // duplicates_size = 0; - duplicates_group = information.number_of_groups_by_name; - entry_info.set_text( - flg!( - "compute_found_duplicates_name", - generate_translation_hashmap(vec![("number_files", duplicates_number.to_string()), ("number_groups", duplicates_group.to_string())]) - ) - .as_str(), - ); - } - 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; - entry_info.set_text( - flg!( - "compute_found_duplicates_hash_size", - generate_translation_hashmap(vec![ - ("number_files", duplicates_number.to_string()), - ("number_groups", duplicates_group.to_string()), - ("size", format_size(duplicates_size, BINARY)) - ]) - ) - .as_str(), - ); - } - 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; - entry_info.set_text( - flg!( - "compute_found_duplicates_hash_size", - generate_translation_hashmap(vec![ - ("number_files", duplicates_number.to_string()), - ("number_groups", duplicates_group.to_string()), - ("size", format_size(duplicates_size, BINARY)) - ]) - ) - .as_str(), - ); - } - CheckingMethod::None => { - panic!(); - } - } - - // Create GUI - { - let list_store = get_list_store(&tree_view_duplicate_finder); - - if df.get_use_reference() { - match df.get_check_method() { - CheckingMethod::Name => { - let btreemap = df.get_files_with_identical_name_referenced(); - - for (_name, (base_file_entry, vector)) in btreemap.iter().rev() { - // Sort - let vector = if vector.len() >= 2 { - let mut vector = vector.clone(); - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - vector - } else { - vector.clone() - }; - - // HEADER - let (directory, file) = split_path(&base_file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &false), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&format_size(base_file_entry.size, BINARY))), - (ColumnsDuplicates::SizeAsBytes as u32, &base_file_entry.size), - (ColumnsDuplicates::Name as u32, &file), - (ColumnsDuplicates::Path as u32, &directory), - ( - ColumnsDuplicates::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(base_file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsDuplicates::ModificationAsSecs as u32, &(base_file_entry.modified_date)), - (ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &true), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - - list_store.set(&list_store.append(), &values); - - // MEAT - for entry in vector { - let (directory, file) = split_path(&entry.path); - println!("{}", entry.size); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &true), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&format_size(entry.size, BINARY))), - (ColumnsDuplicates::SizeAsBytes as u32, &entry.size), - (ColumnsDuplicates::Name as u32, &file), - (ColumnsDuplicates::Path as u32, &directory), - ( - ColumnsDuplicates::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsDuplicates::ModificationAsSecs as u32, &(entry.modified_date)), - (ColumnsDuplicates::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &false), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - } - } - } - CheckingMethod::Hash => { - let btreemap = df.get_files_with_identical_hashes_referenced(); - - for (_size, vectors_vector) in btreemap.iter().rev() { - for (base_file_entry, vector) in vectors_vector { - // Sort - let vector = if vector.len() >= 2 { - let mut vector = vector.clone(); - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - vector - } else { - vector.clone() - }; - - // HEADER - let (directory, file) = split_path(&base_file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &false), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&format_size(base_file_entry.size, BINARY))), - (ColumnsDuplicates::SizeAsBytes as u32, &base_file_entry.size), - (ColumnsDuplicates::Name as u32, &file), - (ColumnsDuplicates::Path as u32, &directory), - ( - ColumnsDuplicates::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(base_file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsDuplicates::ModificationAsSecs as u32, &(base_file_entry.modified_date)), - (ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &true), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - - // MEAT - list_store.set(&list_store.append(), &values); - for entry in vector { - let (directory, file) = split_path(&entry.path); - - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &true), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&format_size(entry.size, BINARY))), - (ColumnsDuplicates::SizeAsBytes as u32, &entry.size), - (ColumnsDuplicates::Name as u32, &file), - (ColumnsDuplicates::Path as u32, &directory), - ( - ColumnsDuplicates::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsDuplicates::ModificationAsSecs as u32, &(entry.modified_date)), - (ColumnsDuplicates::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &false), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - - list_store.set(&list_store.append(), &values); - } - } - } - } - CheckingMethod::Size => { - let btreemap = df.get_files_with_identical_size_referenced(); - - for (_size, (base_file_entry, vector)) in btreemap.iter().rev() { - // Sort - let vector = if vector.len() >= 2 { - let mut vector = vector.clone(); - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - vector - } else { - vector.clone() - }; - - // HEADER - let (directory, file) = split_path(&base_file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &false), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&format_size(base_file_entry.size, BINARY))), - (ColumnsDuplicates::SizeAsBytes as u32, &base_file_entry.size), - (ColumnsDuplicates::Name as u32, &file), - (ColumnsDuplicates::Path as u32, &directory), - ( - ColumnsDuplicates::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(base_file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsDuplicates::ModificationAsSecs as u32, &(base_file_entry.modified_date)), - (ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &true), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - - // MEAT - list_store.set(&list_store.append(), &values); - for entry in vector { - let (directory, file) = split_path(&entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &true), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&format_size(entry.size, BINARY))), - (ColumnsDuplicates::SizeAsBytes as u32, &entry.size), - (ColumnsDuplicates::Name as u32, &file), - (ColumnsDuplicates::Path as u32, &directory), - ( - ColumnsDuplicates::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsDuplicates::ModificationAsSecs as u32, &(entry.modified_date)), - (ColumnsDuplicates::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &false), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - } - } - } - CheckingMethod::None => { - panic!(); - } - } - } else { - match df.get_check_method() { - CheckingMethod::Name => { - let btreemap = df.get_files_sorted_by_names(); - - for (_name, vector) in btreemap.iter().rev() { - // Sort - let vector = if vector.len() >= 2 { - let mut vector = vector.clone(); - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - vector - } else { - vector.clone() - }; - - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &false), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&String::new())), - (ColumnsDuplicates::SizeAsBytes as u32, &0), - (ColumnsDuplicates::Name as u32, (&String::new())), - (ColumnsDuplicates::Path as u32, (&(format!("{} results", vector.len())))), - (ColumnsDuplicates::Modification as u32, (&String::new())), // No text in 3 column - (ColumnsDuplicates::ModificationAsSecs as u32, (&(0))), // Not used here - (ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &true), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - - list_store.set(&list_store.append(), &values); - for entry in vector { - let (directory, file) = split_path(&entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &true), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&format_size(entry.size, BINARY))), - (ColumnsDuplicates::SizeAsBytes as u32, &entry.size), - (ColumnsDuplicates::Name as u32, &file), - (ColumnsDuplicates::Path as u32, &directory), - ( - ColumnsDuplicates::Modification as u32, - &(format!( - "{} - ({})", - NaiveDateTime::from_timestamp_opt(entry.modified_date as i64, 0).unwrap(), - format_size(entry.size, BINARY) - )), - ), - (ColumnsDuplicates::ModificationAsSecs as u32, &(entry.modified_date)), - (ColumnsDuplicates::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &false), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - } - } - } - CheckingMethod::Hash => { - let btreemap = df.get_files_sorted_by_hash(); - - for (_size, vectors_vector) in btreemap.iter().rev() { - for vector in vectors_vector { - // Sort - let vector = if vector.len() >= 2 { - let mut vector = vector.clone(); - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - vector - } else { - vector.clone() - }; - - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &false), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&String::new())), - (ColumnsDuplicates::SizeAsBytes as u32, &0), - (ColumnsDuplicates::Name as u32, (&String::new())), - (ColumnsDuplicates::Path as u32, (&String::new())), - (ColumnsDuplicates::Modification as u32, &String::new()), // No text in 3 column - (ColumnsDuplicates::ModificationAsSecs as u32, &(0)), - (ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &true), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - - list_store.set(&list_store.append(), &values); - for entry in vector { - let (directory, file) = split_path(&entry.path); - - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &true), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&format_size(entry.size, BINARY))), - (ColumnsDuplicates::SizeAsBytes as u32, &entry.size), - (ColumnsDuplicates::Name as u32, &file), - (ColumnsDuplicates::Path as u32, &directory), - ( - ColumnsDuplicates::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsDuplicates::ModificationAsSecs as u32, &(entry.modified_date)), - (ColumnsDuplicates::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &false), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - - list_store.set(&list_store.append(), &values); - } - } - } - } - CheckingMethod::Size => { - let btreemap = df.get_files_sorted_by_size(); - - for (_size, vector) in btreemap.iter().rev() { - // Sort - let vector = if vector.len() >= 2 { - let mut vector = vector.clone(); - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - vector - } else { - vector.clone() - }; - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &false), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&String::new())), - (ColumnsDuplicates::SizeAsBytes as u32, &0), - (ColumnsDuplicates::Name as u32, (&String::new())), - (ColumnsDuplicates::Path as u32, (&String::new())), - (ColumnsDuplicates::Modification as u32, &String::new()), // No text in 3 column - (ColumnsDuplicates::ModificationAsSecs as u32, &(0)), // Not used here - (ColumnsDuplicates::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &true), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - - list_store.set(&list_store.append(), &values); - for entry in vector { - let (directory, file) = split_path(&entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsDuplicates::ActivatableSelectButton as u32, &true), - (ColumnsDuplicates::SelectionButton as u32, &false), - (ColumnsDuplicates::Size as u32, (&format_size(entry.size, BINARY))), - (ColumnsDuplicates::SizeAsBytes as u32, &entry.size), - (ColumnsDuplicates::Name as u32, &file), - (ColumnsDuplicates::Path as u32, &directory), - ( - ColumnsDuplicates::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsDuplicates::ModificationAsSecs as u32, &(entry.modified_date)), - (ColumnsDuplicates::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsDuplicates::IsHeader as u32, &false), - (ColumnsDuplicates::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - } - } - } - CheckingMethod::None => { - panic!(); - } - } - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_duplication_state.borrow_mut() = df; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::Duplicate, - &[ - BottomButtonsEnum::Save, - BottomButtonsEnum::Delete, - BottomButtonsEnum::Select, - BottomButtonsEnum::Sort, - BottomButtonsEnum::Symlink, - BottomButtonsEnum::Hardlink, - BottomButtonsEnum::Move, - ], - duplicates_number > 0, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::Duplicate).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_duplicate_finder( + df, + &entry_info, + &tree_view_duplicate_finder, + &text_view_errors, + &shared_duplication_state, + &shared_buttons, + &buttons_array, + &buttons_names, + ); } Message::EmptyFolders(ef) => { - const COLUMNS_NUMBER: usize = 5; - if ef.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - let information = ef.get_information(); - let text_messages = ef.get_text_messages(); - - let empty_folder_number: usize = information.number_of_empty_folders; - - entry_info.set_text( - flg!( - "compute_found_empty_folders", - generate_translation_hashmap(vec![("number_files", empty_folder_number.to_string()),]) - ) - .as_str(), - ); - - // Create GUI - { - let list_store = get_list_store(&tree_view_empty_folder_finder); - - let hashmap = ef.get_empty_folder_list(); - let mut vector = hashmap.keys().cloned().collect::>(); - - vector.sort_unstable_by_key(|e| { - let t = split_path(e.as_path()); - (t.0, t.1) - }); - - for path in vector { - let (directory, file) = split_path(&path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsEmptyFolders::SelectionButton as u32, &false), - (ColumnsEmptyFolders::Name as u32, &file), - (ColumnsEmptyFolders::Path as u32, &directory), - ( - ColumnsEmptyFolders::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(hashmap.get(&path).unwrap().modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsEmptyFolders::ModificationAsSecs as u32, &(hashmap.get(&path).unwrap().modified_date)), - ]; - list_store.set(&list_store.append(), &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_empty_folders_state.borrow_mut() = ef; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::EmptyDirectories, - &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], - empty_folder_number > 0, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::EmptyDirectories).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_empty_folders( + ef, + &entry_info, + &tree_view_empty_folder_finder, + &text_view_errors, + &shared_empty_folders_state, + &shared_buttons, + &buttons_array, + &buttons_names, + ); } Message::EmptyFiles(vf) => { - const COLUMNS_NUMBER: usize = 5; - if vf.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - let information = vf.get_information(); - let text_messages = vf.get_text_messages(); - - let empty_files_number: usize = information.number_of_empty_files; - - entry_info.set_text( - flg!( - "compute_found_empty_files", - generate_translation_hashmap(vec![("number_files", empty_files_number.to_string()),]) - ) - .as_str(), - ); - - // Create GUI - { - let list_store = get_list_store(&tree_view_empty_files_finder); - - let vector = vf.get_empty_files(); - - // Sort - let mut vector = vector.clone(); - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - - for file_entry in vector { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsEmptyFiles::SelectionButton as u32, &false), - (ColumnsEmptyFiles::Name as u32, &file), - (ColumnsEmptyFiles::Path as u32, &directory), - ( - ColumnsEmptyFiles::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsEmptyFiles::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), - ]; - list_store.set(&list_store.append(), &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_empty_files_state.borrow_mut() = vf; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::EmptyFiles, - &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], - empty_files_number > 0, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::EmptyFiles).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_empty_files( + vf, + &entry_info, + &tree_view_empty_files_finder, + &text_view_errors, + &shared_empty_files_state, + &shared_buttons, + &buttons_array, + &buttons_names, + ); } Message::BigFiles(bf) => { - const COLUMNS_NUMBER: usize = 7; - if bf.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - let information = bf.get_information(); - let text_messages = bf.get_text_messages(); - - let biggest_files_number: usize = information.number_of_real_files; - - entry_info.set_text( - flg!( - "compute_found_big_files", - generate_translation_hashmap(vec![("number_files", biggest_files_number.to_string()),]) - ) - .as_str(), - ); - - // Create GUI - { - let list_store = get_list_store(&tree_view_big_files_finder); - - let vector = bf.get_big_files(); - - for (size, file_entry) in vector.iter() { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsBigFiles::SelectionButton as u32, &false), - (ColumnsBigFiles::Size as u32, &(format_size(*size, BINARY))), - (ColumnsBigFiles::Name as u32, &file), - (ColumnsBigFiles::Path as u32, &directory), - ( - ColumnsBigFiles::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsBigFiles::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), - (ColumnsBigFiles::SizeAsBytes as u32, &(size)), - ]; - list_store.set(&list_store.append(), &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_big_files_state.borrow_mut() = bf; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::BigFiles, - &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], - biggest_files_number > 0, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::BigFiles).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_big_files( + bf, + &entry_info, + &tree_view_big_files_finder, + &text_view_errors, + &shared_big_files_state, + &shared_buttons, + &buttons_array, + &buttons_names, + ); } Message::Temporary(tf) => { - const COLUMNS_NUMBER: usize = 5; - if tf.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - let information = tf.get_information(); - let text_messages = tf.get_text_messages(); - - let temporary_files_number: usize = information.number_of_temporary_files; - entry_info.set_text( - flg!( - "compute_found_temporary_files", - generate_translation_hashmap(vec![("number_files", temporary_files_number.to_string()),]) - ) - .as_str(), - ); - - // Create GUI - { - let list_store = get_list_store(&tree_view_temporary_files_finder); - - let vector = tf.get_temporary_files(); - - // Sort - let mut vector = vector.clone(); - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - - for file_entry in vector { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsTemporaryFiles::SelectionButton as u32, &false), - (ColumnsTemporaryFiles::Name as u32, &file), - (ColumnsTemporaryFiles::Path as u32, &directory), - ( - ColumnsTemporaryFiles::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsTemporaryFiles::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), - ]; - list_store.set(&list_store.append(), &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_temporary_files_state.borrow_mut() = tf; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::Temporary, - &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], - temporary_files_number > 0, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::Temporary).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_temporary_files( + tf, + &entry_info, + &tree_view_temporary_files_finder, + &text_view_errors, + &shared_temporary_files_state, + &shared_buttons, + &buttons_array, + &buttons_names, + ); } Message::SimilarImages(sf) => { - const COLUMNS_NUMBER: usize = 13; - if sf.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - if sf.get_use_reference() { - tree_view_similar_images_finder.selection().set_select_function(select_function_always_true); - } else { - tree_view_similar_images_finder.selection().set_select_function(select_function_similar_images); - } - let information = sf.get_information(); - let text_messages = sf.get_text_messages(); - - let found_any_duplicates = information.number_of_duplicates > 0; - - entry_info.set_text( - flg!( - "compute_found_images", - generate_translation_hashmap(vec![ - ("number_files", information.number_of_duplicates.to_string()), - ("number_groups", information.number_of_groups.to_string()), - ]) - ) - .as_str(), - ); - - // Create GUI - { - let list_store = get_list_store(&tree_view_similar_images_finder); - - if sf.get_use_reference() { - let vec_struct_similar: &Vec<(similar_images::FileEntry, Vec)> = sf.get_similar_images_referenced(); - for (base_file_entry, vec_file_entry) in vec_struct_similar.iter() { - // Sort - let vec_file_entry = if vec_file_entry.len() >= 2 { - let mut vec_file_entry = vec_file_entry.clone(); - // Use comparison by similarity, because it is more important that path here - vec_file_entry.sort_unstable_by_key(|e| e.similarity); - vec_file_entry - } else { - vec_file_entry.clone() - }; - - // Header - let (directory, file) = split_path(&base_file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSimilarImages::ActivatableSelectButton as u32, &false), - (ColumnsSimilarImages::SelectionButton as u32, &false), - (ColumnsSimilarImages::Similarity as u32, &String::new()), - (ColumnsSimilarImages::Size as u32, &format_size(base_file_entry.size, BINARY)), - (ColumnsSimilarImages::SizeAsBytes as u32, &base_file_entry.size), - (ColumnsSimilarImages::Dimensions as u32, &base_file_entry.dimensions), - (ColumnsSimilarImages::Name as u32, &file), - (ColumnsSimilarImages::Path as u32, &directory), - ( - ColumnsSimilarImages::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(base_file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsSimilarImages::ModificationAsSecs as u32, &(base_file_entry.modified_date)), - (ColumnsSimilarImages::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsSimilarImages::IsHeader as u32, &true), - (ColumnsSimilarImages::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - - // Meat - for file_entry in &vec_file_entry { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSimilarImages::ActivatableSelectButton as u32, &true), - (ColumnsSimilarImages::SelectionButton as u32, &false), - ( - ColumnsSimilarImages::Similarity as u32, - &(similar_images::get_string_from_similarity(&file_entry.similarity, hash_size).to_string()), - ), - (ColumnsSimilarImages::Size as u32, &format_size(file_entry.size, BINARY)), - (ColumnsSimilarImages::SizeAsBytes as u32, &file_entry.size), - (ColumnsSimilarImages::Dimensions as u32, &file_entry.dimensions), - (ColumnsSimilarImages::Name as u32, &file), - (ColumnsSimilarImages::Path as u32, &directory), - ( - ColumnsSimilarImages::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsSimilarImages::ModificationAsSecs as u32, &(file_entry.modified_date)), - (ColumnsSimilarImages::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsSimilarImages::IsHeader as u32, &false), - (ColumnsSimilarImages::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - } - } - } else { - let vec_struct_similar = sf.get_similar_images(); - for vec_file_entry in vec_struct_similar.iter() { - // Sort - let vec_file_entry = if vec_file_entry.len() >= 2 { - let mut vec_file_entry = vec_file_entry.clone(); - // Use comparison by similarity, because it is more important that path here - vec_file_entry.sort_unstable_by_key(|e| e.similarity); - vec_file_entry - } else { - vec_file_entry.clone() - }; - - // Header - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSimilarImages::ActivatableSelectButton as u32, &false), - (ColumnsSimilarImages::SelectionButton as u32, &false), - (ColumnsSimilarImages::Similarity as u32, &String::new()), - (ColumnsSimilarImages::Size as u32, &String::new()), - (ColumnsSimilarImages::SizeAsBytes as u32, &(0)), - (ColumnsSimilarImages::Dimensions as u32, &String::new()), - (ColumnsSimilarImages::Name as u32, &String::new()), - (ColumnsSimilarImages::Path as u32, &String::new()), - (ColumnsSimilarImages::Modification as u32, &String::new()), - (ColumnsSimilarImages::ModificationAsSecs as u32, &(0)), - (ColumnsSimilarImages::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsSimilarImages::IsHeader as u32, &true), - (ColumnsSimilarImages::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - - // Meat - for file_entry in &vec_file_entry { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSimilarImages::ActivatableSelectButton as u32, &true), - (ColumnsSimilarImages::SelectionButton as u32, &false), - ( - ColumnsSimilarImages::Similarity as u32, - &(similar_images::get_string_from_similarity(&file_entry.similarity, hash_size).to_string()), - ), - (ColumnsSimilarImages::Size as u32, &format_size(file_entry.size, BINARY)), - (ColumnsSimilarImages::SizeAsBytes as u32, &file_entry.size), - (ColumnsSimilarImages::Dimensions as u32, &file_entry.dimensions), - (ColumnsSimilarImages::Name as u32, &file), - (ColumnsSimilarImages::Path as u32, &directory), - ( - ColumnsSimilarImages::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsSimilarImages::ModificationAsSecs as u32, &(file_entry.modified_date)), - (ColumnsSimilarImages::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsSimilarImages::IsHeader as u32, &false), - (ColumnsSimilarImages::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - } - } - } - - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_similar_images_state.borrow_mut() = sf; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::SimilarImages, - &[ - BottomButtonsEnum::Save, - BottomButtonsEnum::Delete, - BottomButtonsEnum::Select, - BottomButtonsEnum::Sort, - BottomButtonsEnum::Symlink, - BottomButtonsEnum::Hardlink, - BottomButtonsEnum::Move, - BottomButtonsEnum::Compare, - ], - found_any_duplicates, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::SimilarImages).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_similar_images( + sf, + &entry_info, + &tree_view_similar_images_finder, + &text_view_errors, + &shared_similar_images_state, + &shared_buttons, + &buttons_array, + &buttons_names, + hash_size, + ); } Message::SimilarVideos(ff) => { - const COLUMNS_NUMBER: usize = 11; - if ff.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - if ff.get_use_reference() { - tree_view_similar_videos_finder.selection().set_select_function(select_function_always_true); - } else { - tree_view_similar_videos_finder.selection().set_select_function(select_function_similar_videos); - } - let information = ff.get_information(); - let text_messages = ff.get_text_messages(); - let found_any_duplicates = information.number_of_duplicates > 0; - - entry_info.set_text( - flg!( - "compute_found_videos", - generate_translation_hashmap(vec![ - ("number_files", information.number_of_duplicates.to_string()), - ("number_groups", information.number_of_groups.to_string()), - ]) - ) - .as_str(), - ); - - // Create GUI - { - let list_store = get_list_store(&tree_view_similar_videos_finder); - - if ff.get_use_reference() { - let vec_struct_similar = ff.get_similar_videos_referenced(); - - for (base_file_entry, vec_file_entry) in vec_struct_similar.iter() { - // Sort - let vec_file_entry = if vec_file_entry.len() >= 2 { - let mut vec_file_entry = vec_file_entry.clone(); - vec_file_entry.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - vec_file_entry - } else { - vec_file_entry.clone() - }; - - // Header - let (directory, file) = split_path(&base_file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSimilarVideos::ActivatableSelectButton as u32, &false), - (ColumnsSimilarVideos::SelectionButton as u32, &false), - (ColumnsSimilarVideos::Size as u32, &format_size(base_file_entry.size, BINARY)), - (ColumnsSimilarVideos::SizeAsBytes as u32, &base_file_entry.size), - (ColumnsSimilarVideos::Name as u32, &file), - (ColumnsSimilarVideos::Path as u32, &directory), - ( - ColumnsSimilarVideos::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(base_file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsSimilarVideos::ModificationAsSecs as u32, &(base_file_entry.modified_date)), - (ColumnsSimilarVideos::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsSimilarVideos::IsHeader as u32, &true), - (ColumnsSimilarVideos::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - - // Meat - for file_entry in &vec_file_entry { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSimilarVideos::ActivatableSelectButton as u32, &true), - (ColumnsSimilarVideos::SelectionButton as u32, &false), - (ColumnsSimilarVideos::Size as u32, &format_size(file_entry.size, BINARY)), - (ColumnsSimilarVideos::SizeAsBytes as u32, &file_entry.size), - (ColumnsSimilarVideos::Name as u32, &file), - (ColumnsSimilarVideos::Path as u32, &directory), - ( - ColumnsSimilarVideos::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsSimilarVideos::ModificationAsSecs as u32, &(file_entry.modified_date)), - (ColumnsSimilarVideos::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsSimilarVideos::IsHeader as u32, &false), - (ColumnsSimilarVideos::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - } - } - } else { - let vec_struct_similar = ff.get_similar_videos(); - - for vec_file_entry in vec_struct_similar.iter() { - // Sort - let vec_file_entry = if vec_file_entry.len() >= 2 { - let mut vec_file_entry = vec_file_entry.clone(); - vec_file_entry.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - vec_file_entry - } else { - vec_file_entry.clone() - }; - - // Header - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSimilarVideos::ActivatableSelectButton as u32, &false), - (ColumnsSimilarVideos::SelectionButton as u32, &false), - (ColumnsSimilarVideos::Size as u32, &String::new()), - (ColumnsSimilarVideos::SizeAsBytes as u32, &(0)), - (ColumnsSimilarVideos::Name as u32, &String::new()), - (ColumnsSimilarVideos::Path as u32, &String::new()), - (ColumnsSimilarVideos::Modification as u32, &String::new()), - (ColumnsSimilarVideos::ModificationAsSecs as u32, &(0)), - (ColumnsSimilarVideos::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsSimilarVideos::IsHeader as u32, &true), - (ColumnsSimilarVideos::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - - // Meat - for file_entry in &vec_file_entry { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSimilarVideos::ActivatableSelectButton as u32, &true), - (ColumnsSimilarVideos::SelectionButton as u32, &false), - (ColumnsSimilarVideos::Size as u32, &format_size(file_entry.size, BINARY)), - (ColumnsSimilarVideos::SizeAsBytes as u32, &file_entry.size), - (ColumnsSimilarVideos::Name as u32, &file), - (ColumnsSimilarVideos::Path as u32, &directory), - ( - ColumnsSimilarVideos::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsSimilarVideos::ModificationAsSecs as u32, &(file_entry.modified_date)), - (ColumnsSimilarVideos::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsSimilarVideos::IsHeader as u32, &false), - (ColumnsSimilarVideos::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - } - } - } - - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_similar_videos_state.borrow_mut() = ff; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::SimilarVideos, - &[ - BottomButtonsEnum::Save, - BottomButtonsEnum::Delete, - BottomButtonsEnum::Select, - BottomButtonsEnum::Sort, - BottomButtonsEnum::Symlink, - BottomButtonsEnum::Hardlink, - BottomButtonsEnum::Move, - ], - found_any_duplicates, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::SimilarVideos).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_similar_videos( + ff, + &entry_info, + &tree_view_similar_videos_finder, + &text_view_errors, + &shared_similar_videos_state, + &shared_buttons, + &buttons_array, + &buttons_names, + ); } Message::SameMusic(mf) => { - const COLUMNS_NUMBER: usize = 18; - if mf.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - if mf.get_use_reference() { - tree_view_same_music_finder.selection().set_select_function(select_function_always_true); - } else { - tree_view_same_music_finder.selection().set_select_function(select_function_same_music); - } - - let information = mf.get_information(); - let text_messages = mf.get_text_messages(); - - let same_music_number: usize = information.number_of_duplicates; - - entry_info.set_text( - flg!( - "compute_found_music", - generate_translation_hashmap(vec![ - ("number_files", information.number_of_duplicates.to_string()), - ("number_groups", information.number_of_groups.to_string()), - ]) - ) - .as_str(), - ); - - // Create GUI - { - let list_store = get_list_store(&tree_view_same_music_finder); - - let music_similarity = *mf.get_music_similarity(); - - let is_track_title = (MusicSimilarity::TRACK_TITLE & music_similarity) != MusicSimilarity::NONE; - let is_track_artist = (MusicSimilarity::TRACK_ARTIST & music_similarity) != MusicSimilarity::NONE; - let is_year = (MusicSimilarity::YEAR & music_similarity) != MusicSimilarity::NONE; - let is_bitrate = (MusicSimilarity::BITRATE & music_similarity) != MusicSimilarity::NONE; - let is_length = (MusicSimilarity::LENGTH & music_similarity) != MusicSimilarity::NONE; - let is_genre = (MusicSimilarity::GENRE & music_similarity) != MusicSimilarity::NONE; - - if mf.get_use_reference() { - let vector = mf.get_similar_music_referenced(); - - for (base_file_entry, vec_file_entry) in vector { - // Sort - let vec_file_entry = if vec_file_entry.len() >= 2 { - let mut vec_file_entry = vec_file_entry.clone(); - vec_file_entry.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - vec_file_entry - } else { - vec_file_entry.clone() - }; - - let (directory, file) = split_path(&base_file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSameMusic::ActivatableSelectButton as u32, &false), - (ColumnsSameMusic::SelectionButton as u32, &false), - (ColumnsSameMusic::Size as u32, &format_size(base_file_entry.size, BINARY)), - (ColumnsSameMusic::SizeAsBytes as u32, &base_file_entry.size), - (ColumnsSameMusic::Name as u32, &file), - (ColumnsSameMusic::Path as u32, &directory), - (ColumnsSameMusic::Title as u32, &base_file_entry.track_title), - (ColumnsSameMusic::Artist as u32, &base_file_entry.track_artist), - (ColumnsSameMusic::Year as u32, &base_file_entry.year.to_string()), - (ColumnsSameMusic::Genre as u32, &base_file_entry.genre), - (ColumnsSameMusic::Bitrate as u32, &(format!("{} kbps", base_file_entry.bitrate))), - (ColumnsSameMusic::BitrateAsNumber as u32, &(base_file_entry.bitrate)), - (ColumnsSameMusic::Length as u32, &base_file_entry.length), - ( - ColumnsSameMusic::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(base_file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsSameMusic::ModificationAsSecs as u32, &(base_file_entry.modified_date)), - (ColumnsSameMusic::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsSameMusic::IsHeader as u32, &true), - (ColumnsSameMusic::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - for file_entry in vec_file_entry { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSameMusic::ActivatableSelectButton as u32, &true), - (ColumnsSameMusic::SelectionButton as u32, &false), - (ColumnsSameMusic::Size as u32, &format_size(file_entry.size, BINARY)), - (ColumnsSameMusic::SizeAsBytes as u32, &file_entry.size), - (ColumnsSameMusic::Name as u32, &file), - (ColumnsSameMusic::Path as u32, &directory), - (ColumnsSameMusic::Title as u32, &file_entry.track_title), - (ColumnsSameMusic::Artist as u32, &file_entry.track_artist), - (ColumnsSameMusic::Year as u32, &file_entry.year.to_string()), - (ColumnsSameMusic::Genre as u32, &file_entry.genre), - (ColumnsSameMusic::Bitrate as u32, &(format!("{} kbps", file_entry.bitrate))), - (ColumnsSameMusic::BitrateAsNumber as u32, &(file_entry.bitrate)), - (ColumnsSameMusic::Length as u32, &file_entry.length), - ( - ColumnsSameMusic::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsSameMusic::ModificationAsSecs as u32, &(file_entry.modified_date)), - (ColumnsSameMusic::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsSameMusic::IsHeader as u32, &false), - (ColumnsSameMusic::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - } - } - } else { - let vector = mf.get_duplicated_music_entries(); - - let text: String = "-----".to_string(); - - for vec_file_entry in vector { - // Sort - let vec_file_entry = if vec_file_entry.len() >= 2 { - let mut vec_file_entry = vec_file_entry.clone(); - vec_file_entry.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - vec_file_entry - } else { - vec_file_entry.clone() - }; - - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSameMusic::ActivatableSelectButton as u32, &false), - (ColumnsSameMusic::SelectionButton as u32, &false), - (ColumnsSameMusic::Size as u32, &String::new()), - (ColumnsSameMusic::SizeAsBytes as u32, &(0)), - (ColumnsSameMusic::Name as u32, &String::new()), - (ColumnsSameMusic::Path as u32, &String::new()), - (ColumnsSameMusic::Title as u32, &(if is_track_title { text.clone() } else { String::new() })), - (ColumnsSameMusic::Artist as u32, &(if is_track_artist { text.clone() } else { String::new() })), - (ColumnsSameMusic::Year as u32, &(if is_year { text.clone() } else { String::new() })), - (ColumnsSameMusic::Bitrate as u32, &(if is_bitrate { text.clone() } else { String::new() })), - (ColumnsSameMusic::BitrateAsNumber as u32, &(0)), - (ColumnsSameMusic::Genre as u32, &(if is_genre { text.clone() } else { String::new() })), - (ColumnsSameMusic::Length as u32, &(if is_length { text.clone() } else { String::new() })), - (ColumnsSameMusic::Modification as u32, &String::new()), - (ColumnsSameMusic::ModificationAsSecs as u32, &(0)), - (ColumnsSameMusic::Color as u32, &(HEADER_ROW_COLOR.to_string())), - (ColumnsSameMusic::IsHeader as u32, &true), - (ColumnsSameMusic::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - for file_entry in vec_file_entry { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsSameMusic::ActivatableSelectButton as u32, &true), - (ColumnsSameMusic::SelectionButton as u32, &false), - (ColumnsSameMusic::Size as u32, &format_size(file_entry.size, BINARY)), - (ColumnsSameMusic::SizeAsBytes as u32, &file_entry.size), - (ColumnsSameMusic::Name as u32, &file), - (ColumnsSameMusic::Path as u32, &directory), - (ColumnsSameMusic::Title as u32, &file_entry.track_title), - (ColumnsSameMusic::Artist as u32, &file_entry.track_artist), - (ColumnsSameMusic::Year as u32, &file_entry.year.to_string()), - (ColumnsSameMusic::Genre as u32, &file_entry.genre), - (ColumnsSameMusic::Bitrate as u32, &(format!("{} kbps", file_entry.bitrate))), - (ColumnsSameMusic::BitrateAsNumber as u32, &(file_entry.bitrate)), - (ColumnsSameMusic::Length as u32, &file_entry.length), - ( - ColumnsSameMusic::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsSameMusic::ModificationAsSecs as u32, &(file_entry.modified_date)), - (ColumnsSameMusic::Color as u32, &(MAIN_ROW_COLOR.to_string())), - (ColumnsSameMusic::IsHeader as u32, &false), - (ColumnsSameMusic::TextColor as u32, &(TEXT_COLOR.to_string())), - ]; - list_store.set(&list_store.append(), &values); - } - } - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_same_music_state.borrow_mut() = mf; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::SameMusic, - &[ - BottomButtonsEnum::Save, - BottomButtonsEnum::Delete, - BottomButtonsEnum::Select, - BottomButtonsEnum::Sort, - BottomButtonsEnum::Symlink, - BottomButtonsEnum::Hardlink, - BottomButtonsEnum::Move, - ], - same_music_number > 0, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::SameMusic).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_same_music( + mf, + &entry_info, + &tree_view_same_music_finder, + &text_view_errors, + &shared_same_music_state, + &shared_buttons, + &buttons_array, + &buttons_names, + ); } Message::InvalidSymlinks(ifs) => { - const COLUMNS_NUMBER: usize = 7; - if ifs.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - let information = ifs.get_information(); - let text_messages = ifs.get_text_messages(); - - let invalid_symlinks: usize = information.number_of_invalid_symlinks; - - entry_info.set_text( - flg!( - "compute_found_invalid_symlinks", - generate_translation_hashmap(vec![("number_files", invalid_symlinks.to_string()),]) - ) - .as_str(), - ); - - // Create GUI - { - let list_store = get_list_store(&tree_view_invalid_symlinks); - - let vector = ifs.get_invalid_symlinks(); - - // Sort - let mut vector = vector.clone(); - - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - - for file_entry in vector { - let (directory, file) = split_path(&file_entry.path); - let symlink_info = file_entry.symlink_info.clone().expect("invalid traversal result"); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsInvalidSymlinks::SelectionButton as u32, &false), - (ColumnsInvalidSymlinks::Name as u32, &file), - (ColumnsInvalidSymlinks::Path as u32, &directory), - (ColumnsInvalidSymlinks::DestinationPath as u32, &symlink_info.destination_path.to_string_lossy().to_string()), - ( - ColumnsInvalidSymlinks::TypeOfError as u32, - &get_text_from_invalid_symlink_cause(&symlink_info.type_of_error), - ), - ( - ColumnsInvalidSymlinks::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsInvalidSymlinks::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), - ]; - list_store.set(&list_store.append(), &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_same_invalid_symlinks.borrow_mut() = ifs; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::Symlinks, - &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], - invalid_symlinks > 0, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::Symlinks).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_invalid_symlinks( + ifs, + &entry_info, + &tree_view_invalid_symlinks, + &text_view_errors, + &shared_same_invalid_symlinks, + &shared_buttons, + &buttons_array, + &buttons_names, + ); } Message::BrokenFiles(br) => { - const COLUMNS_NUMBER: usize = 6; - if br.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - let information = br.get_information(); - let text_messages = br.get_text_messages(); - - let broken_files_number: usize = information.number_of_broken_files; - - entry_info.set_text( - flg!( - "compute_found_broken_files", - generate_translation_hashmap(vec![("number_files", broken_files_number.to_string()),]) - ) - .as_str(), - ); - - // Create GUI - { - let list_store = get_list_store(&tree_view_broken_files); - - let vector = br.get_broken_files(); - - // Sort - let mut vector = vector.clone(); - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - - for file_entry in vector { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsBrokenFiles::SelectionButton as u32, &false), - (ColumnsBrokenFiles::Name as u32, &file), - (ColumnsBrokenFiles::Path as u32, &directory), - (ColumnsBrokenFiles::ErrorType as u32, &file_entry.error_string), - ( - ColumnsBrokenFiles::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsBrokenFiles::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), - ]; - list_store.set(&list_store.append(), &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_broken_files_state.borrow_mut() = br; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::BrokenFiles, - &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], - broken_files_number > 0, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::BrokenFiles).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_broken_files( + br, + &entry_info, + &tree_view_broken_files, + &text_view_errors, + &shared_broken_files_state, + &shared_buttons, + &buttons_array, + &buttons_names, + ); } Message::BadExtensions(be) => { - const COLUMNS_NUMBER: usize = 7; - if be.get_stopped_search() { - entry_info.set_text(&flg!("compute_stopped_by_user")); - } else { - let information = be.get_information(); - let text_messages = be.get_text_messages(); - - let bad_extensions_number: usize = information.number_of_files_with_bad_extension; - entry_info.set_text( - flg!( - "compute_found_bad_extensions", - generate_translation_hashmap(vec![("number_files", bad_extensions_number.to_string()),]) - ) - .as_str(), - ); - - // Create GUI - { - let list_store = get_list_store(&tree_view_bad_extensions); - - let vector = be.get_bad_extensions_files(); - - // Sort - let mut vector = vector.clone(); - vector.sort_unstable_by_key(|e| { - let t = split_path(e.path.as_path()); - (t.0, t.1) - }); - - for file_entry in vector { - let (directory, file) = split_path(&file_entry.path); - let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ - (ColumnsBadExtensions::SelectionButton as u32, &false), - (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::Modification as u32, - &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), - ), - (ColumnsBadExtensions::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), - ]; - list_store.set(&list_store.append(), &values); - } - print_text_messages_to_text_view(text_messages, &text_view_errors); - } - - // Set state - { - *shared_bad_extensions_state.borrow_mut() = be; - - set_specific_buttons_as_active( - &shared_buttons, - &NotebookMainEnum::Temporary, - &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], - bad_extensions_number > 0, - ); - - set_buttons( - &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::Temporary).unwrap(), - &buttons_array, - &buttons_names, - ); - } - } + computer_bad_extensions( + be, + &entry_info, + &tree_view_bad_extensions, + &text_view_errors, + &shared_bad_extensions_state, + &shared_buttons, + &buttons_array, + &buttons_names, + ); } } // Returning false here would close the receiver and have senders fail @@ -1582,13 +229,1293 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver< }); } -fn set_specific_buttons_as_active( - buttons_array: &Rc>>>, - notebook_enum: &NotebookMainEnum, - buttons: &[BottomButtonsEnum], - value_to_set: bool, +fn computer_bad_extensions( + be: BadExtensions, + entry_info: &Entry, + tree_view: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], ) { - for i in buttons { - *buttons_array.borrow_mut().get_mut(notebook_enum).unwrap().get_mut(i).unwrap() = value_to_set; + const COLUMNS_NUMBER: usize = 7; + if be.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + let information = be.get_information(); + let text_messages = be.get_text_messages(); + + let bad_extensions_number: usize = information.number_of_files_with_bad_extension; + entry_info.set_text( + flg!( + "compute_found_bad_extensions", + generate_translation_hashmap(vec![("number_files", bad_extensions_number.to_string()),]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(tree_view); + + let vector = be.get_bad_extensions_files(); + + // Sort + let mut vector = vector.clone(); + vector.sort_unstable_by_key(|e| { + let t = split_path(e.path.as_path()); + (t.0, t.1) + }); + + for file_entry in vector { + let (directory, file) = split_path(&file_entry.path); + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsBadExtensions::SelectionButton as u32, &false), + (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::Modification as u32, + &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), + ), + (ColumnsBadExtensions::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), + ]; + list_store.set(&list_store.append(), &values); + } + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = be; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::Temporary, bad_extensions_number > 0); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::Temporary).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn computer_broken_files( + br: BrokenFiles, + entry_info: &Entry, + tree_view: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], +) { + const COLUMNS_NUMBER: usize = 6; + if br.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + let information = br.get_information(); + let text_messages = br.get_text_messages(); + + let broken_files_number: usize = information.number_of_broken_files; + + entry_info.set_text( + flg!( + "compute_found_broken_files", + generate_translation_hashmap(vec![("number_files", broken_files_number.to_string()),]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(tree_view); + + let vector = br.get_broken_files(); + + // Sort + let mut vector = vector.clone(); + vector.sort_unstable_by_key(|e| { + let t = split_path(e.path.as_path()); + (t.0, t.1) + }); + + for file_entry in vector { + let (directory, file) = split_path(&file_entry.path); + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsBrokenFiles::SelectionButton as u32, &false), + (ColumnsBrokenFiles::Name as u32, &file), + (ColumnsBrokenFiles::Path as u32, &directory), + (ColumnsBrokenFiles::ErrorType as u32, &file_entry.error_string), + ( + ColumnsBrokenFiles::Modification as u32, + &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), + ), + (ColumnsBrokenFiles::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), + ]; + list_store.set(&list_store.append(), &values); + } + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = br; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::BrokenFiles, broken_files_number > 0); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::BrokenFiles).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn computer_invalid_symlinks( + ifs: InvalidSymlinks, + entry_info: &Entry, + tree_view: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], +) { + const COLUMNS_NUMBER: usize = 7; + if ifs.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + let information = ifs.get_information(); + let text_messages = ifs.get_text_messages(); + + let invalid_symlinks: usize = information.number_of_invalid_symlinks; + + entry_info.set_text( + flg!( + "compute_found_invalid_symlinks", + generate_translation_hashmap(vec![("number_files", invalid_symlinks.to_string()),]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(tree_view); + + let vector = vector_sort_simple_unstable_entry_by_path(ifs.get_invalid_symlinks()); + + for file_entry in vector { + let (directory, file) = split_path(&file_entry.path); + let symlink_info = file_entry.symlink_info.clone().expect("invalid traversal result"); + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsInvalidSymlinks::SelectionButton as u32, &false), + (ColumnsInvalidSymlinks::Name as u32, &file), + (ColumnsInvalidSymlinks::Path as u32, &directory), + (ColumnsInvalidSymlinks::DestinationPath as u32, &symlink_info.destination_path.to_string_lossy().to_string()), + ( + ColumnsInvalidSymlinks::TypeOfError as u32, + &get_text_from_invalid_symlink_cause(&symlink_info.type_of_error), + ), + ( + ColumnsInvalidSymlinks::Modification as u32, + &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), + ), + (ColumnsInvalidSymlinks::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), + ]; + list_store.set(&list_store.append(), &values); + } + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = ifs; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::Symlinks, invalid_symlinks > 0); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::Symlinks).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn computer_same_music( + mf: SameMusic, + entry_info: &Entry, + tree_view: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], +) { + if mf.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + if mf.get_use_reference() { + tree_view.selection().set_select_function(select_function_always_true); + } else { + tree_view.selection().set_select_function(select_function_same_music); + } + + let information = mf.get_information(); + let text_messages = mf.get_text_messages(); + + let same_music_number: usize = information.number_of_duplicates; + + entry_info.set_text( + flg!( + "compute_found_music", + generate_translation_hashmap(vec![ + ("number_files", information.number_of_duplicates.to_string()), + ("number_groups", information.number_of_groups.to_string()), + ]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(tree_view); + + let music_similarity = *mf.get_music_similarity(); + + let is_track_title = (MusicSimilarity::TRACK_TITLE & music_similarity) != MusicSimilarity::NONE; + let is_track_artist = (MusicSimilarity::TRACK_ARTIST & music_similarity) != MusicSimilarity::NONE; + let is_year = (MusicSimilarity::YEAR & music_similarity) != MusicSimilarity::NONE; + let is_bitrate = (MusicSimilarity::BITRATE & music_similarity) != MusicSimilarity::NONE; + let is_length = (MusicSimilarity::LENGTH & music_similarity) != MusicSimilarity::NONE; + let is_genre = (MusicSimilarity::GENRE & music_similarity) != MusicSimilarity::NONE; + + if mf.get_use_reference() { + let vector = mf.get_similar_music_referenced(); + + for (base_file_entry, vec_file_entry) in vector { + // Sort + let vec_file_entry = if vec_file_entry.len() >= 2 { + let mut vec_file_entry = vec_file_entry.clone(); + vec_file_entry.sort_unstable_by_key(|e| { + let t = split_path(e.path.as_path()); + (t.0, t.1) + }); + vec_file_entry + } else { + vec_file_entry.clone() + }; + + let (directory, file) = split_path(&base_file_entry.path); + same_music_add_to_list_store( + &list_store, + &file, + &directory, + base_file_entry.size, + base_file_entry.modified_date, + &base_file_entry.track_title, + &base_file_entry.track_artist, + &base_file_entry.year, + base_file_entry.bitrate, + &format!("{} kbps", base_file_entry.bitrate), + &base_file_entry.genre, + &base_file_entry.length, + true, + true, + ); + for file_entry in vec_file_entry { + let (directory, file) = split_path(&file_entry.path); + same_music_add_to_list_store( + &list_store, + &file, + &directory, + file_entry.size, + file_entry.modified_date, + &file_entry.track_title, + &file_entry.track_artist, + &file_entry.year, + file_entry.bitrate, + &format!("{} kbps", file_entry.bitrate), + &file_entry.genre, + &file_entry.length, + false, + true, + ); + } + } + } else { + let vector = mf.get_duplicated_music_entries(); + + let text: &str = "-----"; + + for vec_file_entry in vector { + // Sort + let vec_file_entry = if vec_file_entry.len() >= 2 { + let mut vec_file_entry = vec_file_entry.clone(); + vec_file_entry.sort_unstable_by_key(|e| { + let t = split_path(e.path.as_path()); + (t.0, t.1) + }); + vec_file_entry + } else { + vec_file_entry.clone() + }; + + same_music_add_to_list_store( + &list_store, + "", + "", + 0, + 0, + if is_track_title { text } else { "" }, + if is_track_artist { text } else { "" }, + if is_year { text } else { "" }, + 0, + if is_bitrate { text } else { "" }, + if is_genre { text } else { "" }, + if is_length { text } else { "" }, + true, + false, + ); + for file_entry in vec_file_entry { + let (directory, file) = split_path(&file_entry.path); + same_music_add_to_list_store( + &list_store, + &file, + &directory, + file_entry.size, + file_entry.modified_date, + &file_entry.track_title, + &file_entry.track_artist, + &file_entry.year, + file_entry.bitrate, + &format!("{} kbps", file_entry.bitrate), + &file_entry.genre, + &file_entry.length, + false, + false, + ); + } + } + } + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = mf; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::SameMusic, same_music_number > 0); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::SameMusic).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn computer_similar_videos( + ff: SimilarVideos, + entry_info: &Entry, + tree_view: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], +) { + if ff.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + if ff.get_use_reference() { + tree_view.selection().set_select_function(select_function_always_true); + } else { + tree_view.selection().set_select_function(select_function_similar_videos); + } + let information = ff.get_information(); + let text_messages = ff.get_text_messages(); + let found_any_duplicates = information.number_of_duplicates > 0; + + entry_info.set_text( + flg!( + "compute_found_videos", + generate_translation_hashmap(vec![ + ("number_files", information.number_of_duplicates.to_string()), + ("number_groups", information.number_of_groups.to_string()), + ]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(tree_view); + + if ff.get_use_reference() { + let vec_struct_similar = ff.get_similar_videos_referenced(); + + for (base_file_entry, vec_file_entry) in vec_struct_similar.iter() { + // Sort + let vec_file_entry = if vec_file_entry.len() >= 2 { + let mut vec_file_entry = vec_file_entry.clone(); + vec_file_entry.sort_unstable_by_key(|e| { + let t = split_path(e.path.as_path()); + (t.0, t.1) + }); + vec_file_entry + } else { + vec_file_entry.clone() + }; + + similar_videos_add_to_list_store(&list_store, "", "", base_file_entry.size, base_file_entry.modified_date, true, true); + for file_entry in &vec_file_entry { + let (directory, file) = split_path(&file_entry.path); + similar_videos_add_to_list_store(&list_store, &file, &directory, file_entry.size, file_entry.modified_date, false, true); + } + } + } else { + let vec_struct_similar = ff.get_similar_videos(); + + for vec_file_entry in vec_struct_similar.iter() { + // Sort + let vec_file_entry = if vec_file_entry.len() >= 2 { + let mut vec_file_entry = vec_file_entry.clone(); + vec_file_entry.sort_unstable_by_key(|e| { + let t = split_path(e.path.as_path()); + (t.0, t.1) + }); + vec_file_entry + } else { + vec_file_entry.clone() + }; + + similar_videos_add_to_list_store(&list_store, "", "", 0, 0, true, false); + for file_entry in &vec_file_entry { + let (directory, file) = split_path(&file_entry.path); + similar_videos_add_to_list_store(&list_store, &file, &directory, file_entry.size, file_entry.modified_date, false, false); + } + } + } + + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = ff; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::SimilarVideos, found_any_duplicates); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::SimilarVideos).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn computer_similar_images( + sf: SimilarImages, + entry_info: &Entry, + tree_view: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], + hash_size: u8, +) { + if sf.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + if sf.get_use_reference() { + tree_view.selection().set_select_function(select_function_always_true); + } else { + tree_view.selection().set_select_function(select_function_similar_images); + } + let information = sf.get_information(); + let text_messages = sf.get_text_messages(); + + let found_any_duplicates = information.number_of_duplicates > 0; + + entry_info.set_text( + flg!( + "compute_found_images", + generate_translation_hashmap(vec![ + ("number_files", information.number_of_duplicates.to_string()), + ("number_groups", information.number_of_groups.to_string()), + ]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(tree_view); + + if sf.get_use_reference() { + let vec_struct_similar: &Vec<(similar_images::FileEntry, Vec)> = sf.get_similar_images_referenced(); + for (base_file_entry, vec_file_entry) in vec_struct_similar.iter() { + // Sort + let vec_file_entry = if vec_file_entry.len() >= 2 { + let mut vec_file_entry = vec_file_entry.clone(); + // Use comparison by similarity, because it is more important that path here + vec_file_entry.sort_unstable_by_key(|e| e.similarity); + vec_file_entry + } else { + vec_file_entry.clone() + }; + + // Header + let (directory, file) = split_path(&base_file_entry.path); + similar_images_add_to_list_store(&list_store, &file, &directory, 0, 0, "", 0, 0, true, true); + for file_entry in &vec_file_entry { + let (directory, file) = split_path(&file_entry.path); + similar_images_add_to_list_store( + &list_store, + &file, + &directory, + file_entry.size, + file_entry.modified_date, + &file_entry.dimensions, + file_entry.similarity, + hash_size, + false, + true, + ); + } + } + } else { + let vec_struct_similar = sf.get_similar_images(); + for vec_file_entry in vec_struct_similar.iter() { + // Sort + let vec_file_entry = if vec_file_entry.len() >= 2 { + let mut vec_file_entry = vec_file_entry.clone(); + // Use comparison by similarity, because it is more important that path here + vec_file_entry.sort_unstable_by_key(|e| e.similarity); + vec_file_entry + } else { + vec_file_entry.clone() + }; + + similar_images_add_to_list_store(&list_store, "", "", 0, 0, "", 0, 0, true, false); + for file_entry in &vec_file_entry { + let (directory, file) = split_path(&file_entry.path); + similar_images_add_to_list_store( + &list_store, + &file, + &directory, + file_entry.size, + file_entry.modified_date, + &file_entry.dimensions, + file_entry.similarity, + hash_size, + false, + false, + ); + } + } + } + + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = sf; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::SimilarImages, found_any_duplicates); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::SimilarImages).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn computer_temporary_files( + tf: Temporary, + entry_info: &Entry, + tree_view: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], +) { + const COLUMNS_NUMBER: usize = 5; + if tf.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + let information = tf.get_information(); + let text_messages = tf.get_text_messages(); + + let temporary_files_number: usize = information.number_of_temporary_files; + entry_info.set_text( + flg!( + "compute_found_temporary_files", + generate_translation_hashmap(vec![("number_files", temporary_files_number.to_string()),]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(tree_view); + + let vector = tf.get_temporary_files(); + + // Sort // TODO maybe simplify this via common file entry + let mut vector = vector.clone(); + vector.sort_unstable_by_key(|e| { + let t = split_path(e.path.as_path()); + (t.0, t.1) + }); + + for file_entry in vector { + let (directory, file) = split_path(&file_entry.path); + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsTemporaryFiles::SelectionButton as u32, &false), + (ColumnsTemporaryFiles::Name as u32, &file), + (ColumnsTemporaryFiles::Path as u32, &directory), + ( + ColumnsTemporaryFiles::Modification as u32, + &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), + ), + (ColumnsTemporaryFiles::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), + ]; + list_store.set(&list_store.append(), &values); + } + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = tf; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::Temporary, temporary_files_number > 0); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::Temporary).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn computer_big_files( + bf: BigFile, + entry_info: &Entry, + tree_view: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], +) { + const COLUMNS_NUMBER: usize = 7; + if bf.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + let information = bf.get_information(); + let text_messages = bf.get_text_messages(); + + let biggest_files_number: usize = information.number_of_real_files; + + entry_info.set_text( + flg!( + "compute_found_big_files", + generate_translation_hashmap(vec![("number_files", biggest_files_number.to_string()),]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(tree_view); + + let vector = bf.get_big_files(); + + for (size, file_entry) in vector.iter() { + let (directory, file) = split_path(&file_entry.path); + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsBigFiles::SelectionButton as u32, &false), + (ColumnsBigFiles::Size as u32, &(format_size(*size, BINARY))), + (ColumnsBigFiles::Name as u32, &file), + (ColumnsBigFiles::Path as u32, &directory), + ( + ColumnsBigFiles::Modification as u32, + &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), + ), + (ColumnsBigFiles::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), + (ColumnsBigFiles::SizeAsBytes as u32, &(size)), + ]; + list_store.set(&list_store.append(), &values); + } + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = bf; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::BigFiles, biggest_files_number > 0); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::BigFiles).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn computer_empty_files( + vf: EmptyFiles, + entry_info: &Entry, + tree_view: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], +) { + const COLUMNS_NUMBER: usize = 5; + if vf.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + let information = vf.get_information(); + let text_messages = vf.get_text_messages(); + + let empty_files_number: usize = information.number_of_empty_files; + + entry_info.set_text( + flg!( + "compute_found_empty_files", + generate_translation_hashmap(vec![("number_files", empty_files_number.to_string()),]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(tree_view); + + let vector = vf.get_empty_files(); + let vector = vector_sort_simple_unstable_entry_by_path(vector); + + for file_entry in vector { + let (directory, file) = split_path(&file_entry.path); + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsEmptyFiles::SelectionButton as u32, &false), + (ColumnsEmptyFiles::Name as u32, &file), + (ColumnsEmptyFiles::Path as u32, &directory), + ( + ColumnsEmptyFiles::Modification as u32, + &(NaiveDateTime::from_timestamp_opt(file_entry.modified_date as i64, 0).unwrap().to_string()), + ), + (ColumnsEmptyFiles::ModificationAsSecs as u32, &(file_entry.modified_date as i64)), + ]; + list_store.set(&list_store.append(), &values); + } + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = vf; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::EmptyFiles, empty_files_number > 0); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::EmptyFiles).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn computer_empty_folders( + ef: EmptyFolder, + entry_info: &Entry, + tree_view: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], +) { + const COLUMNS_NUMBER: usize = 5; + if ef.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + let information = ef.get_information(); + let text_messages = ef.get_text_messages(); + + let empty_folder_number: usize = information.number_of_empty_folders; + + entry_info.set_text( + flg!( + "compute_found_empty_folders", + generate_translation_hashmap(vec![("number_files", empty_folder_number.to_string()),]) + ) + .as_str(), + ); + + // Create GUI + { + let list_store = get_list_store(tree_view); + + let hashmap = ef.get_empty_folder_list(); + let mut vector = hashmap.keys().cloned().collect::>(); + + vector.sort_unstable_by_key(|e| { + let t = split_path(e.as_path()); + (t.0, t.1) + }); + + for path in vector { + let (directory, file) = split_path(&path); + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsEmptyFolders::SelectionButton as u32, &false), + (ColumnsEmptyFolders::Name as u32, &file), + (ColumnsEmptyFolders::Path as u32, &directory), + ( + ColumnsEmptyFolders::Modification as u32, + &(NaiveDateTime::from_timestamp_opt(hashmap.get(&path).unwrap().modified_date as i64, 0).unwrap().to_string()), + ), + (ColumnsEmptyFolders::ModificationAsSecs as u32, &(hashmap.get(&path).unwrap().modified_date)), + ]; + list_store.set(&list_store.append(), &values); + } + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = ef; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::EmptyDirectories, empty_folder_number > 0); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::EmptyDirectories).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn computer_duplicate_finder( + df: DuplicateFinder, + entry_info: &Entry, + tree_view_duplicate_finder: &TreeView, + text_view_errors: &TextView, + shared_state: &Rc>, + shared_buttons: &Rc>>>, + buttons_array: &[Widget; 9], + buttons_names: &[BottomButtonsEnum; 9], +) { + if df.get_stopped_search() { + entry_info.set_text(&flg!("compute_stopped_by_user")); + } else { + if df.get_use_reference() { + tree_view_duplicate_finder.selection().set_select_function(select_function_always_true); + } else { + tree_view_duplicate_finder.selection().set_select_function(select_function_duplicates); + } + + let information = df.get_information(); + let text_messages = df.get_text_messages(); + + let duplicates_number: usize; + let duplicates_size: u64; + let duplicates_group: usize; + + match df.get_check_method() { + CheckingMethod::Name => { + duplicates_number = information.number_of_duplicated_files_by_name; + duplicates_size = 0; + duplicates_group = information.number_of_groups_by_name; + } + 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; + } + 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; + } + CheckingMethod::SizeName => { + duplicates_number = information.number_of_duplicated_files_by_size_name; + duplicates_size = information.lost_space_by_size; + duplicates_group = information.number_of_groups_by_size_name; + } + CheckingMethod::None => { + panic!(); + } + } + if duplicates_size == 0 { + entry_info.set_text( + flg!( + "compute_found_duplicates_name", + generate_translation_hashmap(vec![("number_files", duplicates_number.to_string()), ("number_groups", duplicates_group.to_string())]) + ) + .as_str(), + ); + } else { + entry_info.set_text( + flg!( + "compute_found_duplicates_hash_size", + generate_translation_hashmap(vec![ + ("number_files", duplicates_number.to_string()), + ("number_groups", duplicates_group.to_string()), + ("size", format_size(duplicates_size, BINARY)) + ]) + ) + .as_str(), + ); + } + + // Create GUI + { + let list_store = get_list_store(tree_view_duplicate_finder); + + if df.get_use_reference() { + match df.get_check_method() { + CheckingMethod::Name => { + let btreemap = df.get_files_with_identical_name_referenced(); + + for (_name, (base_file_entry, vector)) in btreemap.iter().rev() { + let vector = vector_sort_unstable_entry_by_path(vector); + let (directory, file) = split_path(&base_file_entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, base_file_entry.size, base_file_entry.modified_date, true, true); + + for entry in vector { + let (directory, file) = split_path(&entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, entry.size, entry.modified_date, false, true); + } + } + } + CheckingMethod::Hash => { + let btreemap = df.get_files_with_identical_hashes_referenced(); + + for (_size, vectors_vector) in btreemap.iter().rev() { + for (base_file_entry, vector) in vectors_vector { + let vector = vector_sort_unstable_entry_by_path(vector); + let (directory, file) = split_path(&base_file_entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, base_file_entry.size, base_file_entry.modified_date, true, true); + for entry in vector { + let (directory, file) = split_path(&entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, entry.size, entry.modified_date, false, true); + } + } + } + } + CheckingMethod::Size => { + let btreemap = df.get_files_with_identical_size_referenced(); + + for (_size, (base_file_entry, vector)) in btreemap.iter().rev() { + let vector = vector_sort_unstable_entry_by_path(vector); + let (directory, file) = split_path(&base_file_entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, base_file_entry.size, base_file_entry.modified_date, true, true); + for entry in vector { + let (directory, file) = split_path(&entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, entry.size, entry.modified_date, false, true); + } + } + } + CheckingMethod::SizeName => { + let btreemap = df.get_files_with_identical_size_names_referenced(); + + for (_size, (base_file_entry, vector)) in btreemap.iter().rev() { + let vector = vector_sort_unstable_entry_by_path(vector); + let (directory, file) = split_path(&base_file_entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, base_file_entry.size, base_file_entry.modified_date, true, true); + for entry in vector { + let (directory, file) = split_path(&entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, entry.size, entry.modified_date, false, true); + } + } + } + CheckingMethod::None => { + panic!(); + } + } + } else { + match df.get_check_method() { + CheckingMethod::Name => { + let btreemap = df.get_files_sorted_by_names(); + + for (_name, vector) in btreemap.iter().rev() { + let vector = vector_sort_unstable_entry_by_path(vector); + duplicates_add_to_list_store(&list_store, "", "", 0, 0, true, false); + for entry in vector { + let (directory, file) = split_path(&entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, entry.size, entry.modified_date, false, false); + } + } + } + CheckingMethod::Hash => { + let btreemap = df.get_files_sorted_by_hash(); + + for (_size, vectors_vector) in btreemap.iter().rev() { + for vector in vectors_vector { + let vector = vector_sort_unstable_entry_by_path(vector); + duplicates_add_to_list_store(&list_store, "", "", 0, 0, true, false); + + for entry in vector { + let (directory, file) = split_path(&entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, entry.size, entry.modified_date, false, false); + } + } + } + } + CheckingMethod::Size => { + let btreemap = df.get_files_sorted_by_size(); + + for (_size, vector) in btreemap.iter().rev() { + let vector = vector_sort_unstable_entry_by_path(vector); + duplicates_add_to_list_store(&list_store, "", "", 0, 0, true, false); + + for entry in vector { + let (directory, file) = split_path(&entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, entry.size, entry.modified_date, false, false); + } + } + } + CheckingMethod::SizeName => { + let btreemap = df.get_files_sorted_by_size_name(); + + for (_size, vector) in btreemap.iter().rev() { + let vector = vector_sort_unstable_entry_by_path(vector); + duplicates_add_to_list_store(&list_store, "", "", 0, 0, true, false); + + for entry in vector { + let (directory, file) = split_path(&entry.path); + duplicates_add_to_list_store(&list_store, &file, &directory, entry.size, entry.modified_date, false, false); + } + } + } + CheckingMethod::None => { + panic!(); + } + } + } + print_text_messages_to_text_view(text_messages, text_view_errors); + } + + // Set state + { + *shared_state.borrow_mut() = df; + + set_specific_buttons_as_active(shared_buttons, &NotebookMainEnum::Duplicate, duplicates_number > 0); + + set_buttons( + &mut *shared_buttons.borrow_mut().get_mut(&NotebookMainEnum::Duplicate).unwrap(), + buttons_array, + buttons_names, + ); + } + } +} + +fn vector_sort_unstable_entry_by_path(vector: &Vec) -> Vec { + if vector.len() >= 2 { + let mut vector = vector.clone(); + vector.sort_unstable_by_key(|e| { + let t = split_path(e.path.as_path()); + (t.0, t.1) + }); + vector + } else { + vector.clone() + } +} +fn vector_sort_simple_unstable_entry_by_path(vector: &[FileEntry]) -> Vec { + let mut vector = vector.to_owned(); + vector.sort_unstable_by_key(|e| { + let t = split_path(e.path.as_path()); + (t.0, t.1) + }); + vector +} + +fn duplicates_add_to_list_store(list_store: &ListStore, file: &str, directory: &str, size: u64, modified_date: u64, is_header: bool, is_reference_folder: bool) { + const COLUMNS_NUMBER: usize = 11; + let size_str; + let string_date; + let color = if is_header { HEADER_ROW_COLOR } else { MAIN_ROW_COLOR }; + + if is_header && !is_reference_folder { + size_str = String::new(); + string_date = String::new(); + } else { + size_str = format_size(size, BINARY); + string_date = NaiveDateTime::from_timestamp_opt(modified_date as i64, 0).unwrap().to_string(); + }; + + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsDuplicates::ActivatableSelectButton as u32, &(!is_header)), + (ColumnsDuplicates::SelectionButton as u32, &false), + (ColumnsDuplicates::Size as u32, &size_str), + (ColumnsDuplicates::SizeAsBytes as u32, &size), + (ColumnsDuplicates::Name as u32, &file), + (ColumnsDuplicates::Path as u32, &directory), + (ColumnsDuplicates::Modification as u32, &string_date), + (ColumnsDuplicates::ModificationAsSecs as u32, &modified_date), + (ColumnsDuplicates::Color as u32, &color), + (ColumnsDuplicates::IsHeader as u32, &is_header), + (ColumnsDuplicates::TextColor as u32, &TEXT_COLOR), + ]; + list_store.set(&list_store.append(), &values); +} + +fn similar_images_add_to_list_store( + list_store: &ListStore, + file: &str, + directory: &str, + size: u64, + modified_date: u64, + dimensions: &str, + similarity: u32, + hash_size: u8, + is_header: bool, + is_reference_folder: bool, +) { + const COLUMNS_NUMBER: usize = 13; + let size_str; + let string_date; + let similarity_string; + let color = if is_header { HEADER_ROW_COLOR } else { MAIN_ROW_COLOR }; + if is_header && !is_reference_folder { + size_str = String::new(); + string_date = String::new(); + similarity_string = String::new(); + } else { + size_str = format_size(size, BINARY); + string_date = NaiveDateTime::from_timestamp_opt(modified_date as i64, 0).unwrap().to_string(); + similarity_string = similar_images::get_string_from_similarity(&similarity, hash_size); + }; + + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsSimilarImages::ActivatableSelectButton as u32, &(!is_header)), + (ColumnsSimilarImages::SelectionButton as u32, &false), + (ColumnsSimilarImages::Similarity as u32, &similarity_string), + (ColumnsSimilarImages::Size as u32, &size_str), + (ColumnsSimilarImages::SizeAsBytes as u32, &size), + (ColumnsSimilarImages::Dimensions as u32, &dimensions), + (ColumnsSimilarImages::Name as u32, &file), + (ColumnsSimilarImages::Path as u32, &directory), + (ColumnsSimilarImages::Modification as u32, &string_date), + (ColumnsSimilarImages::ModificationAsSecs as u32, &modified_date), + (ColumnsSimilarImages::Color as u32, &color), + (ColumnsSimilarImages::IsHeader as u32, &is_header), + (ColumnsSimilarImages::TextColor as u32, &TEXT_COLOR), + ]; + list_store.set(&list_store.append(), &values); +} + +fn similar_videos_add_to_list_store(list_store: &ListStore, file: &str, directory: &str, size: u64, modified_date: u64, is_header: bool, is_reference_folder: bool) { + const COLUMNS_NUMBER: usize = 11; + let size_str; + let string_date; + let color = if is_header { HEADER_ROW_COLOR } else { MAIN_ROW_COLOR }; + if is_header && !is_reference_folder { + size_str = String::new(); + string_date = String::new(); + } else { + size_str = format_size(size, BINARY); + string_date = NaiveDateTime::from_timestamp_opt(modified_date as i64, 0).unwrap().to_string(); + }; + + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsSimilarVideos::ActivatableSelectButton as u32, &(!is_header)), + (ColumnsSimilarVideos::SelectionButton as u32, &false), + (ColumnsSimilarVideos::Size as u32, &size_str), + (ColumnsSimilarVideos::SizeAsBytes as u32, &size), + (ColumnsSimilarVideos::Name as u32, &file), + (ColumnsSimilarVideos::Path as u32, &directory), + (ColumnsSimilarVideos::Modification as u32, &string_date), + (ColumnsSimilarVideos::ModificationAsSecs as u32, &modified_date), + (ColumnsSimilarVideos::Color as u32, &color), + (ColumnsSimilarVideos::IsHeader as u32, &false), + (ColumnsSimilarVideos::TextColor as u32, &TEXT_COLOR), + ]; + + list_store.set(&list_store.append(), &values); +} + +fn same_music_add_to_list_store( + list_store: &ListStore, + file: &str, + directory: &str, + size: u64, + modified_date: u64, + track_title: &str, + track_artist: &str, + track_year: &str, + track_bitrate: u32, + bitrate_string: &str, + track_genre: &str, + track_length: &str, + is_header: bool, + is_reference_folder: bool, +) { + const COLUMNS_NUMBER: usize = 18; + let size_str; + let string_date; + let color = if is_header { HEADER_ROW_COLOR } else { MAIN_ROW_COLOR }; + if is_header && !is_reference_folder { + size_str = String::new(); + string_date = String::new(); + } else { + size_str = format_size(size, BINARY); + string_date = NaiveDateTime::from_timestamp_opt(modified_date as i64, 0).unwrap().to_string(); + }; + + let values: [(u32, &dyn ToValue); COLUMNS_NUMBER] = [ + (ColumnsSameMusic::ActivatableSelectButton as u32, &(!is_header)), + (ColumnsSameMusic::SelectionButton as u32, &false), + (ColumnsSameMusic::Size as u32, &size_str), + (ColumnsSameMusic::SizeAsBytes as u32, &size), + (ColumnsSameMusic::Name as u32, &file), + (ColumnsSameMusic::Path as u32, &directory), + (ColumnsSameMusic::Title as u32, &track_title), + (ColumnsSameMusic::Artist as u32, &track_artist), + (ColumnsSameMusic::Year as u32, &track_year), + (ColumnsSameMusic::Genre as u32, &track_genre), + (ColumnsSameMusic::Bitrate as u32, &bitrate_string), + (ColumnsSameMusic::BitrateAsNumber as u32, &track_bitrate), + (ColumnsSameMusic::Length as u32, &track_length), + (ColumnsSameMusic::Modification as u32, &string_date), + (ColumnsSameMusic::ModificationAsSecs as u32, &modified_date), + (ColumnsSameMusic::Color as u32, &color), + (ColumnsSameMusic::IsHeader as u32, &is_header), + (ColumnsSameMusic::TextColor as u32, &TEXT_COLOR), + ]; + + list_store.set(&list_store.append(), &values); +} + +fn set_specific_buttons_as_active(buttons_array: &Rc>>>, notebook_enum: &NotebookMainEnum, value_to_set: bool) { + let mut b_mut = buttons_array.borrow_mut(); + let butt = b_mut.get_mut(notebook_enum).unwrap(); + let allowed_buttons = NOTEBOOKS_INFO[*notebook_enum as usize].bottom_buttons; + for i in allowed_buttons { + *butt.get_mut(i).unwrap() = value_to_set; } } diff --git a/czkawka_gui/src/connect_things/connect_button_compare.rs b/czkawka_gui/src/connect_things/connect_button_compare.rs index 026e0ed..5228520 100644 --- a/czkawka_gui/src/connect_things/connect_button_compare.rs +++ b/czkawka_gui/src/connect_things/connect_button_compare.rs @@ -395,13 +395,13 @@ fn generate_cache_for_results(vector_with_path: Vec<(String, String, TreePath)>) #[allow(clippy::never_loop)] loop { - let Some(pixbuf_big) = resize_pixbuf_dimension(&pixbuf, (BIG_PREVIEW_SIZE, BIG_PREVIEW_SIZE), InterpType::Bilinear) else{ - println!("Failed to resize image {full_path}."); - break; + let Some(pixbuf_big) = resize_pixbuf_dimension(&pixbuf, (BIG_PREVIEW_SIZE, BIG_PREVIEW_SIZE), InterpType::Bilinear) else { + println!("Failed to resize image {full_path}."); + break; }; - let Some(pixbuf_small) = resize_pixbuf_dimension(&pixbuf_big, (SMALL_PREVIEW_SIZE, SMALL_PREVIEW_SIZE), InterpType::Bilinear) else { - println!("Failed to resize image {full_path}."); - break; + let Some(pixbuf_small) = resize_pixbuf_dimension(&pixbuf_big, (SMALL_PREVIEW_SIZE, SMALL_PREVIEW_SIZE), InterpType::Bilinear) else { + println!("Failed to resize image {full_path}."); + break; }; big_img.set_from_pixbuf(Some(&pixbuf_big)); diff --git a/czkawka_gui/src/connect_things/connect_button_sort.rs b/czkawka_gui/src/connect_things/connect_button_sort.rs index 810dc00..cb90d6e 100644 --- a/czkawka_gui/src/connect_things/connect_button_sort.rs +++ b/czkawka_gui/src/connect_things/connect_button_sort.rs @@ -1,9 +1,10 @@ +use gtk4::prelude::*; + use crate::gui_structs::gui_data::GuiData; use crate::gui_structs::gui_popovers_sort::GuiSortPopovers; use crate::help_functions::PopoverTypes; use crate::notebook_enums::{to_notebook_main_enum, NotebookMainEnum}; use crate::notebook_info::NOTEBOOKS_INFO; -use gtk4::prelude::*; pub fn connect_button_sort(gui_data: &GuiData) { let popovers_sort = gui_data.popovers_sort.clone(); diff --git a/czkawka_gui/src/connect_things/connect_duplicate_buttons.rs b/czkawka_gui/src/connect_things/connect_duplicate_buttons.rs index b687243..4b88d15 100644 --- a/czkawka_gui/src/connect_things/connect_duplicate_buttons.rs +++ b/czkawka_gui/src/connect_things/connect_duplicate_buttons.rs @@ -21,7 +21,7 @@ pub fn connect_duplicate_combo_box(gui_data: &GuiData) { label_duplicate_hash_type.set_visible(false); } - if DUPLICATES_CHECK_METHOD_COMBO_BOX[chosen_index as usize].check_method == CheckingMethod::Name { + if [CheckingMethod::Name, CheckingMethod::SizeName].contains(&DUPLICATES_CHECK_METHOD_COMBO_BOX[chosen_index as usize].check_method) { check_button_duplicate_case_sensitive_name.set_visible(true); } else { check_button_duplicate_case_sensitive_name.set_visible(false); diff --git a/czkawka_gui/src/connect_things/connect_popovers_sort.rs b/czkawka_gui/src/connect_things/connect_popovers_sort.rs index 798fde4..62156cc 100644 --- a/czkawka_gui/src/connect_things/connect_popovers_sort.rs +++ b/czkawka_gui/src/connect_things/connect_popovers_sort.rs @@ -1,6 +1,7 @@ +use std::fmt::Debug; + use gtk4::prelude::*; use gtk4::{ListStore, TreeIter}; -use std::fmt::Debug; use crate::gui_structs::gui_data::GuiData; use crate::help_functions::*; @@ -118,10 +119,11 @@ pub fn connect_popover_sort(gui_data: &GuiData) { #[cfg(test)] mod test { - use crate::connect_things::connect_popovers_sort::{popover_sort_general, sort_iters}; use gtk4::prelude::*; use gtk4::{Popover, TreeView}; + use crate::connect_things::connect_popovers_sort::{popover_sort_general, sort_iters}; + #[gtk4::test] fn test_sort_iters() { let columns_types: &[glib::types::Type] = &[glib::types::Type::U32, glib::types::Type::STRING]; diff --git a/czkawka_gui/src/connect_things/connect_progress_window.rs b/czkawka_gui/src/connect_things/connect_progress_window.rs index 7821db0..825281c 100644 --- a/czkawka_gui/src/connect_things/connect_progress_window.rs +++ b/czkawka_gui/src/connect_things/connect_progress_window.rs @@ -109,6 +109,16 @@ pub fn connect_progress_window( )); taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE); } + common_dir_traversal::CheckingMethod::SizeName => { + label_stage.show(); + grid_progress_stages.hide(); + + label_stage.set_text(&flg!( + "progress_scanning_size_name", + generate_translation_hashmap(vec![("file_number", item.entries_checked.to_string())]) + )); + taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE); + } common_dir_traversal::CheckingMethod::Size => { label_stage.show(); grid_progress_stages.hide(); diff --git a/czkawka_gui/src/gui_structs/gui_main_notebook.rs b/czkawka_gui/src/gui_structs/gui_main_notebook.rs index 8356888..11484c0 100644 --- a/czkawka_gui/src/gui_structs/gui_main_notebook.rs +++ b/czkawka_gui/src/gui_structs/gui_main_notebook.rs @@ -549,9 +549,8 @@ impl GuiMainNotebook { CheckingMethod::Hash => flg!("duplicate_mode_hash_combo_box"), CheckingMethod::Size => flg!("duplicate_mode_size_combo_box"), CheckingMethod::Name => flg!("duplicate_mode_name_combo_box"), - _ => { - panic!() - } + CheckingMethod::SizeName => flg!("duplicate_mode_size_name_combo_box"), + CheckingMethod::None => panic!(), }; self.combo_box_duplicate_check_method.append_text(&text); } diff --git a/czkawka_gui/src/help_combo_box.rs b/czkawka_gui/src/help_combo_box.rs index 525e997..9fc7291 100644 --- a/czkawka_gui/src/help_combo_box.rs +++ b/czkawka_gui/src/help_combo_box.rs @@ -29,7 +29,7 @@ pub struct CheckMethodStruct { pub check_method: CheckingMethod, } -pub const DUPLICATES_CHECK_METHOD_COMBO_BOX: [CheckMethodStruct; 3] = [ +pub const DUPLICATES_CHECK_METHOD_COMBO_BOX: [CheckMethodStruct; 4] = [ CheckMethodStruct { eng_name: "Hash", check_method: CheckingMethod::Hash, @@ -42,6 +42,10 @@ pub const DUPLICATES_CHECK_METHOD_COMBO_BOX: [CheckMethodStruct; 3] = [ eng_name: "Name", check_method: CheckingMethod::Name, }, + CheckMethodStruct { + eng_name: "Size and Name", + check_method: CheckingMethod::SizeName, + }, ]; #[derive(Copy, Clone)] diff --git a/czkawka_gui/src/help_functions.rs b/czkawka_gui/src/help_functions.rs index ee8abec..f530101 100644 --- a/czkawka_gui/src/help_functions.rs +++ b/czkawka_gui/src/help_functions.rs @@ -244,8 +244,8 @@ pub fn get_string_from_list_store(tree_view: &TreeView, column_full_path: i32, c let mut string_vector: Vec = Vec::new(); - let Some(tree_iter) = list_store.iter_first() else { - return string_vector; + let Some(tree_iter) = list_store.iter_first() else { + return string_vector; }; match column_selection { Some(column_selection) => loop { diff --git a/czkawka_gui/src/initialize_gui.rs b/czkawka_gui/src/initialize_gui.rs index 504f305..2c4690b 100644 --- a/czkawka_gui/src/initialize_gui.rs +++ b/czkawka_gui/src/initialize_gui.rs @@ -1,5 +1,4 @@ use std::cell::RefCell; - use std::path::Path; use std::rc::Rc; diff --git a/czkawka_gui/src/notebook_info.rs b/czkawka_gui/src/notebook_info.rs index e5fc2a5..1438be0 100644 --- a/czkawka_gui/src/notebook_info.rs +++ b/czkawka_gui/src/notebook_info.rs @@ -1,6 +1,6 @@ use crate::help_functions::{ - ColumnsBadExtensions, ColumnsBigFiles, ColumnsBrokenFiles, ColumnsDuplicates, ColumnsEmptyFiles, ColumnsEmptyFolders, ColumnsInvalidSymlinks, ColumnsSameMusic, - ColumnsSimilarImages, ColumnsSimilarVideos, ColumnsTemporaryFiles, PopoverTypes, + BottomButtonsEnum, ColumnsBadExtensions, ColumnsBigFiles, ColumnsBrokenFiles, ColumnsDuplicates, ColumnsEmptyFiles, ColumnsEmptyFolders, ColumnsInvalidSymlinks, + ColumnsSameMusic, ColumnsSimilarImages, ColumnsSimilarVideos, ColumnsTemporaryFiles, PopoverTypes, }; use crate::notebook_enums::{NotebookMainEnum, NUMBER_OF_NOTEBOOK_MAIN_TABS}; @@ -17,6 +17,7 @@ pub struct NotebookObject { pub column_size_as_bytes: Option, pub column_modification_as_secs: Option, pub columns_types: &'static [glib::types::Type], + pub bottom_buttons: &'static [BottomButtonsEnum], } pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ @@ -52,6 +53,15 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::BOOL, // IsHeader glib::types::Type::STRING, // TextColor ], + bottom_buttons: &[ + BottomButtonsEnum::Save, + BottomButtonsEnum::Delete, + BottomButtonsEnum::Select, + BottomButtonsEnum::Sort, + BottomButtonsEnum::Symlink, + BottomButtonsEnum::Hardlink, + BottomButtonsEnum::Move, + ], }, NotebookObject { notebook_type: NotebookMainEnum::EmptyDirectories, @@ -72,6 +82,7 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::STRING, // Modification glib::types::Type::U64, // ModificationAsSecs ], + bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], }, NotebookObject { notebook_type: NotebookMainEnum::BigFiles, @@ -94,6 +105,7 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::U64, // SizeAsBytes glib::types::Type::U64, // ModificationAsSecs ], + bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], }, NotebookObject { notebook_type: NotebookMainEnum::EmptyFiles, @@ -114,6 +126,7 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::STRING, // Modification glib::types::Type::U64, // ModificationAsSecs ], + bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], }, NotebookObject { notebook_type: NotebookMainEnum::Temporary, @@ -134,6 +147,7 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::STRING, // Modification glib::types::Type::U64, // ModificationAsSecs ], + bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], }, NotebookObject { notebook_type: NotebookMainEnum::SimilarImages, @@ -162,6 +176,16 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::BOOL, // IsHeader glib::types::Type::STRING, // TextColor ], + bottom_buttons: &[ + BottomButtonsEnum::Save, + BottomButtonsEnum::Delete, + BottomButtonsEnum::Select, + BottomButtonsEnum::Sort, + BottomButtonsEnum::Symlink, + BottomButtonsEnum::Hardlink, + BottomButtonsEnum::Move, + BottomButtonsEnum::Compare, + ], }, NotebookObject { notebook_type: NotebookMainEnum::SimilarVideos, @@ -188,6 +212,15 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::BOOL, // IsHeader glib::types::Type::STRING, // TextColor ], + bottom_buttons: &[ + BottomButtonsEnum::Save, + BottomButtonsEnum::Delete, + BottomButtonsEnum::Select, + BottomButtonsEnum::Sort, + BottomButtonsEnum::Symlink, + BottomButtonsEnum::Hardlink, + BottomButtonsEnum::Move, + ], }, NotebookObject { notebook_type: NotebookMainEnum::SameMusic, @@ -221,6 +254,15 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::BOOL, // IsHeader glib::types::Type::STRING, // TextColor ], + bottom_buttons: &[ + BottomButtonsEnum::Save, + BottomButtonsEnum::Delete, + BottomButtonsEnum::Select, + BottomButtonsEnum::Sort, + BottomButtonsEnum::Symlink, + BottomButtonsEnum::Hardlink, + BottomButtonsEnum::Move, + ], }, NotebookObject { notebook_type: NotebookMainEnum::Symlinks, @@ -243,6 +285,7 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::STRING, // Modification glib::types::Type::U64, // ModificationAsSecs ], + bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], }, NotebookObject { notebook_type: NotebookMainEnum::BrokenFiles, @@ -264,6 +307,7 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::STRING, // Modification glib::types::Type::U64, // ModificationAsSecs ], + bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], }, NotebookObject { notebook_type: NotebookMainEnum::BadExtensions, @@ -286,5 +330,6 @@ pub static NOTEBOOKS_INFO: [NotebookObject; NUMBER_OF_NOTEBOOK_MAIN_TABS] = [ glib::types::Type::STRING, // Modification glib::types::Type::U64, // ModificationAsSecs ], + bottom_buttons: &[BottomButtonsEnum::Save, BottomButtonsEnum::Delete, BottomButtonsEnum::Select, BottomButtonsEnum::Move], }, ]; diff --git a/czkawka_gui/src/saving_loading.rs b/czkawka_gui/src/saving_loading.rs index b380d33..d4db3eb 100644 --- a/czkawka_gui/src/saving_loading.rs +++ b/czkawka_gui/src/saving_loading.rs @@ -5,11 +5,11 @@ use std::io::{Read, Write}; use std::path::{Path, PathBuf}; use std::{env, fs}; -use czkawka_core::common::get_default_number_of_threads; use directories_next::ProjectDirs; use gtk4::prelude::*; use gtk4::{ComboBoxText, ScrolledWindow, TextView, TreeView}; +use czkawka_core::common::get_default_number_of_threads; use czkawka_core::common_dir_traversal::CheckingMethod; use czkawka_core::similar_images::SIMILAR_VALUES; @@ -938,7 +938,7 @@ pub fn load_configuration( main_notebook.label_duplicate_hash_type.set_visible(false); } - if DUPLICATES_CHECK_METHOD_COMBO_BOX[combo_chosen_index as usize].check_method == CheckingMethod::Name { + if [CheckingMethod::Name, CheckingMethod::SizeName].contains(&DUPLICATES_CHECK_METHOD_COMBO_BOX[combo_chosen_index as usize].check_method) { main_notebook.check_button_duplicate_case_sensitive_name.set_visible(true); } else { main_notebook.check_button_duplicate_case_sensitive_name.set_visible(false);