From 378fa1fd6e116c1c423f1a666fa7dcf9bce505cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= <41945903+qarmin@users.noreply.github.com> Date: Wed, 14 Feb 2024 17:45:25 +0100 Subject: [PATCH] Excluded extensions and krokiet new features (#1184) * AVC * Import split * Default thread size * Hen * Allowed extensions * Perf * Connect * Excluded * Zmiany * Optimization * 4.10 * At once * Included * Chang * VD * VD * Hashes * Wersja * SD * Up * Up * 2024 * Dup * Slint files * Added select * Selections * Fix * LTO * Actions * Added popup delete * AB * V4 * Release * LTO * Basic moving * Commonsy * Moving probably works * Popup move --- .github/workflows/linux_cli.yml | 10 +- .github/workflows/linux_cli_eyra.yml | 10 +- .github/workflows/linux_gui.yml | 44 +- .github/workflows/mac.yml | 18 +- .github/workflows/quality.yml | 2 +- .github/workflows/windows.yml | 95 +- .gitignore | 5 +- Cargo.lock | 1132 ++++++++--------- Cargo.toml | 1 + Changelog.md | 9 +- ci_tester/src/main.rs | 3 +- czkawka_cli/Cargo.toml | 4 +- czkawka_cli/LICENSE | 2 +- czkawka_cli/README.md | 2 +- czkawka_cli/src/commands.rs | 2 + czkawka_cli/src/main.rs | 6 +- czkawka_core/Cargo.toml | 11 +- czkawka_core/LICENSE | 2 +- czkawka_core/src/bad_extensions.rs | 2 +- czkawka_core/src/big_file.rs | 3 +- czkawka_core/src/broken_files.rs | 7 +- czkawka_core/src/common.rs | 60 +- czkawka_core/src/common_cache.rs | 16 +- czkawka_core/src/common_dir_traversal.rs | 66 +- czkawka_core/src/common_directory.rs | 25 +- czkawka_core/src/common_extensions.rs | 82 +- czkawka_core/src/common_items.rs | 4 +- czkawka_core/src/common_messages.rs | 9 + czkawka_core/src/common_tool.rs | 15 +- czkawka_core/src/common_traits.rs | 5 +- czkawka_core/src/duplicate.rs | 4 +- czkawka_core/src/empty_files.rs | 3 +- czkawka_core/src/empty_folder.rs | 96 +- czkawka_core/src/invalid_symlinks.rs | 3 +- czkawka_core/src/same_music.rs | 8 +- czkawka_core/src/similar_images.rs | 170 ++- czkawka_core/src/similar_videos.rs | 15 +- czkawka_core/src/temporary.rs | 3 +- czkawka_gui/Cargo.toml | 14 +- czkawka_gui/LICENSE | 2 +- czkawka_gui/README.md | 2 +- czkawka_gui/i18n/en/czkawka_gui.ftl | 6 + czkawka_gui/icons/czk_hide_down.svg | 20 +- czkawka_gui/icons/czk_hide_up.svg | 20 +- czkawka_gui/icons/czk_sort.svg | 4 +- czkawka_gui/src/compute_results.rs | 444 +++---- .../connect_things/connect_button_compare.rs | 9 +- .../connect_things/connect_button_delete.rs | 38 +- .../connect_things/connect_button_hardlink.rs | 28 +- .../src/connect_things/connect_button_move.rs | 22 +- .../src/connect_things/connect_button_save.rs | 3 +- .../connect_things/connect_button_search.rs | 440 +++---- .../connect_things/connect_popovers_select.rs | 5 +- .../connect_things/connect_progress_window.rs | 8 +- .../connect_selection_of_directories.rs | 2 +- .../src/connect_things/connect_settings.rs | 8 +- czkawka_gui/src/gui_structs/gui_about.rs | 2 +- .../src/gui_structs/gui_progress_dialog.rs | 6 +- .../src/gui_structs/gui_upper_notebook.rs | 9 + czkawka_gui/src/help_functions.rs | 5 +- czkawka_gui/src/initialize_gui.rs | 23 +- czkawka_gui/src/main.rs | 12 +- czkawka_gui/src/saving_loading.rs | 344 ++--- czkawka_gui/ui/about_dialog.ui | 2 +- czkawka_gui/ui/czkawka.cmb | 9 +- czkawka_gui/ui/main_window.ui | 11 + czkawka_gui/ui/progress.ui | 2 +- instructions/Compilation.md | 2 +- krokiet/Cargo.toml | 18 +- krokiet/LICENSE | 2 +- krokiet/LICENSE_MIT_CODE | 2 +- krokiet/README.md | 23 +- krokiet/src/common.rs | 152 ++- krokiet/src/connect_delete.rs | 270 +--- krokiet/src/connect_directories_changes.rs | 128 +- krokiet/src/connect_move.rs | 122 ++ krokiet/src/connect_open.rs | 3 +- krokiet/src/connect_progress_receiver.rs | 8 +- krokiet/src/connect_scan.rs | 162 ++- krokiet/src/connect_select.rs | 216 ++++ krokiet/src/connect_show_preview.rs | 42 +- krokiet/src/connect_stop.rs | 3 +- krokiet/src/connect_translation.rs | 6 +- krokiet/src/main.rs | 47 +- krokiet/src/model_operations.rs | 238 ++++ krokiet/src/set_initial_gui_info.rs | 5 +- krokiet/src/settings.rs | 37 +- krokiet/ui/action_buttons.slint | 47 +- krokiet/ui/bottom_panel.slint | 15 +- krokiet/ui/callabler.slint | 10 +- krokiet/ui/common.slint | 31 +- krokiet/ui/gui_state.slint | 6 +- krokiet/ui/included_directories.slint | 133 +- krokiet/ui/left_side_panel.slint | 3 + krokiet/ui/main_lists.slint | 8 +- krokiet/ui/main_window.slint | 69 +- krokiet/ui/popup_delete.slint | 73 ++ krokiet/ui/popup_move_folders.slint | 94 ++ krokiet/ui/popup_select.slint | 74 -- krokiet/ui/popup_select_results.slint | 51 + krokiet/ui/selectable_tree_view.slint | 18 +- krokiet/ui/settings.slint | 12 +- krokiet/ui/settings_list.slint | 14 +- krokiet/ui/tool_settings.slint | 9 +- 104 files changed, 3087 insertions(+), 2520 deletions(-) create mode 100644 krokiet/src/connect_move.rs create mode 100644 krokiet/src/connect_select.rs create mode 100644 krokiet/src/model_operations.rs create mode 100644 krokiet/ui/popup_delete.slint create mode 100644 krokiet/ui/popup_move_folders.slint delete mode 100644 krokiet/ui/popup_select.slint create mode 100644 krokiet/ui/popup_select_results.slint diff --git a/.github/workflows/linux_cli.yml b/.github/workflows/linux_cli.yml index 053f22f..76e2e15 100644 --- a/.github/workflows/linux_cli.yml +++ b/.github/workflows/linux_cli.yml @@ -12,11 +12,11 @@ jobs: linux-cli: strategy: matrix: - toolchain: [ stable, 1.72.1 ] + toolchain: [ stable, 1.74.0 ] type: [ release ] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install basic libraries run: sudo apt update || true; sudo apt install libheif-dev ffmpeg -y @@ -24,12 +24,16 @@ jobs: - name: Setup rust version run: rustup default ${{ matrix.toolchain }} + - name: Enable LTO + run: sed -i 's/#lto = "fat"/lto = "fat"/g' Cargo.toml + if: ${{ (matrix.type == 'release') }} + - name: Build Release run: cargo build --release --bin czkawka_cli if: ${{ (matrix.type == 'release') }} - name: Store Linux CLI - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: czkawka_cli-${{ runner.os }}-${{ matrix.toolchain }} path: target/release/czkawka_cli diff --git a/.github/workflows/linux_cli_eyra.yml b/.github/workflows/linux_cli_eyra.yml index beda1f9..9d1854a 100644 --- a/.github/workflows/linux_cli_eyra.yml +++ b/.github/workflows/linux_cli_eyra.yml @@ -16,14 +16,14 @@ jobs: type: [ release ] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install basic libraries run: sudo apt update || true; sudo apt install -y ffmpeg # New versions of nightly rust may call new unimplemented in eyra functions, so use const version - name: Setup rust version - run: rustup default nightly-2023-12-14 + run: rustup default nightly-2024-02-06 - name: Add eyra run: | @@ -32,12 +32,16 @@ jobs: echo 'fn main() { println!("cargo:rustc-link-arg=-nostartfiles"); }' > build.rs cd .. + - name: Enable LTO + run: sed -i 's/#lto = "fat"/lto = "fat"/g' Cargo.toml + if: ${{ (matrix.type == 'release') }} + - name: Build Release run: cargo build --release --bin czkawka_cli if: ${{ (matrix.type == 'release') }} - name: Store Linux CLI - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: czkawka_cli-${{ runner.os }}-${{ matrix.toolchain }} path: target/release/czkawka_cli diff --git a/.github/workflows/linux_gui.yml b/.github/workflows/linux_gui.yml index aca39f5..5d3491d 100644 --- a/.github/workflows/linux_gui.yml +++ b/.github/workflows/linux_gui.yml @@ -12,21 +12,25 @@ jobs: linux-krokiet-gui: strategy: matrix: - toolchain: [ stable, 1.72.1 ] + toolchain: [ stable, 1.74.0 ] type: [ release ] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup rust version run: rustup default ${{ matrix.toolchain }} + - name: Enable LTO + run: sed -i 's/#lto = "fat"/lto = "fat"/g' Cargo.toml + if: ${{ (matrix.type == 'release') }} + - name: Build Release Krokiet run: cargo build --release --bin krokiet if: ${{ (matrix.type == 'release') }} - name: Store Linux GUI Krokiet - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: krokiet-${{ runner.os }}-${{ matrix.toolchain }} path: target/release/krokiet @@ -35,11 +39,11 @@ jobs: linux-krokiet-gui-heif: strategy: matrix: - toolchain: [ stable, 1.72.1 ] + toolchain: [ stable, 1.74.0 ] type: [ release ] runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install basic libraries run: sudo apt update || true; sudo apt install libheif-dev libraw-dev -y @@ -47,12 +51,16 @@ jobs: - name: Setup rust version run: rustup default ${{ matrix.toolchain }} + - name: Enable LTO + run: sed -i 's/#lto = "fat"/lto = "fat"/g' Cargo.toml + if: ${{ (matrix.type == 'release') }} + - name: Build Release Krokiet heif run: cargo build --release --bin krokiet --features "heif,libraw" if: ${{ (matrix.type == 'release') }} - name: Store Linux GUI Krokiet heif libraw - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: krokiet-${{ runner.os }}-${{ matrix.toolchain }}-heif-libraw path: target/release/krokiet @@ -61,11 +69,11 @@ jobs: linux-gui: strategy: matrix: - toolchain: [ stable, 1.72.1 ] + toolchain: [ stable, 1.74.0 ] type: [ release ] runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install basic libraries run: sudo apt update || true; sudo apt install libgtk-4-dev libheif-dev libraw-dev -y @@ -73,12 +81,16 @@ jobs: - name: Setup rust version run: rustup default ${{ matrix.toolchain }} + - name: Enable LTO + run: sed -i 's/#lto = "fat"/lto = "fat"/g' Cargo.toml + if: ${{ (matrix.type == 'release') }} + - name: Build Release Heif Libraw run: cargo build --release --bin czkawka_gui --features "heif,libraw" if: ${{ (matrix.type == 'release') }} - name: Store Linux GUI Heif Libraw - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: czkawka_gui-${{ runner.os }}-${{ matrix.toolchain }}-heif-libraw path: target/release/czkawka_gui @@ -90,7 +102,7 @@ jobs: # Only store stable toolchain - name: Store Linux GUI - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: czkawka_gui-${{ runner.os }}-${{ matrix.toolchain }} path: target/release/czkawka_gui @@ -103,7 +115,7 @@ jobs: type: [ release ] runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Dependencies run: sudo apt update || true; sudo apt install libgtk-4-dev libheif-dev librsvg2-dev wget fuse libfuse2 -y @@ -111,6 +123,10 @@ jobs: - name: Setup rust version run: rustup default ${{ matrix.toolchain }} + - name: Enable LTO + run: sed -i 's/#lto = "fat"/lto = "fat"/g' Cargo.toml + if: ${{ (matrix.type == 'release') }} + - name: Build Release run: cargo build --release --bin czkawka_gui @@ -127,7 +143,7 @@ jobs: ./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin gtk --output appimage --icon-file data/icons/com.github.qarmin.czkawka.svg --desktop-file data/com.github.qarmin.czkawka.desktop - name: Store Linux Appimage GUI - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: czkawka_gui-appimage-${{ runner.os }}-${{ matrix.toolchain }} path: Czkawka*.AppImage @@ -144,7 +160,7 @@ jobs: mv out/Czkawka*.AppImage out/czkawka_gui-minimal.AppImage - name: Minimal Appimage Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: czkawka_gui-${{ matrix.toolchain }}_minimal_AppImage path: out/*.AppImage @@ -156,7 +172,7 @@ jobs: type: [ debug ] runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Dependencies run: sudo apt update || true; sudo apt install libgtk-4-dev libheif-dev librsvg2-dev wget fuse libfuse2 -y xvfb diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index e746fcc..1eaf6fa 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -16,7 +16,7 @@ jobs: type: [ release ] runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Homebrew run: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" @@ -30,26 +30,30 @@ jobs: - name: Setup rust version run: rustup default ${{ matrix.toolchain }} + - name: Enable LTO + run: sed -i '' 's/#lto = "fat"/lto = "fat"/g' Cargo.toml + if: ${{ (matrix.type == 'release') }} + - name: Build Release run: cargo build --release if: ${{ matrix.type == 'release'}} - name: Store MacOS CLI - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: czkawka_cli-${{ runner.os }}-${{ matrix.toolchain }} path: target/release/czkawka_cli if: ${{ matrix.type == 'release' }} - name: Store MacOS GUI - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: czkawka_gui-${{ runner.os }}-${{ matrix.toolchain }} path: target/release/czkawka_gui if: ${{ matrix.type == 'release' }} - name: Store MacOS Krokiet - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: krokiet-${{ runner.os }}-${{ matrix.toolchain }} path: target/release/krokiet @@ -60,21 +64,21 @@ jobs: if: ${{ matrix.type == 'release'}} - name: Store MacOS CLI Heif - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: czkawka_cli-${{ runner.os }}-${{ matrix.toolchain }}-heif path: target/release/czkawka_cli if: ${{ matrix.type == 'release' }} - name: Store MacOS GUI Heif - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: czkawka_gui-${{ runner.os }}-${{ matrix.toolchain }}-heif path: target/release/czkawka_gui if: ${{ matrix.type == 'release' }} - name: Store MacOS Krokiet Heif - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: krokiet-${{ runner.os }}-${{ matrix.toolchain }}-heif path: target/release/krokiet diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 60e3bd8..c864a3a 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -12,7 +12,7 @@ jobs: quality: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Gtk 4 run: sudo apt update || true; sudo apt install -y libgtk-4-dev libraw-dev libheif-dev -y diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index f979715..4f31d48 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -16,7 +16,7 @@ jobs: use_heif: [ normal ] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies(mostly sd) run: | @@ -30,11 +30,14 @@ jobs: run: | rustup target add x86_64-pc-windows-gnu + - name: Enable LTO + run: sed -i 's/#lto = "fat"/lto = "fat"/g' Cargo.toml + - name: Compile Krokiet run: cargo build --release --target x86_64-pc-windows-gnu --bin krokiet - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: krokiet-windows-c-on-linux-${{ github.sha }}-${{ matrix.use_heif }} path: | @@ -50,7 +53,7 @@ jobs: run: cargo build --release --target x86_64-pc-windows-gnu --bin krokiet - name: Upload artifacts Console - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: krokiet-windows-c-on-linux-${{ github.sha }}-${{ matrix.use_heif }}-console path: | @@ -64,7 +67,7 @@ jobs: use_heif: [ normal ] runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies(mostly sd) run: | @@ -75,11 +78,14 @@ jobs: - name: Setup rust version run: rustup default stable-x86_64-pc-windows-gnu + - name: Enable LTO + run: sed -i 's/#lto = "fat"/lto = "fat"/g' Cargo.toml + - name: Compile Krokiet run: cargo build --release --bin krokiet - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: krokiet-windows-c-on-windows-${{ github.sha }}-${{ matrix.use_heif }} path: | @@ -95,7 +101,7 @@ jobs: run: cargo build --release --bin krokiet - name: Upload artifacts Console - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: krokiet-windows-c-on-windows-${{ github.sha }}-${{ matrix.use_heif }}-console path: | @@ -103,16 +109,16 @@ jobs: if-no-files-found: error - container: + container_4_10: strategy: fail-fast: false matrix: use_heif: [ non_heif ] runs-on: ubuntu-latest container: - image: ghcr.io/piegamesde/gtk4-cross:gtk-4.6 + image: ghcr.io/mglolenstine/gtk4-cross:gtk-4.10 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install additional dependencies # gio is for the build script run: | @@ -155,20 +161,79 @@ jobs: rm gtk4_theme.zip cd ../.. - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: czkawka-windows-${{ github.sha }}-${{ matrix.use_heif }} + name: czkawka-windows-${{ github.sha }}-${{ matrix.use_heif }}-4.10 + path: | + ./package + if-no-files-found: error + + container_4_6: + strategy: + fail-fast: false + matrix: + use_heif: [ non_heif ] + runs-on: ubuntu-latest + container: + image: ghcr.io/piegamesde/gtk4-cross:gtk-4.6 + steps: + - uses: actions/checkout@v4 + - name: Install additional dependencies + # gio is for the build script + run: | + dnf install curl wget2 unzip mingw64-bzip2.noarch mingw64-poppler mingw64-poppler-glib mingw32-python3 rust-gio-devel adwaita-icon-theme -y && dnf clean all -y + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + source "$HOME/.cargo/env" + rustup target add x86_64-pc-windows-gnu + + - name: Cross compile for Windows + run: | + source "$HOME/.cargo/env" + #!/bin/bash + set -euo pipefail + export PKG_CONFIG_PATH=/usr/lib64/pkgconfig:/usr/share/pkgconfig:$MINGW_PREFIX/lib/pkgconfig/:/usr/x86_64-w64-mingw32/lib/pkgconfig/ + cargo build --target=x86_64-pc-windows-gnu --release --locked + mkdir -p package + cp target/x86_64-pc-windows-gnu/release/czkawka_gui.exe package/ + cp target/x86_64-pc-windows-gnu/release/czkawka_cli.exe package/ + + - name: Package + run: | + #!/bin/bash + set -euo pipefail + cp -t package $(pds -vv -f package/*.exe) + # Add gdbus which is recommended on Windows (why?) + cp $MINGW_PREFIX/bin/gdbus.exe package + # Handle the glib schema compilation as well + glib-compile-schemas $MINGW_PREFIX/share/glib-2.0/schemas/ + mkdir -p package/share/glib-2.0/schemas/ + cp -T $MINGW_PREFIX/share/glib-2.0/schemas/gschemas.compiled package/share/glib-2.0/schemas/gschemas.compiled + # Pixbuf stuff, in order to get SVGs (scalable icons) to load + mkdir -p package/lib/gdk-pixbuf-2.0 + cp -rT $MINGW_PREFIX/lib/gdk-pixbuf-2.0 package/lib/gdk-pixbuf-2.0 + cp -f -t package $(pds -vv -f $MINGW_PREFIX/lib/gdk-pixbuf-2.0/2.10.0/loaders/*) + find package -iname "*.dll" -or -iname "*.exe" -type f -exec mingw-strip {} + + + cd package/share + wget2 https://github.com/qarmin/czkawka/files/10832192/gtk4_theme.zip + unzip gtk4_theme.zip + rm gtk4_theme.zip + cd ../.. + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: czkawka-windows-${{ github.sha }}-${{ matrix.use_heif }}-4.6 path: | ./package if-no-files-found: error # Provide option to log things to windows CLI - container_console_window: + container_4_6_console_window: runs-on: ubuntu-22.04 container: image: ghcr.io/piegamesde/gtk4-cross:gtk-4.6 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies(mostly sd) run: | dnf install wget -y @@ -219,9 +284,9 @@ jobs: rm gtk4_theme.zip cd ../.. - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: czkawka-windows-${{ github.sha }}-console + name: czkawka-windows-${{ github.sha }}-console-4.6 path: | ./package if-no-files-found: error diff --git a/.gitignore b/.gitignore index 384ef7e..257863a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,7 @@ ci_tester/Cargo.lock krokiet/Cargo.lock krokiet/target *.json -*.mm_profdata \ No newline at end of file +*.mm_profdata +perf.data +perf.data.old +krokiet/ui/test.slint diff --git a/Cargo.lock b/Cargo.lock index 02f2f9c..c120dac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,9 +32,9 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -43,9 +43,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if", "getrandom", @@ -71,12 +71,12 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "android-activity" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b801912a977c3fd52d80511fe1c0c8480c6f957f21ae2ce1b92ffe970cf4b9" +checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" dependencies = [ "android-properties", - "bitflags 2.4.1", + "bitflags 2.4.2", "cc", "cesu8", "jni", @@ -113,9 +113,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.7" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -127,9 +127,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -204,7 +204,7 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac22eda5891cc086690cb6fa10121c0390de0e3b04eb269f2d766b00d3f2d81" dependencies = [ - "async-fs 2.1.0", + "async-fs 2.1.1", "async-net", "enumflags2", "futures-channel", @@ -229,13 +229,13 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener 4.0.3", - "event-listener-strategy", + "event-listener 5.0.0", + "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", ] @@ -268,9 +268,9 @@ dependencies = [ [[package]] name = "async-fs" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1f344136bad34df1f83a47f3fd7f2ab85d75cb8a940af4ccf6d482a84ea01b" +checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" dependencies = [ "async-lock 3.3.0", "blocking", @@ -299,9 +299,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" +checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" dependencies = [ "async-lock 3.3.0", "cfg-if", @@ -309,8 +309,8 @@ dependencies = [ "futures-io", "futures-lite 2.2.0", "parking", - "polling 3.3.1", - "rustix 0.38.30", + "polling 3.4.0", + "rustix 0.38.31", "slab", "tracing", "windows-sys 0.52.0", @@ -332,7 +332,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ "event-listener 4.0.3", - "event-listener-strategy", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -342,7 +342,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ - "async-io 2.2.2", + "async-io 2.3.1", "blocking", "futures-lite 2.2.0", ] @@ -360,7 +360,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.30", + "rustix 0.38.31", "windows-sys 0.48.0", ] @@ -381,13 +381,13 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" dependencies = [ - "async-io 2.2.2", + "async-io 2.3.1", "async-lock 2.8.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.30", + "rustix 0.38.31", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -427,9 +427,9 @@ dependencies = [ [[package]] name = "auto_enums" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a764179c02b324e33cf71b4180e7dd13572400ff7e5c866da813f6c84e0e4cd3" +checksum = "1899bfcfd9340ceea3533ea157360ba8fa864354eccbceab58e1006ecab35393" dependencies = [ "derive_utils", "proc-macro2", @@ -466,17 +466,17 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.2" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c69fae65a523209d34240b60abe0c42d33d1045d445c0839d8a4894a736e2d" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cexpr", "clang-sys", + "itertools 0.12.1", "lazy_static", "lazycell", "log", - "peeking_take_while", "prettyplease", "proc-macro2", "quote", @@ -501,9 +501,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bk-tree" @@ -601,9 +601,9 @@ checksum = "bf8dba2868114ed769a1f2590fc9ae5eb331175b44313b6c9b922f8f7ca813d0" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" dependencies = [ "bytemuck_derive", ] @@ -654,23 +654,22 @@ dependencies = [ [[package]] name = "cairo-rs" -version = "0.18.5" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +checksum = "2650f66005301bd33cc486dec076e1293c4cecf768bc7ba9bf5d2b1be339b99c" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cairo-sys-rs", "glib", "libc", - "once_cell", "thiserror", ] [[package]] name = "cairo-sys-rs" -version = "0.18.2" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +checksum = "fd3bb3119664efbd78b5e6c93957447944f16bdbced84c17a9f41c7829b81e64" dependencies = [ "glib-sys", "libc", @@ -679,14 +678,14 @@ dependencies = [ [[package]] name = "calloop" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf" +checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "log", - "polling 3.3.1", - "rustix 0.38.30", + "polling 3.4.0", + "rustix 0.38.31", "slab", "thiserror", ] @@ -698,7 +697,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" dependencies = [ "calloop", - "rustix 0.38.30", + "rustix 0.38.31", "wayland-backend", "wayland-client", ] @@ -750,9 +749,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.6" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a" +checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" dependencies = [ "smallvec", "target-lexicon", @@ -787,16 +786,16 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] @@ -822,9 +821,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.16" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" dependencies = [ "clap_builder", "clap_derive", @@ -832,21 +831,21 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.16" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.0", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", @@ -856,9 +855,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clipboard-win" @@ -969,7 +968,8 @@ dependencies = [ [[package]] name = "const-field-offset" version = "0.1.3" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6304465f16f463cddc572b737c3df93576edd3a6b53f057bd8beeb29f4ef8dfd" dependencies = [ "const-field-offset-macro", "field-offset", @@ -978,7 +978,8 @@ dependencies = [ [[package]] name = "const-field-offset-macro" version = "0.1.3" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57aaaad9185d3bcb3afe63549d8ba60b2fb0ea8dc2da83f62dd56805edf56fd1" dependencies = [ "proc-macro2", "quote", @@ -1011,9 +1012,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "copypasta" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d35364349bf9e9e1c3a035ddcb00d188d23a3c40c50244c03c27a99fc6a65ae" +checksum = "deb85422867ca93da58b7f95fb5c0c10f6183ed6e1ef8841568968a896d3a858" dependencies = [ "clipboard-win", "objc", @@ -1101,9 +1102,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -1230,7 +1231,7 @@ dependencies = [ "anyhow", "audio_checker", "bincode", - "bitflags 2.4.1", + "bitflags 2.4.2", "bk-tree", "blake3", "crc32fast", @@ -1266,6 +1267,7 @@ dependencies = [ "state", "symphonia", "tempfile", + "trash", "vid_dup_finder_lib", "xxhash-rust", "zip", @@ -1320,7 +1322,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 1.0.109", ] @@ -1430,9 +1432,9 @@ dependencies = [ [[package]] name = "derive_utils" -version = "0.13.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9abcad25e9720609ccb3dcdb795d845e37d8ce34183330a9f48b03a1a71c8e21" +checksum = "61bb5a1014ce6dfc2a378578509abe775a5aa06bff584a547555d9efdb81b926" dependencies = [ "proc-macro2", "quote", @@ -1528,11 +1530,11 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "bytemuck", "drm-ffi 0.7.1", "drm-fourcc", - "rustix 0.38.30", + "rustix 0.38.31", ] [[package]] @@ -1552,7 +1554,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6" dependencies = [ "drm-sys 0.6.1", - "rustix 0.38.30", + "rustix 0.38.31", ] [[package]] @@ -1577,7 +1579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176" dependencies = [ "libc", - "linux-raw-sys 0.6.3", + "linux-raw-sys 0.6.4", ] [[package]] @@ -1596,9 +1598,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encode_unicode" @@ -1617,9 +1619,9 @@ dependencies = [ [[package]] name = "enumflags2" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" dependencies = [ "enumflags2_derive", "serde", @@ -1627,9 +1629,9 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ "proc-macro2", "quote", @@ -1700,6 +1702,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72557800024fabbaa2449dd4bf24e37b93702d457a4d4f2b0dd1f0f039f20c1" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "event-listener-strategy" version = "0.4.0" @@ -1711,10 +1724,20 @@ dependencies = [ ] [[package]] -name = "exr" -version = "1.6.4" +name = "event-listener-strategy" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279d3efcc55e19917fff7ab3ddd6c14afb6a90881a0078465196fe2f99d08c56" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +dependencies = [ + "event-listener 5.0.0", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" dependencies = [ "bit_field", "flume", @@ -1763,25 +1786,26 @@ dependencies = [ [[package]] name = "fdeflate" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] [[package]] name = "femtovg" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19df4b4c86231086212f22513ccfdbce94a1e1270d1cb09c030bd39fd73f3ee4" +checksum = "18ab822e58e8bc2b89840dc5dde49afe39302e129c60d39c8520200c085404a7" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "fnv", "generational-arena", "glow", "image", "imgref", + "log", "lru", "rgb", "rustybuzz 0.11.0", @@ -1904,14 +1928,10 @@ dependencies = [ [[package]] name = "flume" -version = "0.10.14" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ - "futures-core", - "futures-sink", - "nanorand", - "pin-project", "spin", ] @@ -1923,25 +1943,25 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fontconfig-parser" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "674e258f4b5d2dcd63888c01c68413c51f565e8af99d2f7701c7b81d79ef41c4" +checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" dependencies = [ "roxmltree", ] [[package]] name = "fontdb" -version = "0.15.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020e203f177c0fb250fb19455a252e838d2bbbce1f80f25ecc42402aafa8cd38" +checksum = "3890d0893c8253d3eb98337af18b3e1a10a9b2958f2a164b53a93fb3a3049e72" dependencies = [ "fontconfig-parser", "log", - "memmap2 0.8.0", + "memmap2 0.9.4", "slotmap", "tinyvec", - "ttf-parser 0.19.2", + "ttf-parser", ] [[package]] @@ -1951,7 +1971,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9099a2f86b8e674b75d03ff154b3fe4c5208ed249ced8d69cc313a9fa40bb488" dependencies = [ "hashbrown 0.14.3", - "ttf-parser 0.20.0", + "ttf-parser", ] [[package]] @@ -2004,9 +2024,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "fun_time" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fdd8402ab5955698d66b0ba307bb2d3b3bfb911a4dfe7bd6bbc7f140be6a5" +checksum = "bee194d43605ea83cca7af42af5f9001fab1a8e2220cb8a012e21dda6167fdb0" dependencies = [ "fun_time_derive", "log", @@ -2014,9 +2034,9 @@ dependencies = [ [[package]] name = "fun_time_derive" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d89b974360ce9cd00ef9dd26353bc6f1ec9ff12d95e741ad83b36385f3a736" +checksum = "71555fd2db00938d82d29d8fa62a2ae80aed2c162c328d775f79e98d9212f013" dependencies = [ "darling", "log", @@ -2149,22 +2169,21 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.18.5" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +checksum = "f6a23f8a0b5090494fd04924662d463f8386cc678dd3915015a838c1a3679b92" dependencies = [ "gdk-pixbuf-sys", "gio", "glib", "libc", - "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +checksum = "3dcbd04c1b2c4834cc008b4828bc917d062483b88d26effde6342e5622028f96" dependencies = [ "gio-sys", "glib-sys", @@ -2175,9 +2194,9 @@ dependencies = [ [[package]] name = "gdk4" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edb019ad581f8ecf8ea8e4baa6df7c483a95b5a59be3140be6a9c3b0c632af6" +checksum = "6771942f85a2beaa220c64739395e4401b9fab4a52aba9b503fa1e6ed4d4d806" dependencies = [ "cairo-rs", "gdk-pixbuf", @@ -2190,9 +2209,9 @@ dependencies = [ [[package]] name = "gdk4-sys" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbab43f332a3cf1df9974da690b5bb0e26720ed09a228178ce52175372dcfef0" +checksum = "1eb95854fab65072023a7814434f003db571d6e45c287c0b0c540c1c78bdf6ae" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -2264,10 +2283,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -2282,9 +2299,9 @@ dependencies = [ [[package]] name = "gio" -version = "0.18.4" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +checksum = "2eae10b27b6dd27e22ed0d812c6387deba295e6fc004a8b379e459b663b05a02" dependencies = [ "futures-channel", "futures-core", @@ -2293,7 +2310,6 @@ dependencies = [ "gio-sys", "glib", "libc", - "once_cell", "pin-project-lite", "smallvec", "thiserror", @@ -2301,15 +2317,15 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +checksum = "bcf8e1d9219bb294636753d307b030c1e8a032062cba74f493c431a5c8b81ce4" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2325,11 +2341,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.18.5" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +checksum = "ab9e86540b5d8402e905ad4ce7d6aa544092131ab564f3102175af176b90a053" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "futures-channel", "futures-core", "futures-executor", @@ -2341,20 +2357,18 @@ dependencies = [ "gobject-sys", "libc", "memchr", - "once_cell", "smallvec", "thiserror", ] [[package]] name = "glib-macros" -version = "0.18.5" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +checksum = "0f5897ca27a83e4cdc7b4666850bade0a2e73e17689aabafcc9acddad9d823b8" dependencies = [ "heck", - "proc-macro-crate 2.0.0", - "proc-macro-error", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 2.0.48", @@ -2362,9 +2376,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +checksum = "630f097773d7c7a0bb3258df4e8157b47dc98bbfa0e60ad9ab56174813feced4" dependencies = [ "libc", "system-deps", @@ -2388,9 +2402,9 @@ dependencies = [ [[package]] name = "glow" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "886c2a30b160c4c6fec8f987430c26b526b7988ca71f664e6a699ddf6f9601e4" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" dependencies = [ "js-sys", "slotmap", @@ -2400,11 +2414,11 @@ dependencies = [ [[package]] name = "glutin" -version = "0.31.2" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005459a22af86adc706522d78d360101118e2638ec21df3852fcc626e0dbb212" +checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cfg_aliases 0.1.1", "cgl", "core-foundation", @@ -2465,9 +2479,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +checksum = "c85e2b1080b9418dd0c58b498da3a5c826030343e0ef07bde6a955d28de54979" dependencies = [ "glib-sys", "libc", @@ -2476,9 +2490,9 @@ dependencies = [ [[package]] name = "graphene-rs" -version = "0.18.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2228cda1505613a7a956cca69076892cfbda84fc2b7a62b94a41a272c0c401" +checksum = "99e4d388e96c5f29e2b2f67045d229ddf826d0a8d6d282f94ed3b34452222c91" dependencies = [ "glib", "graphene-sys", @@ -2487,9 +2501,9 @@ dependencies = [ [[package]] name = "graphene-sys" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4144cee8fc8788f2a9b73dc5f1d4e1189d1f95305c4cb7bd9c1af1cfa31f59" +checksum = "236ed66cc9b18d8adf233716f75de803d0bf6fc806f60d14d948974a12e240d0" dependencies = [ "glib-sys", "libc", @@ -2499,9 +2513,9 @@ dependencies = [ [[package]] name = "gsk4" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d958e351d2f210309b32d081c832d7de0aca0b077aa10d88336c6379bd01f7e" +checksum = "0e8ce8dee0fd87a11002214b1204ff18c9272fbd530408f0884a0f9b25dc31de" dependencies = [ "cairo-rs", "gdk4", @@ -2514,9 +2528,9 @@ dependencies = [ [[package]] name = "gsk4-sys" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bd9e3effea989f020e8f1ff3fa3b8c63ba93d43b899c11a118868853a56d55" +checksum = "2660a652da5b662d43924df19ba40d73f015ed427329ef51d2b1360a4e0dc0e4" dependencies = [ "cairo-sys-rs", "gdk4-sys", @@ -2530,9 +2544,9 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb51aa3e9728575a053e1f43543cd9992ac2477e1b186ad824fd4adfb70842" +checksum = "7d26ffa3ec6316ccaa1df62d3e7f5bae1637c0acbb43f250fabef38319f73c64" dependencies = [ "cairo-rs", "field-offset", @@ -2551,12 +2565,12 @@ dependencies = [ [[package]] name = "gtk4-macros" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d57ec49cf9b657f69a05bca8027cff0a8dfd0c49e812be026fc7311f2163832f" +checksum = "c8b86439e9896f6f3f47c3d8077c5c8205174078760afdabd9098a8e9e937d97" dependencies = [ "anyhow", - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.1.0", "proc-macro-error", "proc-macro2", "quote", @@ -2565,9 +2579,9 @@ dependencies = [ [[package]] name = "gtk4-sys" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54d8c4aa23638ce9faa2caf7e2a27d4a1295af2155c8e8d28c4d4eeca7a65eb8" +checksum = "2abc0a6d356d59a3806021829ce6ed3e70bba3509b41a535fedcb09fae13fbc0" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -2644,9 +2658,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" [[package]] name = "hex" @@ -2683,8 +2697,9 @@ dependencies = [ [[package]] name = "i-slint-backend-linuxkms" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fff24de728432445a94f890093bd852a7c3fd01ae282e40ae0eab6ea629a66d" dependencies = [ "calloop", "drm 0.9.0", @@ -2703,8 +2718,9 @@ dependencies = [ [[package]] name = "i-slint-backend-selector" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1953f792a394a7ad26932b4989646719fb126910029006f7cef40ffd189633d0" dependencies = [ "cfg-if", "i-slint-backend-linuxkms", @@ -2716,8 +2732,9 @@ dependencies = [ [[package]] name = "i-slint-backend-winit" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138c36c5c9e12f22fe07f280200cedb5d31e07641e40e3b45376fe2112ead303" dependencies = [ "bytemuck", "cfg-if", @@ -2741,7 +2758,6 @@ dependencies = [ "rgb", "scoped-tls-hkt", "scopeguard", - "send_wrapper", "softbuffer", "vtable", "wasm-bindgen", @@ -2751,8 +2767,9 @@ dependencies = [ [[package]] name = "i-slint-common" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "550e9282fda488b76ce057a728168d1aef947c758f3221b92ae904b167ab008c" dependencies = [ "cfg-if", "derive_more", @@ -2762,8 +2779,9 @@ dependencies = [ [[package]] name = "i-slint-compiler" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa2c3525ea761b3e9251f7869e64dce5f483f7ea1a5e04fe7b9a3fb5780dd6" dependencies = [ "by_address", "codemap", @@ -2773,7 +2791,7 @@ dependencies = [ "fontdue", "i-slint-common", "image", - "itertools 0.12.0", + "itertools 0.12.1", "linked_hash_set", "lyon_extra", "lyon_path", @@ -2791,8 +2809,9 @@ dependencies = [ [[package]] name = "i-slint-core" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d40990f7ed42c76799edc0e8cae9a8ffcc77569506a94e86eef81626c7387a7" dependencies = [ "auto_enums", "bytemuck", @@ -2817,7 +2836,7 @@ dependencies = [ "portable-atomic", "resvg", "rgb", - "rustybuzz 0.11.0", + "rustybuzz 0.12.1", "scoped-tls-hkt", "scopeguard", "slab", @@ -2829,13 +2848,14 @@ dependencies = [ "vtable", "wasm-bindgen", "web-sys", - "web-time", + "web-time 1.0.0", ] [[package]] name = "i-slint-core-macros" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2a4e4c21ed660181abe101b6f141d920e838fec4bc1ff20f7440dc03fb5ad8" dependencies = [ "quote", "syn 2.0.48", @@ -2843,8 +2863,9 @@ dependencies = [ [[package]] name = "i-slint-renderer-femtovg" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "337e6ec928bde01baa27d3fdeddd5a33665952aead4d0d9be9818f573a41344c" dependencies = [ "cfg-if", "const-field-offset", @@ -2864,7 +2885,7 @@ dependencies = [ "raw-window-handle", "rgb", "scoped-tls-hkt", - "ttf-parser 0.20.0", + "ttf-parser", "unicode-script", "unicode-segmentation", "vtable", @@ -2875,8 +2896,9 @@ dependencies = [ [[package]] name = "i-slint-renderer-skia" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1189ac65d7e31129407e2cfef4009cdea500040a73d8f87505949f82af0939db" dependencies = [ "ash", "bytemuck", @@ -2905,8 +2927,7 @@ dependencies = [ "unicode-segmentation", "vtable", "vulkano", - "winapi", - "wio", + "windows 0.52.0", ] [[package]] @@ -2919,7 +2940,7 @@ dependencies = [ "serde", "serde_derive", "thiserror", - "toml 0.8.8", + "toml 0.8.10", "unic-langid", ] @@ -2947,9 +2968,9 @@ dependencies = [ [[package]] name = "i18n-embed-fl" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc1f8715195dffc4caddcf1cf3128da15fe5d8a137606ea8856c9300047d5a2" +checksum = "8241a781f49e923415e106fcd1f89c3fab92cc9f699a521c56e95dee273903d3" dependencies = [ "dashmap", "find-crate", @@ -2961,7 +2982,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 2.0.48", "unic-langid", ] @@ -2981,9 +3002,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -3031,9 +3052,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23" dependencies = [ "bytemuck", "byteorder", @@ -3041,7 +3062,6 @@ dependencies = [ "exr", "gif", "jpeg-decoder", - "num-rational", "num-traits", "png", "qoi", @@ -3089,9 +3109,9 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" [[package]] name = "imgref" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90d944e334f00f4449c9640b440a171f816be0152305c12ef90424fc35fd035c" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" [[package]] name = "indexmap" @@ -3105,9 +3125,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -3115,9 +3135,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", @@ -3161,12 +3181,9 @@ dependencies = [ [[package]] name = "input-sys" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f6c2a17e8aba7217660e32863af87b0febad811d4b8620ef76b386603fddc2" -dependencies = [ - "libc", -] +checksum = "bd4f5b4d1c00331c5245163aacfe5f20be75b564c7112d45893d4ae038119eb0" [[package]] name = "instant" @@ -3255,9 +3272,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -3292,27 +3309,27 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] [[package]] name = "jpeg-decoder" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" dependencies = [ "rayon", ] [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -3386,9 +3403,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libflate" @@ -3485,7 +3502,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "libc", "redox_syscall 0.4.1", ] @@ -3496,7 +3513,7 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "libc", "redox_syscall 0.4.1", ] @@ -3534,15 +3551,15 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "linux-raw-sys" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab96045f1fabcc9fe043d9cb6900c5e1cba5c13f6aaa3d2295b496661924464" +checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4" [[package]] name = "locale_config" @@ -3569,9 +3586,9 @@ dependencies = [ [[package]] name = "lofty" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7b592b0deed44e1633b140464af992a15c4045c83e28f2e558c3cc06f50b6" +checksum = "f75066eb1d25a7047fb2667edb410ae2592439ed81546f95c28b0a1c7d7d3818" dependencies = [ "byteorder", "data-encoding", @@ -3616,9 +3633,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" +checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22" [[package]] name = "lyon_algorithms" @@ -3701,9 +3718,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] @@ -3732,7 +3749,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "block", "core-graphics-types", "foreign-types", @@ -3765,9 +3782,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -3782,22 +3799,13 @@ dependencies = [ "linked-hash-map", ] -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" -dependencies = [ - "getrandom", -] - [[package]] name = "ndk" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "jni-sys", "log", "ndk-sys", @@ -3839,7 +3847,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cfg-if", "libc", ] @@ -3875,39 +3883,33 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -3928,7 +3930,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 3.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 2.0.48", @@ -4081,27 +4083,26 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" dependencies = [ - "ttf-parser 0.20.0", + "ttf-parser", ] [[package]] name = "pango" -version = "0.18.3" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +checksum = "7809e8af4df8d024a066106b72ca6bc7253a484ae3867041a96103ef8a13188d" dependencies = [ "gio", "glib", "libc", - "once_cell", "pango-sys", ] [[package]] name = "pango-sys" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +checksum = "f52ef6a881c19fbfe3b1484df5cad411acaaba29dbec843941c3110d19f340ea" dependencies = [ "glib-sys", "gobject-sys", @@ -4186,7 +4187,7 @@ dependencies = [ "deflate", "fax", "globalcache", - "indexmap 2.1.0", + "indexmap 2.2.3", "istring", "itertools 0.10.5", "jpeg-decoder", @@ -4212,12 +4213,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "percent-encoding" version = "2.3.1" @@ -4232,18 +4227,18 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", @@ -4281,15 +4276,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" [[package]] name = "png" -version = "0.17.10" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +checksum = "1f6c3c3e617595665b8ea2ff95a86066be38fb121ff920a9c0eb282abcd1da5a" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -4316,14 +4311,14 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +checksum = "30054e72317ab98eddd8561db0f6524df3367636884b7b21b703e4b280a84a14" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.30", + "rustix 0.38.31", "tracing", "windows-sys 0.52.0", ] @@ -4386,20 +4381,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit 0.20.7", -] - -[[package]] -name = "proc-macro-crate" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2685dd208a3771337d8d386a89840f0f43cd68be8dae90a5f8c2384effc9cd" -dependencies = [ - "toml_edit 0.21.0", + "toml_edit 0.21.1", ] [[package]] @@ -4428,9 +4414,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -4446,9 +4432,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" dependencies = [ "memchr", ] @@ -4515,9 +4501,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" dependencies = [ "either", "rayon-core", @@ -4525,27 +4511,21 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] -[[package]] -name = "rctree" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" - [[package]] name = "realfft" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953d9f7e5cdd80963547b456251296efc2626ed4e3cbf36c869d9564e0220571" dependencies = [ - "rustfft 6.1.0", + "rustfft 6.2.0", ] [[package]] @@ -4579,13 +4559,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.5", "regex-syntax 0.8.2", ] @@ -4600,9 +4580,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -4623,9 +4603,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "resvg" -version = "0.36.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7980f653f9a7db31acff916a262c3b78c562919263edea29bf41a056e20497" +checksum = "5c34501046959e06470ba62a2dc7f31c15f94ac250d842a45f9e012f4ee40c1e" dependencies = [ "log", "pico-args", @@ -4637,15 +4617,13 @@ dependencies = [ [[package]] name = "rfd" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" +checksum = "c0d8ab342bcc5436e04d3a4c1e09e17d74958bfaddf8d5fad6f85607df0f994f" dependencies = [ "ashpd", - "async-io 1.13.0", "block", "dispatch", - "futures-util", "js-sys", "log", "objc", @@ -4669,20 +4647,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "ring" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" -dependencies = [ - "cc", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.48.0", -] - [[package]] name = "rle-decode-fast" version = "1.0.3" @@ -4704,20 +4668,17 @@ dependencies = [ [[package]] name = "roxmltree" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" -dependencies = [ - "xmlparser", -] +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" [[package]] name = "rubato" -version = "0.12.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd70209c27d5b08f5528bdc779ea3ffb418954e28987f9f9775c6eac41003f9c" +checksum = "e6dd52e80cfc21894deadf554a5673002938ae4625f7a283e536f9cf7c17b0d5" dependencies = [ - "num-complex 0.4.4", + "num-complex 0.4.5", "num-integer", "num-traits", "realfft", @@ -4787,7 +4748,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b61555105d6a9bf98797c063c362a1d24ed8ab0431655e38f1cf51e52089551" dependencies = [ - "rustfft 6.1.0", + "rustfft 6.2.0", ] [[package]] @@ -4806,11 +4767,11 @@ dependencies = [ [[package]] name = "rustfft" -version = "6.1.0" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d4f6cbdb180c9f4b2a26bbf01c4e647f1e1dea22fe8eb9db54198b32f9434" +checksum = "43806561bc506d0c5d160643ad742e3161049ac01027b5e6d7524091fd401d86" dependencies = [ - "num-complex 0.4.4", + "num-complex 0.4.5", "num-integer", "num-traits", "primal-check", @@ -4835,39 +4796,17 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", - "linux-raw-sys 0.4.12", + "linux-raw-sys 0.4.13", "windows-sys 0.52.0", ] -[[package]] -name = "rustls" -version = "0.21.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" -dependencies = [ - "log", - "ring", - "rustls-webpki", - "sct", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "rustversion" version = "1.0.14" @@ -4876,28 +4815,12 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "rusty-chromaprint" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023a224821c3208db13134f398c2d92ed81267ef4f65ac8dff670c00b829faac" +checksum = "1755646867c36ecb391776deaa0b557a76d3badf20c142de7282630c34b20440" dependencies = [ "rubato", - "rustfft 6.1.0", -] - -[[package]] -name = "rustybuzz" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71cd15fef9112a1f94ac64b58d1e4628192631ad6af4dc69997f995459c874e7" -dependencies = [ - "bitflags 1.3.2", - "bytemuck", - "smallvec", - "ttf-parser 0.19.2", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", + "rustfft 6.2.0", ] [[package]] @@ -4909,7 +4832,23 @@ dependencies = [ "bitflags 1.3.2", "bytemuck", "smallvec", - "ttf-parser 0.20.0", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "rustybuzz" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ae5692c5beaad6a9e22830deeed7874eae8a4e3ba4076fb48e12c56856222c" +dependencies = [ + "bitflags 2.4.2", + "bytemuck", + "smallvec", + "ttf-parser", "unicode-bidi-mirroring", "unicode-ccc", "unicode-properties", @@ -4949,16 +4888,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "sctk-adwaita" version = "0.8.1" @@ -4967,7 +4896,7 @@ checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550" dependencies = [ "ab_glyph", "log", - "memmap2 0.9.3", + "memmap2 0.9.4", "smithay-client-toolkit", "tiny-skia", ] @@ -4993,26 +4922,20 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" - [[package]] name = "serde" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", @@ -5021,9 +4944,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ "itoa", "ryu", @@ -5095,9 +5018,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" @@ -5131,9 +5054,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "skia-bindings" -version = "0.69.0" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b4b5af96ee7d895763fa606f4531fddfb11de034217edd0c7beb9ea181efe5b" +checksum = "4bc61a106126a429bb4775ce5fbe23b2bcaa74d1a9c484997f4700de31480b44" dependencies = [ "bindgen", "cc", @@ -5143,21 +5066,19 @@ dependencies = [ "regex", "serde_json", "tar", - "toml 0.8.8", - "ureq", + "toml 0.8.10", ] [[package]] name = "skia-safe" -version = "0.69.0" +version = "0.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d25acaedea0a8ed1dac52f383fc90276f5679a68e3f84c5fb7f7bde8934ff" +checksum = "3201eba92bca1f83864f5c3a48309bcfee7e0590bebd7826e7ab0a49aa24a750" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "lazy_static", "skia-bindings", - "winapi", - "wio", + "windows 0.52.0", ] [[package]] @@ -5171,8 +5092,9 @@ dependencies = [ [[package]] name = "slint" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d51a972e5c6f22793f9b334eeb537121337fbf66ed73e978475fca031fd367a" dependencies = [ "const-field-offset", "i-slint-backend-selector", @@ -5187,19 +5109,21 @@ dependencies = [ [[package]] name = "slint-build" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf98969a520e19ec675435b8c21b883d4a06489947e137bbd30859b50ad0c36" dependencies = [ "i-slint-compiler", "spin_on", "thiserror", - "toml_edit 0.21.0", + "toml_edit 0.21.1", ] [[package]] name = "slint-macros" -version = "1.4.0" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "564b2a25cc34007aa9cd73630394efa972b956fee86538caa5d09808b3ba0c05" dependencies = [ "i-slint-compiler", "proc-macro2", @@ -5218,24 +5142,24 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "smithay-client-toolkit" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60e3d9941fa3bacf7c2bf4b065304faa14164151254cd16ce1b1bc8fc381600f" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "calloop", "calloop-wayland-source", "cursor-icon", "libc", "log", - "memmap2 0.9.3", - "rustix 0.38.30", + "memmap2 0.9.4", + "rustix 0.38.31", "thiserror", "wayland-backend", "wayland-client", @@ -5260,9 +5184,9 @@ dependencies = [ [[package]] name = "smol_str" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49" dependencies = [ "serde", ] @@ -5315,11 +5239,11 @@ dependencies = [ "foreign-types", "js-sys", "log", - "memmap2 0.9.3", + "memmap2 0.9.4", "objc", "raw-window-handle", "redox_syscall 0.4.1", - "rustix 0.38.30", + "rustix 0.38.31", "tiny-xlib", "wasm-bindgen", "wayland-backend", @@ -5402,19 +5326,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "strum" -version = "0.25.0" +name = "strsim" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "strum" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" dependencies = [ "heck", "proc-macro2", @@ -5431,9 +5361,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "svgtypes" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71499ff2d42f59d26edb21369a308ede691421f79ebc0f001e2b1fd3a7c9e52" +checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70" dependencies = [ "kurbo", "siphasher", @@ -5653,7 +5583,7 @@ dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml 0.8.8", + "toml 0.8.10", "version-compare", ] @@ -5676,14 +5606,13 @@ checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand 2.0.1", - "redox_syscall 0.4.1", - "rustix 0.38.30", + "rustix 0.38.31", "windows-sys 0.52.0", ] @@ -5704,18 +5633,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -5734,9 +5663,9 @@ dependencies = [ [[package]] name = "tiff" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" dependencies = [ "flate2", "jpeg-decoder", @@ -5745,13 +5674,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -5767,18 +5697,19 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] [[package]] name = "tiny-skia" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a067b809476893fce6a254cf285850ff69c847e6cfbade6a20b655b6c7e80d" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" dependencies = [ "arrayref", "arrayvec", @@ -5791,9 +5722,9 @@ dependencies = [ [[package]] name = "tiny-skia-path" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de35e8a90052baaaf61f171680ac2f8e925a1e43ea9d2e3a00514772250e541" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" dependencies = [ "arrayref", "bytemuck", @@ -5847,14 +5778,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.22.5", ] [[package]] @@ -5872,33 +5803,33 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "99e68c159e8f5ba8a28c4eb7b0c0c190d77bb479047ca713270048145a9ad28a" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.0", ] [[package]] @@ -5974,9 +5905,9 @@ dependencies = [ [[package]] name = "trash" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e7b1a28f9550f43ac27987f2144d7798520c6dee6a7eb1dedfe3131e3c257e3" +checksum = "c658458d46d9d5a153a3b5cdd88d8579ad50d4fb85d53961e4526c8fc7c55a57" dependencies = [ "chrono", "libc", @@ -5994,12 +5925,6 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622b09ce2fe2df4618636fb92176d205662f59803f39e70d1c333393082de96c" -[[package]] -name = "ttf-parser" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1" - [[package]] name = "ttf-parser" version = "0.20.0" @@ -6092,9 +6017,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-bidi-mirroring" @@ -6131,9 +6056,9 @@ dependencies = [ [[package]] name = "unicode-properties" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" [[package]] name = "unicode-script" @@ -6143,9 +6068,9 @@ checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-vo" @@ -6159,28 +6084,6 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "ureq" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" -dependencies = [ - "base64", - "flate2", - "log", - "once_cell", - "rustls", - "rustls-webpki", - "url", - "webpki-roots", -] - [[package]] name = "url" version = "2.5.0" @@ -6201,9 +6104,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "usvg" -version = "0.36.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51daa774fe9ee5efcf7b4fec13019b8119cda764d9a8b5b06df02bb1445c656" +checksum = "377f62b4a3c173de8654c1aa80ab1dac1154e6f13a779a9943e53780120d1625" dependencies = [ "base64", "log", @@ -6216,9 +6119,9 @@ dependencies = [ [[package]] name = "usvg-parser" -version = "0.36.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c88a5ffaa338f0e978ecf3d4e00d8f9f493e29bed0752e1a808a1db16afc40" +checksum = "351a05e6f2023d6b4e946f734240a3927aefdcf930d7d42587a2c8a8869814b0" dependencies = [ "data-url", "flate2", @@ -6234,14 +6137,14 @@ dependencies = [ [[package]] name = "usvg-text-layout" -version = "0.36.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2374378cb7a3fb8f33894e0fdb8625e1bbc4f25312db8d91f862130b541593" +checksum = "8c41888b9d5cf431fe852eaf9d047bbde83251b98f1749c2f08b1071e6db46e2" dependencies = [ "fontdb", "kurbo", "log", - "rustybuzz 0.10.0", + "rustybuzz 0.12.1", "unicode-bidi", "unicode-script", "unicode-vo", @@ -6250,11 +6153,10 @@ dependencies = [ [[package]] name = "usvg-tree" -version = "0.36.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cacb0c5edeaf3e80e5afcf5b0d4004cc1d36318befc9a7c6606507e5d0f4062" +checksum = "18863e0404ed153d6e56362c5b1146db9f4f262a3244e3cf2dbe7d8a85909f05" dependencies = [ - "rctree", "strict-num", "svgtypes", "tiny-skia-path", @@ -6268,9 +6170,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" [[package]] name = "valuable" @@ -6319,7 +6221,8 @@ dependencies = [ [[package]] name = "vtable" version = "0.1.11" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f4c7506238561777a1861d3dc3c0001877c475187e7bc4392ea87ebf631fd9c" dependencies = [ "const-field-offset", "portable-atomic", @@ -6330,7 +6233,8 @@ dependencies = [ [[package]] name = "vtable-macro" version = "0.1.10" -source = "git+https://github.com/slint-ui/slint.git#ff2bf6849e2d6325db1650ebe3dc96137573d272" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b2b8eecdb8e4284adf5546fc518f048f6dc33e7203dbe36fa93a4add39b31f6" dependencies = [ "proc-macro2", "quote", @@ -6350,7 +6254,7 @@ dependencies = [ "crossbeam-queue", "half", "heck", - "indexmap 2.1.0", + "indexmap 2.2.3", "libloading 0.8.1", "objc", "once_cell", @@ -6390,9 +6294,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -6400,9 +6304,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", @@ -6415,9 +6319,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" dependencies = [ "cfg-if", "js-sys", @@ -6427,9 +6331,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6437,9 +6341,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", @@ -6450,19 +6354,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "wayland-backend" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4" +checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" dependencies = [ "cc", "downcast-rs", - "nix 0.26.4", + "rustix 0.38.31", "scoped-tls", "smallvec", "wayland-sys", @@ -6470,12 +6374,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" +checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" dependencies = [ - "bitflags 2.4.1", - "nix 0.26.4", + "bitflags 2.4.2", + "rustix 0.38.31", "wayland-backend", "wayland-scanner", ] @@ -6486,29 +6390,29 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44aa20ae986659d6c77d64d808a046996a932aa763913864dc40c359ef7ad5b" +checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" dependencies = [ - "nix 0.26.4", + "rustix 0.38.31", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.31.0" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6520,7 +6424,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6533,7 +6437,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6542,9 +6446,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c" +checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" dependencies = [ "proc-macro2", "quick-xml", @@ -6565,9 +6469,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", @@ -6584,16 +6488,20 @@ dependencies = [ ] [[package]] -name = "webpki-roots" -version = "0.25.3" +name = "web-time" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "2ee269d72cc29bf77a2c4bc689cc750fb39f5cbd493d2205bbb3f5c7779cf7b0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "which" @@ -6604,7 +6512,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.30", + "rustix 0.38.31", ] [[package]] @@ -6665,6 +6573,16 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.0", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -6874,14 +6792,14 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winit" -version = "0.29.9" +version = "0.29.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2376dab13e09c01ad8b679f0dbc7038af4ec43d9a91344338e37bd686481550" +checksum = "4c824f11941eeae66ec71111cc2674373c772f482b58939bb4066b642aa2ffcf" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.4.1", + "bitflags 2.4.2", "bytemuck", "calloop", "cfg_aliases 0.1.1", @@ -6892,7 +6810,7 @@ dependencies = [ "js-sys", "libc", "log", - "memmap2 0.9.3", + "memmap2 0.9.4", "ndk", "ndk-sys", "objc2", @@ -6901,7 +6819,7 @@ dependencies = [ "percent-encoding", "raw-window-handle", "redox_syscall 0.3.5", - "rustix 0.38.30", + "rustix 0.38.31", "sctk-adwaita", "smithay-client-toolkit", "smol_str", @@ -6913,7 +6831,7 @@ dependencies = [ "wayland-protocols", "wayland-protocols-plasma", "web-sys", - "web-time", + "web-time 0.2.4", "windows-sys 0.48.0", "x11-dl", "x11rb 0.13.0", @@ -6922,9 +6840,18 @@ dependencies = [ [[package]] name = "winnow" -version = "0.5.34" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1dbce9e90e5404c5a52ed82b1d13fc8cfbdad85033b6f57546ffd1265f8451" dependencies = [ "memchr", ] @@ -6940,11 +6867,12 @@ dependencies = [ [[package]] name = "x11-clipboard" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41aca1115b1f195f21c541c5efb423470848d48143127d0f07f8b90c27440df" +checksum = "613c2be3e772af2bbb57c5a94413675f5ec668bac00a71ada2ced28c420ef087" dependencies = [ - "x11rb 0.12.0", + "libc", + "x11rb 0.13.0", ] [[package]] @@ -6986,7 +6914,7 @@ dependencies = [ "libc", "libloading 0.8.1", "once_cell", - "rustix 0.38.30", + "rustix 0.38.31", "x11rb-protocol 0.13.0", ] @@ -7007,13 +6935,13 @@ checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" [[package]] name = "xattr" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", - "linux-raw-sys 0.4.12", - "rustix 0.38.30", + "linux-raw-sys 0.4.13", + "rustix 0.38.31", ] [[package]] @@ -7024,11 +6952,11 @@ checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" [[package]] name = "xdg-home" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" dependencies = [ - "nix 0.26.4", + "libc", "winapi", ] @@ -7045,11 +6973,11 @@ dependencies = [ [[package]] name = "xkbcommon-dl" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6924668544c48c0133152e7eec86d644a056ca3d09275eb8d5cdb9855f9d8699" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "dlib", "log", "once_cell", @@ -7068,12 +6996,6 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" -[[package]] -name = "xmlparser" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" - [[package]] name = "xmlwriter" version = "0.1.0" @@ -7097,9 +7019,9 @@ dependencies = [ [[package]] name = "zbus" -version = "3.14.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" +checksum = "c45d06ae3b0f9ba1fb2671268b975557d8f5a84bb5ec6e43964f87e763d8bca8" dependencies = [ "async-broadcast", "async-executor", @@ -7138,9 +7060,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.14.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +checksum = "b4a1ba45ed0ad344b85a2bb5a1fe9830aed23d67812ea39a586e7d0136439c7d" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index c8806cb..d72e6f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ resolver = "2" panic = "unwind" # LTO setting is disabled by default, because release mode is usually needed to develop app and compilation with LTO would take a lot of time +# But it is used to optimize release builds(and probably also in CI, where time is not so important as in local development) #lto = "fat" # Optimize all dependencies except application/workspaces, even in debug builds diff --git a/Changelog.md b/Changelog.md index 21ee0b7..e5a50f4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,9 +10,9 @@ ### CLI - Providing full static rust binary with [Eyra](https://github.com/sunfishcode/eyra) - [#1102](https://github.com/qarmin/czkawka/pull/1102) - Fixed duplicated `-c` argument, now saving as compact json is handled via `-C` - [#1153](https://github.com/qarmin/czkawka/pull/1153) -- Added progress bar - [#TODO]() -- Clean and safe cancelling of scan - [#TODO]() -- Unification of CLI arguments - [#TODO]() +- Added scan progress bar - [#1183](https://github.com/qarmin/czkawka/pull/1183) +- Clean and safe cancelling of scan - [#1183](https://github.com/qarmin/czkawka/pull/1183) +- Unification of CLI arguments - [#1183](https://github.com/qarmin/czkawka/pull/1183) ### Krokiet GUI - Initial release of new gui - [#1102](https://github.com/qarmin/czkawka/pull/1102) @@ -31,6 +31,9 @@ - Unifying code for collecting files to scan - [#1159](https://github.com/qarmin/czkawka/pull/1159) - Decrease memory usage when collecting files by removing unused fields in custom file entries structs - [#1159](https://github.com/qarmin/czkawka/pull/1159) - Decrease a little size of cache by few percents and improve loading/saving speed - [#1159](https://github.com/qarmin/czkawka/pull/1159) +- Added ability to remove from scan files with excluded extensions - [#1184](https://github.com/qarmin/czkawka/pull/1102) +- Fixed not showing in similar images results, files with same hashes when using reference folders - [#1184](https://github.com/qarmin/czkawka/pull/1102) +- Optimize release binaries with LTO(~25% smaller, ~5/10% faster) - [#1184](https://github.com/qarmin/czkawka/pull/1102) ## Version 6.1.0 - 15.10.2023r - BREAKING CHANGE - Changed cache saving method, deduplicated, optimized and simplified procedure(all files needs to be hashed again) - [#1072](https://github.com/qarmin/czkawka/pull/1072), [#1086](https://github.com/qarmin/czkawka/pull/1086) diff --git a/ci_tester/src/main.rs b/ci_tester/src/main.rs index a41a410..fd55c17 100644 --- a/ci_tester/src/main.rs +++ b/ci_tester/src/main.rs @@ -1,9 +1,10 @@ -use log::info; use std::collections::BTreeSet; use std::fs; use std::process::Command; use std::process::Stdio; +use log::info; + #[derive(Default, Clone, Debug)] struct CollectedFiles { files: BTreeSet, diff --git a/czkawka_cli/Cargo.toml b/czkawka_cli/Cargo.toml index 55d4d06..5fa4fb5 100644 --- a/czkawka_cli/Cargo.toml +++ b/czkawka_cli/Cargo.toml @@ -3,14 +3,14 @@ name = "czkawka_cli" version = "6.1.0" authors = ["RafaÅ‚ Mikrut "] edition = "2021" -rust-version = "1.72.1" +rust-version = "1.74.0" description = "CLI frontend of Czkawka" license = "MIT" homepage = "https://github.com/qarmin/czkawka" repository = "https://github.com/qarmin/czkawka" [dependencies] -clap = { version = "4.4", features = ["derive"] } +clap = { version = "4.5", features = ["derive"] } # For enum types image_hasher = "1.2" diff --git a/czkawka_cli/LICENSE b/czkawka_cli/LICENSE index 8836e46..298d0d4 100644 --- a/czkawka_cli/LICENSE +++ b/czkawka_cli/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020-2023 RafaÅ‚ Mikrut +Copyright (c) 2020-2024 RafaÅ‚ Mikrut Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/czkawka_cli/README.md b/czkawka_cli/README.md index fb808d8..aa9a47c 100644 --- a/czkawka_cli/README.md +++ b/czkawka_cli/README.md @@ -25,7 +25,7 @@ cargo run --release --bin czkawka_cli --features "heif,libraw" ``` on linux to build fully static binary with eyra you need to use (this is only for crazy people, so just use command above if you don't know what you are doing) ```shell -rustup default nightly-2023-11-16 # or any newer nightly that works fine with eyra +rustup default nightly-2024-02-06 # or any newer nightly that works fine with eyra cd czkawka_cli cargo add eyra --rename=std echo 'fn main() { println!("cargo:rustc-link-arg=-nostartfiles"); }' > build.rs diff --git a/czkawka_cli/src/commands.rs b/czkawka_cli/src/commands.rs index 44a6440..501caf4 100644 --- a/czkawka_cli/src/commands.rs +++ b/czkawka_cli/src/commands.rs @@ -428,6 +428,8 @@ pub struct CommonCliItems { long_help = "List of checked files with provided extension(s). There are also helpful macros which allow to easy use a typical extensions like:\nIMAGE(\"jpg,kra,gif,png,bmp,tiff,hdr,svg\"),\nTEXT(\"txt,doc,docx,odt,rtf\"),\nVIDEO(\"mp4,flv,mkv,webm,vob,ogv,gifv,avi,mov,wmv,mpg,m4v,m4p,mpeg,3gp\") or\nMUSIC(\"mp3,flac,ogg,tta,wma,webm\")\n " )] pub allowed_extensions: Vec, + #[clap(short = 'E', long, help = "Excluded file extension(s)", long_help = "List of extensions, that will be removed from search.\n ")] + pub excluded_extensions: Vec, #[clap(flatten)] pub file_to_save: FileToSave, #[clap(flatten)] diff --git a/czkawka_cli/src/main.rs b/czkawka_cli/src/main.rs index ce3f9d1..8cd6231 100644 --- a/czkawka_cli/src/main.rs +++ b/czkawka_cli/src/main.rs @@ -10,7 +10,7 @@ use commands::Commands; use czkawka_core::bad_extensions::BadExtensions; use czkawka_core::big_file::{BigFile, SearchMode}; use czkawka_core::broken_files::BrokenFiles; -use czkawka_core::common::{print_version_mode, set_number_of_threads, setup_logger}; +use czkawka_core::common::{print_version_mode, set_number_of_threads, setup_logger, DEFAULT_THREAD_SIZE}; use czkawka_core::common_dir_traversal::ProgressData; use czkawka_core::common_tool::{CommonData, DeleteMethod}; #[allow(unused_imports)] // It is used in release for print_results_to_output(). @@ -46,7 +46,7 @@ fn main() { let (progress_sender, progress_receiver): (Sender, Receiver) = unbounded(); let (stop_sender, stop_receiver): (Sender<()>, Receiver<()>) = bounded(1); - let calculate_thread = thread::spawn(move || match command { + let calculate_thread = thread::Builder::new().stack_size(DEFAULT_THREAD_SIZE).spawn(move || match command { Commands::Duplicates(duplicates_args) => duplicates(duplicates_args, &stop_receiver, &progress_sender), Commands::EmptyFolders(empty_folders_args) => empty_folders(empty_folders_args, &stop_receiver, &progress_sender), Commands::BiggestFiles(biggest_files_args) => biggest_files(biggest_files_args, &stop_receiver, &progress_sender), @@ -70,7 +70,7 @@ fn main() { connect_progress(&progress_receiver); - calculate_thread.join().unwrap(); + calculate_thread.unwrap().join().unwrap(); } fn duplicates(duplicates: DuplicatesArgs, stop_receiver: &Receiver<()>, progress_sender: &Sender) { diff --git a/czkawka_core/Cargo.toml b/czkawka_core/Cargo.toml index 7e41d1e..7ae6b0f 100644 --- a/czkawka_core/Cargo.toml +++ b/czkawka_core/Cargo.toml @@ -3,7 +3,7 @@ name = "czkawka_core" version = "6.1.0" authors = ["RafaÅ‚ Mikrut "] edition = "2021" -rust-version = "1.72.1" +rust-version = "1.74.0" description = "Core of Czkawka app" license = "MIT" homepage = "https://github.com/qarmin/czkawka" @@ -34,15 +34,15 @@ audio_checker = "0.1" pdf = "0.9" # Needed by audio similarity feature -rusty-chromaprint = "0.1" +rusty-chromaprint = "0.2" symphonia = { version = "0.5", features = ["all"] } # Hashes for duplicate files blake3 = "1.5" -crc32fast = "1.3" +crc32fast = "1.4" xxhash-rust = { version = "0.8", features = ["xxh3"] } -tempfile = "3.9" +tempfile = "3.10" # Video Duplicates vid_dup_finder_lib = "0.1" @@ -55,7 +55,7 @@ serde_json = "1.0" # Language i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] } -i18n-embed-fl = "0.7" +i18n-embed-fl = "0.8" rust-embed = { version = "8.2", features = ["debug-embed"] } once_cell = "1.19" @@ -75,6 +75,7 @@ anyhow = { version = "1.0" } state = "0.6" +trash = "3.3" os_info = { version = "3", default-features = false } log = "0.4.20" diff --git a/czkawka_core/LICENSE b/czkawka_core/LICENSE index 8836e46..298d0d4 100644 --- a/czkawka_core/LICENSE +++ b/czkawka_core/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020-2023 RafaÅ‚ Mikrut +Copyright (c) 2020-2024 RafaÅ‚ Mikrut Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/czkawka_core/src/bad_extensions.rs b/czkawka_core/src/bad_extensions.rs index abbb8e3..38d1021 100644 --- a/czkawka_core/src/bad_extensions.rs +++ b/czkawka_core/src/bad_extensions.rs @@ -195,7 +195,7 @@ impl BadExtensions { #[fun_time(message = "find_bad_extensions_files", level = "info")] pub fn find_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { - self.optimize_dirs_before_start(); + self.prepare_items(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; return; diff --git a/czkawka_core/src/big_file.rs b/czkawka_core/src/big_file.rs index d6a3239..cd086b0 100644 --- a/czkawka_core/src/big_file.rs +++ b/czkawka_core/src/big_file.rs @@ -43,7 +43,7 @@ impl BigFile { #[fun_time(message = "find_big_files", level = "info")] pub fn find_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { - self.optimize_dirs_before_start(); + self.prepare_items(); if !self.look_for_big_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; return; @@ -60,6 +60,7 @@ impl BigFile { .progress_sender(progress_sender) .common_data(&self.common_data) .minimal_file_size(1) + .maximal_file_size(u64::MAX) .max_stage(0) .build() .run(); diff --git a/czkawka_core/src/broken_files.rs b/czkawka_core/src/broken_files.rs index b25f03b..95f2f78 100644 --- a/czkawka_core/src/broken_files.rs +++ b/czkawka_core/src/broken_files.rs @@ -1,7 +1,6 @@ use std::collections::{BTreeMap, HashSet}; use std::fs::File; use std::io::prelude::*; - use std::path::{Path, PathBuf}; use std::sync::atomic::Ordering; use std::{fs, mem, panic}; @@ -107,7 +106,7 @@ impl BrokenFiles { #[fun_time(message = "find_broken_files", level = "info")] pub fn find_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { - self.optimize_dirs_before_start(); + self.prepare_items(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; return; @@ -140,8 +139,8 @@ impl BrokenFiles { } } - self.common_data.allowed_extensions.set_and_validate_extensions(&extensions); - if !self.common_data.allowed_extensions.set_any_extensions() { + self.common_data.extensions.set_and_validate_allowed_extensions(&extensions); + if !self.common_data.extensions.set_any_extensions() { return true; } diff --git a/czkawka_core/src/common.rs b/czkawka_core/src/common.rs index 4d33b10..94c7201 100644 --- a/czkawka_core/src/common.rs +++ b/czkawka_core/src/common.rs @@ -1,4 +1,6 @@ #![allow(unused_imports)] + +use std::{fs, thread}; // I don't wanna fight with unused imports in this file, so simply ignore it to avoid too much complexity use std::cmp::Ordering; use std::ffi::OsString; @@ -8,7 +10,6 @@ use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::{atomic, Arc}; use std::thread::{sleep, JoinHandle}; use std::time::{Duration, Instant, SystemTime}; -use std::{fs, thread}; #[cfg(feature = "heif")] use anyhow::Result; @@ -38,20 +39,16 @@ use crate::duplicate::make_hard_link; use crate::CZKAWKA_VERSION; static NUMBER_OF_THREADS: state::InitCell = state::InitCell::new(); +static ALL_AVAILABLE_THREADS: state::InitCell = state::InitCell::new(); pub const DEFAULT_THREAD_SIZE: usize = 8 * 1024 * 1024; // 8 MB pub const DEFAULT_WORKER_THREAD_SIZE: usize = 4 * 1024 * 1024; // 4 MB -#[cfg(not(target_family = "windows"))] -pub const CHARACTER: char = '/'; -#[cfg(target_family = "windows")] -pub const CHARACTER: char = '\\'; - pub fn get_number_of_threads() -> usize { let data = NUMBER_OF_THREADS.get(); if *data >= 1 { *data } else { - get_default_number_of_threads() + get_all_available_threads() } } @@ -70,15 +67,19 @@ pub fn setup_logger(disabled_printing: bool) { handsome_logger::TermLogger::init(config, TerminalMode::Mixed, ColorChoice::Always).unwrap(); } -pub fn get_available_threads() -> usize { - thread::available_parallelism().map(std::num::NonZeroUsize::get).unwrap_or(1) +pub fn get_all_available_threads() -> usize { + *ALL_AVAILABLE_THREADS.get_or_init(|| { + let available_threads = thread::available_parallelism().map(std::num::NonZeroUsize::get).unwrap_or(1); + ALL_AVAILABLE_THREADS.set(available_threads); + available_threads + }) } pub fn print_version_mode() { let rust_version = env!("RUST_VERSION_INTERNAL"); let debug_release = if cfg!(debug_assertions) { "debug" } else { "release" }; - let processors = get_available_threads(); + let processors = get_all_available_threads(); let info = os_info::get(); info!( @@ -98,11 +99,7 @@ pub fn print_version_mode() { } pub fn set_default_number_of_threads() { - set_number_of_threads(get_default_number_of_threads()); -} - -pub fn get_default_number_of_threads() -> usize { - thread::available_parallelism().map(std::num::NonZeroUsize::get).unwrap_or(1) + set_number_of_threads(get_all_available_threads()); } pub fn set_number_of_threads(thread_number: usize) { @@ -149,22 +146,21 @@ pub const VIDEO_FILES_EXTENSIONS: &[&str] = &[ pub const LOOP_DURATION: u32 = 20; //ms pub const SEND_PROGRESS_DATA_TIME_BETWEEN: u32 = 200; //ms -pub fn remove_folder_if_contains_only_empty_folders(path: impl AsRef) -> bool { +pub fn remove_folder_if_contains_only_empty_folders(path: impl AsRef, remove_to_trash: bool) -> Result<(), String> { let path = path.as_ref(); if !path.is_dir() { - error!("Trying to remove folder which is not a directory"); - return false; + return Err(format!("Trying to remove folder {path:?} which is not a directory",)); } let mut entries_to_check = Vec::new(); let Ok(initial_entry) = path.read_dir() else { - return false; + return Err(format!("Cannot read directory {path:?}",)); }; for entry in initial_entry { if let Ok(entry) = entry { entries_to_check.push(entry); } else { - return false; + return Err(format!("Cannot read entry from directory {path:?}")); } } loop { @@ -172,25 +168,29 @@ pub fn remove_folder_if_contains_only_empty_folders(path: impl AsRef) -> b break; }; let Some(file_type) = entry.file_type().ok() else { - return false; + return Err(format!("Folder contains file with unknown type {:?} inside {path:?}", entry.path())); }; if !file_type.is_dir() { - return false; + return Err(format!("Folder contains file {:?} inside {path:?}", entry.path(),)); } let Ok(internal_read_dir) = entry.path().read_dir() else { - return false; + return Err(format!("Cannot read directory {:?} inside {path:?}", entry.path())); }; for internal_elements in internal_read_dir { if let Ok(internal_element) = internal_elements { entries_to_check.push(internal_element); } else { - return false; + return Err(format!("Cannot read entry from directory {:?} inside {path:?}", entry.path())); } } } - fs::remove_dir_all(path).is_ok() + if remove_to_trash { + trash::delete(path).map_err(|e| format!("Cannot move folder {path:?} to trash, reason {e}")) + } else { + fs::remove_dir_all(path).map_err(|e| format!("Cannot remove directory {path:?}, reason {e}")) + } } pub fn open_cache_folder(cache_file_name: &str, save_to_cache: bool, use_json: bool, warnings: &mut Vec) -> Option<((Option, PathBuf), (Option, PathBuf))> { @@ -273,7 +273,6 @@ pub fn get_dynamic_image_from_raw_image(path: impl AsRef) -> Option { recursive_search: bool, directories: Option, excluded_items: Option, - allowed_extensions: Option, + extensions: Option, tool_type: ToolType, } @@ -127,7 +126,7 @@ pub struct DirTraversal<'a, 'b, F> { recursive_search: bool, directories: Directories, excluded_items: ExcludedItems, - allowed_extensions: Extensions, + extensions: Extensions, minimal_file_size: u64, maximal_file_size: u64, checking_method: CheckingMethod, @@ -156,7 +155,7 @@ impl<'a, 'b> DirTraversalBuilder<'a, 'b, ()> { collect: Collect::Files, recursive_search: false, directories: None, - allowed_extensions: None, + extensions: None, excluded_items: None, tool_type: ToolType::None, } @@ -171,7 +170,7 @@ impl<'a, 'b, F> DirTraversalBuilder<'a, 'b, F> { pub fn common_data(mut self, common_tool_data: &CommonToolData) -> Self { self.root_dirs = common_tool_data.directories.included_directories.clone(); - self.allowed_extensions = Some(common_tool_data.allowed_extensions.clone()); + self.extensions = Some(common_tool_data.extensions.clone()); self.excluded_items = Some(common_tool_data.excluded_items.clone()); self.recursive_search = common_tool_data.recursive_search; self.minimal_file_size = Some(common_tool_data.minimal_file_size); @@ -221,8 +220,8 @@ impl<'a, 'b, F> DirTraversalBuilder<'a, 'b, F> { self } - pub fn allowed_extensions(mut self, allowed_extensions: Extensions) -> Self { - self.allowed_extensions = Some(allowed_extensions); + pub fn extensions(mut self, extensions: Extensions) -> Self { + self.extensions = Some(extensions); self } @@ -260,7 +259,7 @@ impl<'a, 'b, F> DirTraversalBuilder<'a, 'b, F> { stop_receiver: self.stop_receiver, progress_sender: self.progress_sender, directories: self.directories, - allowed_extensions: self.allowed_extensions, + extensions: self.extensions, excluded_items: self.excluded_items, recursive_search: self.recursive_search, maximal_file_size: self.maximal_file_size, @@ -285,7 +284,7 @@ impl<'a, 'b, F> DirTraversalBuilder<'a, 'b, F> { collect: self.collect, directories: self.directories.expect("could not build"), excluded_items: self.excluded_items.expect("could not build"), - allowed_extensions: self.allowed_extensions.unwrap_or_default(), + extensions: self.extensions.unwrap_or_default(), recursive_search: self.recursive_search, tool_type: self.tool_type, } @@ -334,7 +333,7 @@ where collect, directories, excluded_items, - allowed_extensions, + extensions, recursive_search, minimal_file_size, maximal_file_size, @@ -377,7 +376,7 @@ where entry_data, &mut warnings, &mut fe_result, - &allowed_extensions, + &extensions, &directories, &excluded_items, minimal_file_size, @@ -389,7 +388,7 @@ where } (EntryType::Symlink, Collect::InvalidSymlinks) => { counter += 1; - process_symlink_in_symlink_mode(entry_data, &mut warnings, &mut fe_result, &allowed_extensions, &directories, &excluded_items); + process_symlink_in_symlink_mode(entry_data, &mut warnings, &mut fe_result, &extensions, &directories, &excluded_items); } (EntryType::Symlink, Collect::Files) | (EntryType::Other, _) => { // nothing to do @@ -435,13 +434,13 @@ fn process_file_in_file_mode( entry_data: &DirEntry, warnings: &mut Vec, fe_result: &mut Vec, - allowed_extensions: &Extensions, + extensions: &Extensions, directories: &Directories, excluded_items: &ExcludedItems, minimal_file_size: u64, maximal_file_size: u64, ) { - if !allowed_extensions.check_if_entry_ends_with_extension(entry_data) { + if !extensions.check_if_entry_have_valid_extension(entry_data) { return; } @@ -512,11 +511,11 @@ fn process_symlink_in_symlink_mode( entry_data: &DirEntry, warnings: &mut Vec, fe_result: &mut Vec, - allowed_extensions: &Extensions, + extensions: &Extensions, directories: &Directories, excluded_items: &ExcludedItems, ) { - if !allowed_extensions.check_if_entry_ends_with_extension(entry_data) { + if !extensions.check_if_entry_have_valid_extension(entry_data) { return; } @@ -560,10 +559,7 @@ pub fn common_read_dir(current_folder: &Path, warnings: &mut Vec) -> Opt Some(r) } Err(e) => { - warnings.push(flc!( - "core_cannot_open_dir", - generate_translation_hashmap(vec![("dir", current_folder.to_string_lossy().to_string()), ("reason", e.to_string())]) - )); + warnings.push(flc!("core_cannot_open_dir", dir = current_folder.to_string_lossy().to_string(), reason = e.to_string())); None } } @@ -574,7 +570,8 @@ pub fn common_get_entry_data<'a>(entry: &'a Result, wa Err(e) => { warnings.push(flc!( "core_cannot_read_entry_dir", - generate_translation_hashmap(vec![("dir", current_folder.to_string_lossy().to_string()), ("reason", e.to_string())]) + dir = current_folder.to_string_lossy().to_string(), + reason = e.to_string() )); return None; } @@ -587,7 +584,8 @@ pub fn common_get_metadata_dir(entry_data: &DirEntry, warnings: &mut Vec Err(e) => { warnings.push(flc!( "core_cannot_read_metadata_dir", - generate_translation_hashmap(vec![("dir", current_folder.to_string_lossy().to_string()), ("reason", e.to_string())]) + dir = current_folder.to_string_lossy().to_string(), + reason = e.to_string() )); return None; } @@ -601,7 +599,8 @@ pub fn common_get_entry_data_metadata<'a>(entry: &'a Result { warnings.push(flc!( "core_cannot_read_entry_dir", - generate_translation_hashmap(vec![("dir", current_folder.to_string_lossy().to_string()), ("reason", e.to_string())]) + dir = current_folder.to_string_lossy().to_string(), + reason = e.to_string() )); return None; } @@ -611,7 +610,8 @@ pub fn common_get_entry_data_metadata<'a>(entry: &'a Result { warnings.push(flc!( "core_cannot_read_metadata_dir", - generate_translation_hashmap(vec![("dir", current_folder.to_string_lossy().to_string()), ("reason", e.to_string())]) + dir = current_folder.to_string_lossy().to_string(), + reason = e.to_string() )); return None; } @@ -624,21 +624,27 @@ pub fn get_modified_time(metadata: &Metadata, warnings: &mut Vec, curren Ok(t) => match t.duration_since(UNIX_EPOCH) { Ok(d) => d.as_secs(), Err(_inspected) => { - let translation_hashmap = generate_translation_hashmap(vec![("name", current_file_name.to_string_lossy().to_string())]); if is_folder { - warnings.push(flc!("core_folder_modified_before_epoch", translation_hashmap)); + warnings.push(flc!("core_folder_modified_before_epoch", name = current_file_name.to_string_lossy().to_string())); } else { - warnings.push(flc!("core_file_modified_before_epoch", translation_hashmap)); + warnings.push(flc!("core_file_modified_before_epoch", name = current_file_name.to_string_lossy().to_string())); } 0 } }, Err(e) => { - let translation_hashmap = generate_translation_hashmap(vec![("name", current_file_name.to_string_lossy().to_string()), ("reason", e.to_string())]); if is_folder { - warnings.push(flc!("core_folder_no_modification_date", translation_hashmap)); + warnings.push(flc!( + "core_folder_no_modification_date", + name = current_file_name.to_string_lossy().to_string(), + reason = e.to_string() + )); } else { - warnings.push(flc!("core_file_no_modification_date", translation_hashmap)); + warnings.push(flc!( + "core_file_no_modification_date", + name = current_file_name.to_string_lossy().to_string(), + reason = e.to_string() + )); } 0 } diff --git a/czkawka_core/src/common_directory.rs b/czkawka_core/src/common_directory.rs index 732bd07..cf2524c 100644 --- a/czkawka_core/src/common_directory.rs +++ b/czkawka_core/src/common_directory.rs @@ -1,11 +1,10 @@ -use crate::common::normalize_windows_path; use std::path::{Path, PathBuf}; #[cfg(target_family = "unix")] use std::{fs, os::unix::fs::MetadataExt}; +use crate::common::normalize_windows_path; use crate::common_messages::Messages; use crate::flc; -use crate::localizer_core::generate_translation_hashmap; #[derive(Debug, Clone, Default)] pub struct Directories { @@ -105,19 +104,15 @@ impl Directories { let mut directory = directory.to_path_buf(); if !directory.exists() { if !is_excluded { - messages.warnings.push(flc!( - "core_directory_must_exists", - generate_translation_hashmap(vec![("path", directory.to_string_lossy().to_string())]) - )); + messages.warnings.push(flc!("core_directory_must_exists", path = directory.to_string_lossy().to_string())); } return (None, messages); } if !directory.is_dir() { - messages.warnings.push(flc!( - "core_directory_must_be_directory", - generate_translation_hashmap(vec![("path", directory.to_string_lossy().to_string())]) - )); + messages + .warnings + .push(flc!("core_directory_must_be_directory", path = directory.to_string_lossy().to_string())); return (None, messages); } @@ -290,10 +285,7 @@ impl Directories { for d in &self.included_directories { match fs::metadata(d) { Ok(m) => self.included_dev_ids.push(m.dev()), - Err(_) => messages.errors.push(flc!( - "core_directory_unable_to_get_device_id", - generate_translation_hashmap(vec![("path", d.to_string_lossy().to_string())]) - )), + Err(_) => messages.errors.push(flc!("core_directory_unable_to_get_device_id", path = d.to_string_lossy().to_string())), } } } @@ -322,10 +314,7 @@ impl Directories { let path = path.as_ref(); match fs::metadata(path) { Ok(m) => Ok(!self.included_dev_ids.iter().any(|&id| id == m.dev())), - Err(_) => Err(flc!( - "core_directory_unable_to_get_device_id", - generate_translation_hashmap(vec![("path", path.to_string_lossy().to_string())]) - )), + Err(_) => Err(flc!("core_directory_unable_to_get_device_id", path = path.to_string_lossy().to_string())), } } } diff --git a/czkawka_core/src/common_extensions.rs b/czkawka_core/src/common_extensions.rs index d210fd3..a3336e3 100644 --- a/czkawka_core/src/common_extensions.rs +++ b/czkawka_core/src/common_extensions.rs @@ -1,37 +1,39 @@ -use crate::common_messages::Messages; use std::collections::HashSet; use std::fs::DirEntry; +use crate::common_messages::Messages; + #[derive(Debug, Clone, Default)] pub struct Extensions { - file_extensions_hashset: HashSet, + allowed_extensions_hashset: HashSet, + excluded_extensions_hashset: HashSet, } impl Extensions { pub fn new() -> Self { Default::default() } - /// List of allowed extensions, only files with this extensions will be checking if are duplicates - /// After, extensions cannot contains any dot, commas etc. - pub fn set_allowed_extensions(&mut self, mut allowed_extensions: String) -> Messages { + + pub fn filter_extensions(mut file_extensions: String) -> (HashSet, Messages) { let mut messages = Messages::new(); + let mut extensions_hashset = HashSet::new(); - if allowed_extensions.trim().is_empty() { - return messages; + if file_extensions.trim().is_empty() { + return (Default::default(), messages); } - allowed_extensions = allowed_extensions.replace("IMAGE", "jpg,kra,gif,png,bmp,tiff,hdr,svg"); - allowed_extensions = allowed_extensions.replace("VIDEO", "mp4,flv,mkv,webm,vob,ogv,gifv,avi,mov,wmv,mpg,m4v,m4p,mpeg,3gp"); - allowed_extensions = allowed_extensions.replace("MUSIC", "mp3,flac,ogg,tta,wma,webm"); - allowed_extensions = allowed_extensions.replace("TEXT", "txt,doc,docx,odt,rtf"); + file_extensions = file_extensions.replace("IMAGE", "jpg,kra,gif,png,bmp,tiff,hdr,svg"); + file_extensions = file_extensions.replace("VIDEO", "mp4,flv,mkv,webm,vob,ogv,gifv,avi,mov,wmv,mpg,m4v,m4p,mpeg,3gp"); + file_extensions = file_extensions.replace("MUSIC", "mp3,flac,ogg,tta,wma,webm"); + file_extensions = file_extensions.replace("TEXT", "txt,doc,docx,odt,rtf"); - let extensions: Vec = allowed_extensions.split(',').map(str::trim).map(String::from).collect(); + let extensions: Vec = file_extensions.split(',').map(str::trim).map(String::from).collect(); for mut extension in extensions { if extension.is_empty() || extension.replace(['.', ' '], "").trim().is_empty() { continue; } if extension.starts_with('.') { - extension = extension[1..].to_string(); + extension = extension.chars().skip(1).collect::(); } if extension.contains('.') { @@ -44,49 +46,61 @@ impl Extensions { continue; } - self.file_extensions_hashset.insert(extension); + extensions_hashset.insert(extension); } + (extensions_hashset, messages) + } - if self.file_extensions_hashset.is_empty() { - messages - .messages - .push("No valid extensions were provided, so allowing all extensions by default.".to_string()); - } + /// List of allowed extensions, only files with this extensions will be checking if are duplicates + /// After, extensions cannot contain any dot, commas etc. + pub fn set_allowed_extensions(&mut self, allowed_extensions: String) -> Messages { + let (extensions, messages) = Self::filter_extensions(allowed_extensions); + + self.allowed_extensions_hashset = extensions; messages } - pub fn matches_filename(&self, file_name: &str) -> bool { - // assert_eq!(file_name, file_name.to_lowercase()); - if !self.file_extensions_hashset.is_empty() && !self.file_extensions_hashset.iter().any(|e| file_name.ends_with(e)) { - return false; - } - true + pub fn set_excluded_extensions(&mut self, excluded_extensions: String) -> Messages { + let (extensions, messages) = Self::filter_extensions(excluded_extensions); + + self.excluded_extensions_hashset = extensions; + messages } - pub fn check_if_entry_ends_with_extension(&self, entry_data: &DirEntry) -> bool { - if self.file_extensions_hashset.is_empty() { + + pub fn check_if_entry_have_valid_extension(&self, entry_data: &DirEntry) -> bool { + if self.allowed_extensions_hashset.is_empty() && self.excluded_extensions_hashset.is_empty() { return true; } + // Using entry_data.path().extension() is a lot of slower, even 5 times let file_name = entry_data.file_name(); let Some(file_name_str) = file_name.to_str() else { return false }; let Some(extension_idx) = file_name_str.rfind('.') else { return false }; let extension = &file_name_str[extension_idx + 1..]; - if extension.chars().all(|c| c.is_ascii_lowercase()) { - self.file_extensions_hashset.contains(extension) + if !self.allowed_extensions_hashset.is_empty() { + if extension.chars().all(|c| c.is_ascii_lowercase()) { + self.allowed_extensions_hashset.contains(extension) + } else { + self.allowed_extensions_hashset.contains(&extension.to_lowercase()) + } } else { - self.file_extensions_hashset.contains(&extension.to_lowercase()) + if extension.chars().all(|c| c.is_ascii_lowercase()) { + !self.excluded_extensions_hashset.contains(extension) + } else { + !self.excluded_extensions_hashset.contains(&extension.to_lowercase()) + } } } pub fn set_any_extensions(&self) -> bool { - !self.file_extensions_hashset.is_empty() + !self.allowed_extensions_hashset.is_empty() } fn extend_allowed_extensions(&mut self, file_extensions: &[&str]) { for extension in file_extensions { let extension_without_dot = extension.trim_start_matches('.'); - self.file_extensions_hashset.insert(extension_without_dot.to_string()); + self.allowed_extensions_hashset.insert(extension_without_dot.to_string()); } } @@ -100,8 +114,8 @@ impl Extensions { } } - pub fn set_and_validate_extensions(&mut self, file_extensions: &[&str]) { - if self.file_extensions_hashset.is_empty() { + pub fn set_and_validate_allowed_extensions(&mut self, file_extensions: &[&str]) { + if self.allowed_extensions_hashset.is_empty() { self.extend_allowed_extensions(file_extensions); } else { self.union_allowed_extensions(file_extensions); diff --git a/czkawka_core/src/common_items.rs b/czkawka_core/src/common_items.rs index 6d3b6eb..2ff81c7 100644 --- a/czkawka_core/src/common_items.rs +++ b/czkawka_core/src/common_items.rs @@ -1,8 +1,8 @@ +use std::path::Path; + #[cfg(not(target_family = "unix"))] use crate::common::normalize_windows_path; use crate::common::regex_check; -use std::path::Path; - use crate::common_messages::Messages; #[cfg(target_family = "unix")] diff --git a/czkawka_core/src/common_messages.rs b/czkawka_core/src/common_messages.rs index 0b45743..9e4b26f 100644 --- a/czkawka_core/src/common_messages.rs +++ b/czkawka_core/src/common_messages.rs @@ -9,6 +9,15 @@ impl Messages { pub fn new() -> Self { Default::default() } + pub fn new_from_errors(errors: Vec) -> Self { + Messages { errors, ..Default::default() } + } + pub fn new_from_warnings(warnings: Vec) -> Self { + Messages { warnings, ..Default::default() } + } + pub fn new_from_messages(messages: Vec) -> Self { + Messages { messages, ..Default::default() } + } pub fn print_messages(&self) { println!("{}", self.create_messages_text()); } diff --git a/czkawka_core/src/common_tool.rs b/czkawka_core/src/common_tool.rs index 49b96e7..16d74cd 100644 --- a/czkawka_core/src/common_tool.rs +++ b/czkawka_core/src/common_tool.rs @@ -11,7 +11,7 @@ pub struct CommonToolData { pub(crate) tool_type: ToolType, pub(crate) text_messages: Messages, pub(crate) directories: Directories, - pub(crate) allowed_extensions: Extensions, + pub(crate) extensions: Extensions, pub(crate) excluded_items: ExcludedItems, pub(crate) recursive_search: bool, pub(crate) delete_method: DeleteMethod, @@ -43,7 +43,7 @@ impl CommonToolData { tool_type, text_messages: Messages::new(), directories: Directories::new(), - allowed_extensions: Extensions::new(), + extensions: Extensions::new(), excluded_items: ExcludedItems::new(), recursive_search: true, delete_method: DeleteMethod::None, @@ -168,7 +168,11 @@ pub trait CommonData { self.get_cd_mut().text_messages.extend_with_another_messages(messages); } fn set_allowed_extensions(&mut self, allowed_extensions: String) { - let messages = self.get_cd_mut().allowed_extensions.set_allowed_extensions(allowed_extensions); + let messages = self.get_cd_mut().extensions.set_allowed_extensions(allowed_extensions); + self.get_cd_mut().text_messages.extend_with_another_messages(messages); + } + fn set_excluded_extensions(&mut self, excluded_extensions: String) { + let messages = self.get_cd_mut().extensions.set_excluded_extensions(excluded_extensions); self.get_cd_mut().text_messages.extend_with_another_messages(messages); } @@ -177,8 +181,9 @@ pub trait CommonData { self.get_cd_mut().text_messages.extend_with_another_messages(messages); } - fn optimize_dirs_before_start(&mut self) { + fn prepare_items(&mut self) { let recursive_search = self.get_cd().recursive_search; + // Optimizes directories and removes recursive calls let messages = self.get_cd_mut().directories.optimize_directories(recursive_search); self.get_cd_mut().text_messages.extend_with_another_messages(messages); } @@ -187,7 +192,7 @@ pub trait CommonData { println!("---------------DEBUG PRINT COMMON---------------"); println!("Tool type: {:?}", self.get_cd().tool_type); println!("Directories: {:?}", self.get_cd().directories); - println!("Allowed extensions: {:?}", self.get_cd().allowed_extensions); + println!("Extensions: {:?}", self.get_cd().extensions); println!("Excluded items: {:?}", self.get_cd().excluded_items); println!("Recursive search: {:?}", self.get_cd().recursive_search); println!("Maximal file size: {:?}", self.get_cd().maximal_file_size); diff --git a/czkawka_core/src/common_traits.rs b/czkawka_core/src/common_traits.rs index 27aaebd..744d33e 100644 --- a/czkawka_core/src/common_traits.rs +++ b/czkawka_core/src/common_traits.rs @@ -1,9 +1,10 @@ -use fun_time::fun_time; -use serde::Serialize; use std::fs::File; use std::io::{BufWriter, Write}; use std::path::Path; +use fun_time::fun_time; +use serde::Serialize; + pub trait DebugPrint { fn debug_print(&self); } diff --git a/czkawka_core/src/duplicate.rs b/czkawka_core/src/duplicate.rs index 3cf832b..8a62f7b 100644 --- a/czkawka_core/src/duplicate.rs +++ b/czkawka_core/src/duplicate.rs @@ -134,7 +134,7 @@ impl DuplicateFinder { ignore_hard_links: true, hash_type: HashType::Blake3, use_prehash_cache: true, - minimal_cache_file_size: 1024 * 3256, // By default cache only >= 256 KB files + minimal_cache_file_size: 1024 * 256, // By default cache only >= 256 KB files minimal_prehash_cache_file_size: 0, case_sensitive_name_comparison: false, } @@ -142,7 +142,7 @@ impl DuplicateFinder { #[fun_time(message = "find_duplicates", level = "info")] pub fn find_duplicates(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { - self.optimize_dirs_before_start(); + self.prepare_items(); self.common_data.use_reference_folders = !self.common_data.directories.reference_directories.is_empty(); match self.check_method { diff --git a/czkawka_core/src/empty_files.rs b/czkawka_core/src/empty_files.rs index a396c26..b64d2b3 100644 --- a/czkawka_core/src/empty_files.rs +++ b/czkawka_core/src/empty_files.rs @@ -1,5 +1,4 @@ use std::fs; - use std::io::prelude::*; use crossbeam_channel::{Receiver, Sender}; @@ -41,7 +40,7 @@ impl EmptyFiles { #[fun_time(message = "find_empty_files", level = "info")] pub fn find_empty_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { - self.optimize_dirs_before_start(); + self.prepare_items(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; return; diff --git a/czkawka_core/src/empty_folder.rs b/czkawka_core/src/empty_folder.rs index 1e1346a..bb13f88 100644 --- a/czkawka_core/src/empty_folder.rs +++ b/czkawka_core/src/empty_folder.rs @@ -5,12 +5,12 @@ use std::io::Write; use std::path::{Path, PathBuf}; use std::sync::atomic::Ordering; -use crate::common::{check_if_stop_received, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads}; use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; use log::debug; use rayon::prelude::*; +use crate::common::{check_if_stop_received, prepare_thread_handler_common, send_info_and_wait_for_ending_all_threads}; use crate::common_dir_traversal::{common_get_entry_data, common_get_metadata_dir, common_read_dir, get_modified_time, CheckingMethod, ProgressData, ToolType}; use crate::common_directory::Directories; use crate::common_items::ExcludedItems; @@ -26,6 +26,12 @@ pub struct FolderEntry { pub modified_date: u64, } +impl FolderEntry { + pub fn get_modified_date(&self) -> u64 { + self.modified_date + } +} + pub struct EmptyFolder { common_data: CommonToolData, information: Info, @@ -64,7 +70,7 @@ impl EmptyFolder { #[fun_time(message = "find_empty_folders", level = "info")] pub fn find_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { - self.optimize_dirs_before_start(); + self.prepare_items(); if !self.check_for_empty_folders(stop_receiver, progress_sender) { self.common_data.stopped_search = true; return; @@ -104,19 +110,17 @@ impl EmptyFolder { let excluded_items = self.common_data.excluded_items.clone(); let directories = self.common_data.directories.clone(); - let mut folder_entries: HashMap = HashMap::new(); let mut non_empty_folders: Vec = vec![]; + let mut start_folder_entries = Vec::with_capacity(folders_to_check.len()); + let mut new_folder_entries_list = Vec::new(); for dir in &folders_to_check { - folder_entries.insert( - dir.to_string_lossy().to_string(), - FolderEntry { - path: dir.clone(), - parent_path: None, - is_empty: FolderEmptiness::Maybe, - modified_date: 0, - }, - ); + start_folder_entries.push(FolderEntry { + path: dir.clone(), + parent_path: None, + is_empty: FolderEmptiness::Maybe, + modified_date: 0, + }); } while !folders_to_check.is_empty() { @@ -130,12 +134,13 @@ impl EmptyFolder { .map(|current_folder| { let mut dir_result = vec![]; let mut warnings = vec![]; - let mut set_as_not_empty_folder_list = vec![]; + let mut non_empty_folder = None; let mut folder_entries_list = vec![]; + let current_folder_as_string = current_folder.to_string_lossy().to_string(); + let Some(read_dir) = common_read_dir(¤t_folder, &mut warnings) else { - set_as_not_empty_folder_list.push(current_folder.to_string_lossy().to_string()); - return (dir_result, warnings, set_as_not_empty_folder_list, folder_entries_list); + return (dir_result, warnings, Some(current_folder_as_string), folder_entries_list); }; let mut counter = 0; @@ -150,23 +155,27 @@ impl EmptyFolder { counter += 1; Self::process_dir_in_dir_mode( ¤t_folder, + ¤t_folder_as_string, entry_data, &directories, &mut dir_result, &mut warnings, &excluded_items, - &mut set_as_not_empty_folder_list, + &mut non_empty_folder, &mut folder_entries_list, ); } else { - set_as_not_empty_folder_list.push(current_folder.to_string_lossy().to_string()); + if non_empty_folder.is_none() { + non_empty_folder = Some(current_folder_as_string.clone()); + } } } if counter > 0 { // Increase counter in batch, because usually it may be slow to add multiple times atomic value atomic_counter.fetch_add(counter, Ordering::Relaxed); } - (dir_result, warnings, set_as_not_empty_folder_list, folder_entries_list) + + (dir_result, warnings, non_empty_folder, folder_entries_list) }) .collect(); @@ -174,13 +183,25 @@ impl EmptyFolder { folders_to_check = Vec::with_capacity(required_size); // Process collected data - for (segment, warnings, set_as_not_empty_folder_list, fe_list) in segments { + for (segment, warnings, non_empty_folder, fe_list) in segments { folders_to_check.extend(segment); - self.common_data.text_messages.warnings.extend(warnings); - non_empty_folders.extend(set_as_not_empty_folder_list); - for (path, entry) in fe_list { - folder_entries.insert(path, entry); + if !warnings.is_empty() { + self.common_data.text_messages.warnings.extend(warnings); } + if let Some(non_empty_folder) = non_empty_folder { + non_empty_folders.push(non_empty_folder); + } + new_folder_entries_list.push(fe_list); + } + } + + let mut folder_entries: HashMap = HashMap::with_capacity(start_folder_entries.len() + new_folder_entries_list.iter().map(Vec::len).sum::()); + for fe in start_folder_entries { + folder_entries.insert(fe.path.to_string_lossy().to_string(), fe); + } + for fe_list in new_folder_entries_list { + for fe in fe_list { + folder_entries.insert(fe.path.to_string_lossy().to_string(), fe); } } @@ -223,18 +244,20 @@ impl EmptyFolder { fn process_dir_in_dir_mode( current_folder: &Path, + current_folder_as_str: &str, entry_data: &DirEntry, directories: &Directories, dir_result: &mut Vec, warnings: &mut Vec, excluded_items: &ExcludedItems, - set_as_not_empty_folder_list: &mut Vec, - folder_entries_list: &mut Vec<(String, FolderEntry)>, + non_empty_folder: &mut Option, + folder_entries_list: &mut Vec, ) { - let parent_folder_str = current_folder.to_string_lossy().to_string(); let next_folder = entry_data.path(); if excluded_items.is_excluded(&next_folder) || directories.is_excluded(&next_folder) { - set_as_not_empty_folder_list.push(parent_folder_str); + if non_empty_folder.is_none() { + *non_empty_folder = Some(current_folder_as_str.to_string()); + } return; } @@ -248,20 +271,19 @@ impl EmptyFolder { } let Some(metadata) = common_get_metadata_dir(entry_data, warnings, &next_folder) else { - set_as_not_empty_folder_list.push(parent_folder_str); + if non_empty_folder.is_none() { + *non_empty_folder = Some(current_folder_as_str.to_string()); + } return; }; dir_result.push(next_folder.clone()); - folder_entries_list.push(( - next_folder.to_string_lossy().to_string(), - FolderEntry { - path: next_folder, - parent_path: Some(parent_folder_str), - is_empty: FolderEmptiness::Maybe, - modified_date: get_modified_time(&metadata, warnings, current_folder, true), - }, - )); + folder_entries_list.push(FolderEntry { + path: next_folder, + parent_path: Some(current_folder_as_str.to_string()), + is_empty: FolderEmptiness::Maybe, + modified_date: get_modified_time(&metadata, warnings, current_folder, true), + }); } #[fun_time(message = "delete_files", level = "debug")] diff --git a/czkawka_core/src/invalid_symlinks.rs b/czkawka_core/src/invalid_symlinks.rs index ba00b58..a10c175 100644 --- a/czkawka_core/src/invalid_symlinks.rs +++ b/czkawka_core/src/invalid_symlinks.rs @@ -1,5 +1,4 @@ use std::fs; - use std::io::prelude::*; use std::path::{Path, PathBuf}; @@ -73,7 +72,7 @@ impl InvalidSymlinks { #[fun_time(message = "find_invalid_links", level = "info")] pub fn find_invalid_links(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { - self.optimize_dirs_before_start(); + self.prepare_items(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; return; diff --git a/czkawka_core/src/same_music.rs b/czkawka_core/src/same_music.rs index 0664b48..b97215a 100644 --- a/czkawka_core/src/same_music.rs +++ b/czkawka_core/src/same_music.rs @@ -2,14 +2,12 @@ use std::cmp::max; use std::collections::{BTreeMap, HashSet}; use std::fs::File; use std::io::prelude::*; - use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::{mem, panic}; use anyhow::Context; - use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; use humansize::{format_size, BINARY}; @@ -138,7 +136,7 @@ impl SameMusic { #[fun_time(message = "find_same_music", level = "info")] pub fn find_same_music(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { - self.optimize_dirs_before_start(); + self.prepare_items(); self.common_data.use_reference_folders = !self.common_data.directories.reference_directories.is_empty(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; @@ -177,8 +175,8 @@ impl SameMusic { #[fun_time(message = "check_files", level = "debug")] fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { - self.common_data.allowed_extensions.set_and_validate_extensions(AUDIO_FILES_EXTENSIONS); - if !self.common_data.allowed_extensions.set_any_extensions() { + self.common_data.extensions.set_and_validate_allowed_extensions(AUDIO_FILES_EXTENSIONS); + if !self.common_data.extensions.set_any_extensions() { return true; } diff --git a/czkawka_core/src/similar_images.rs b/czkawka_core/src/similar_images.rs index f13098b..d652de7 100644 --- a/czkawka_core/src/similar_images.rs +++ b/czkawka_core/src/similar_images.rs @@ -6,7 +6,6 @@ use std::time::SystemTime; use std::{mem, panic}; use bk_tree::BKTree; - use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; use humansize::{format_size, BINARY}; @@ -41,7 +40,8 @@ pub const SIMILAR_VALUES: [[u32; 6]; 4] = [ pub struct ImagesEntry { pub path: PathBuf, pub size: u64, - pub dimensions: String, + pub width: u32, + pub height: u32, pub modified_date: u64, pub hash: ImHash, pub similarity: u32, @@ -66,7 +66,8 @@ impl FileEntry { path: self.path, modified_date: self.modified_date, - dimensions: String::new(), + width: 0, + height: 0, hash: Vec::new(), similarity: 0, image_type: ImageType::Unknown, @@ -152,7 +153,7 @@ impl SimilarImages { #[fun_time(message = "find_similar_images", level = "info")] pub fn find_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { - self.optimize_dirs_before_start(); + self.prepare_items(); self.common_data.use_reference_folders = !self.common_data.directories.reference_directories.is_empty(); if !self.check_for_similar_images(stop_receiver, progress_sender) { self.common_data.stopped_search = true; @@ -174,14 +175,14 @@ impl SimilarImages { fn check_for_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { if cfg!(feature = "heif") { self.common_data - .allowed_extensions - .set_and_validate_extensions(&[IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, RAW_IMAGE_EXTENSIONS, HEIC_EXTENSIONS].concat()); + .extensions + .set_and_validate_allowed_extensions(&[IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, RAW_IMAGE_EXTENSIONS, HEIC_EXTENSIONS].concat()); } else { self.common_data - .allowed_extensions - .set_and_validate_extensions(&[IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, RAW_IMAGE_EXTENSIONS].concat()); + .extensions + .set_and_validate_allowed_extensions(&[IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, RAW_IMAGE_EXTENSIONS].concat()); } - if !self.common_data.allowed_extensions.set_any_extensions() { + if !self.common_data.extensions.set_any_extensions() { return true; } @@ -294,7 +295,7 @@ impl SimilarImages { .while_some() .filter_map(|e| e) .collect::>(); - debug!("hash_images - end hashing images"); + debug!("hash_images - end hashing {} images", vec_file_entry.len()); send_info_and_wait_for_ending_all_threads(&progress_thread_run, progress_thread_handle); @@ -396,7 +397,8 @@ impl SimilarImages { let dimensions = img.dimensions(); - file_entry.dimensions = format!("{}x{}", dimensions.0, dimensions.1); + file_entry.width = dimensions.0; + file_entry.height = dimensions.1; let hasher_config = HasherConfig::new() .hash_size(self.hash_size as u32, self.hash_size as u32) @@ -460,54 +462,21 @@ impl SimilarImages { collected_similar_images: &mut HashMap>, hashes_similarity: HashMap, ) { - if self.common_data.use_reference_folders { - // This is same step as without reference folders, but also checks if children are inside/outside reference directories, because may happen, that one file is inside reference folder and other outside + // Collecting results to vector + for (parent_hash, child_number) in hashes_parents { + // If hash contains other hasher OR multiple images are available for checked hash + if child_number > 0 || hashes_with_multiple_images.contains(&parent_hash) { + let vec_fe = all_hashed_images[&parent_hash].clone(); + collected_similar_images.insert(parent_hash.clone(), vec_fe); + } + } - // Collecting results to vector - for (parent_hash, child_number) in hashes_parents { - // If hash contains other hasher OR multiple images are available for checked hash - if child_number > 0 || hashes_with_multiple_images.contains(&parent_hash) { - let vec_fe = all_hashed_images - .get(&parent_hash) - .unwrap() - .iter() - .filter(|e| is_in_reference_folder(&self.common_data.directories.reference_directories, &e.path)) - .cloned() - .collect(); - collected_similar_images.insert(parent_hash.clone(), vec_fe); - } - } - - for (child_hash, (parent_hash, similarity)) in hashes_similarity { - let mut vec_fe: Vec<_> = all_hashed_images - .get(&child_hash) - .unwrap() - .iter() - .filter(|e| !is_in_reference_folder(&self.common_data.directories.reference_directories, &e.path)) - .cloned() - .collect(); - for fe in &mut vec_fe { - fe.similarity = similarity; - } - collected_similar_images.get_mut(&parent_hash).unwrap().append(&mut vec_fe); - } - } else { - // Collecting results to vector - for (parent_hash, child_number) in hashes_parents { - // If hash contains other hasher OR multiple images are available for checked hash - if child_number > 0 || hashes_with_multiple_images.contains(&parent_hash) { - let vec_fe = all_hashed_images.get(&parent_hash).unwrap().clone(); - collected_similar_images.insert(parent_hash.clone(), vec_fe); - } - } - - for (child_hash, (parent_hash, similarity)) in hashes_similarity { - let mut vec_fe = all_hashed_images.get(&child_hash).unwrap().clone(); - for fe in &mut vec_fe { - fe.similarity = similarity; - } - collected_similar_images.get_mut(&parent_hash).unwrap().append(&mut vec_fe); + for (child_hash, (parent_hash, similarity)) in hashes_similarity { + let mut vec_fe = all_hashed_images[&child_hash].clone(); + for fe in &mut vec_fe { + fe.similarity = similarity; } + collected_similar_images.get_mut(&parent_hash).unwrap().append(&mut vec_fe); } } @@ -773,7 +742,7 @@ impl SimilarImages { } } } - assert!(!found, "Found Invalid entries, verify errors before"); // TODO crashes with empty result with reference folder, verify why + assert!(!found, "Found Invalid entries, verify errors before"); } fn delete_files(&mut self) { @@ -818,9 +787,10 @@ impl PrintResults for SimilarImages { for file_entry in struct_similar { writeln!( writer, - "{:?} - {} - {} - {}", + "{:?} - {}x{} - {} - {}", file_entry.path, - file_entry.dimensions, + file_entry.width, + file_entry.height, format_size(file_entry.size, BINARY), get_string_from_similarity(&file_entry.similarity, self.hash_size) )?; @@ -835,18 +805,20 @@ impl PrintResults for SimilarImages { writeln!(writer)?; writeln!( writer, - "{:?} - {} - {} - {}", + "{:?} - {}x{} - {} - {}", file_entry.path, - file_entry.dimensions, + file_entry.width, + file_entry.height, format_size(file_entry.size, BINARY), get_string_from_similarity(&file_entry.similarity, self.hash_size) )?; for file_entry in vec_file_entry { writeln!( writer, - "{:?} - {} - {} - {}", + "{:?} - {}x{} - {} - {}", file_entry.path, - file_entry.dimensions, + file_entry.width, + file_entry.height, format_size(file_entry.size, BINARY), get_string_from_similarity(&file_entry.similarity, self.hash_size) )?; @@ -1007,12 +979,12 @@ fn debug_check_for_duplicated_things( for (hash, number_of_children) in hashes_parents { if *number_of_children > 0 { if hashmap_hashes.contains(hash) { - println!("------1--HASH--{} {:?}", numm, all_hashed_images.get(hash).unwrap()); + println!("------1--HASH--{} {:?}", numm, all_hashed_images[hash]); found_broken_thing = true; } hashmap_hashes.insert((*hash).clone()); - for i in all_hashed_images.get(hash).unwrap() { + for i in &all_hashed_images[hash] { let name = i.path.to_string_lossy().to_string(); if hashmap_names.contains(&name) { println!("------1--NAME--{numm} {name:?}"); @@ -1024,12 +996,12 @@ fn debug_check_for_duplicated_things( } for hash in hashes_similarity.keys() { if hashmap_hashes.contains(hash) { - println!("------2--HASH--{} {:?}", numm, all_hashed_images.get(hash).unwrap()); + println!("------2--HASH--{} {:?}", numm, all_hashed_images[hash]); found_broken_thing = true; } hashmap_hashes.insert((*hash).clone()); - for i in all_hashed_images.get(hash).unwrap() { + for i in &all_hashed_images[hash] { let name = i.path.to_string_lossy().to_string(); if hashmap_names.contains(&name) { println!("------2--NAME--{numm} {name:?}"); @@ -1103,9 +1075,9 @@ mod tests { use std::collections::HashMap; use std::path::PathBuf; - use crate::common_dir_traversal::ToolType; use bk_tree::BKTree; + use crate::common_dir_traversal::ToolType; use crate::common_directory::Directories; use crate::common_tool::CommonToolData; use crate::similar_images::{Hamming, ImHash, ImageType, ImagesEntry, SimilarImages}; @@ -1201,34 +1173,6 @@ mod tests { } } - // TODO this not works yet, - // Need to find a way to - // #[test] - // fn test_similar_similarity() { - // for _ in 0..100 { - // let mut similar_images = SimilarImages { - // similarity: 10, - // common_data: CommonToolData { - // tool_type: ToolType::SimilarImages, - // ..Default::default() - // }, - // use_reference_folders: false, - // ..Default::default() - // }; - // - // let fe1 = create_random_file_entry(vec![1, 1, 1, 1, 1, 1, 1, 0b0000_0001], "abc.txt"); - // let fe2 = create_random_file_entry(vec![1, 1, 1, 1, 1, 1, 1, 0b0000_0010], "bcd.txt"); - // let fe3 = create_random_file_entry(vec![1, 1, 1, 1, 1, 1, 1, 0b0000_0100], "rrd.txt"); - // let fe4 = create_random_file_entry(vec![1, 1, 1, 1, 1, 1, 1, 0b0111_1111], "rdd.txt"); - // - // add_hashes(&mut similar_images.image_hashes, vec![fe1, fe2, fe3, fe4]); - // - // similar_images.find_similar_hashes(None, None); - // assert_eq!(similar_images.get_similar_images().len(), 1); - // assert_eq!(similar_images.get_similar_images()[0].len(), 4); - // } - // } - #[test] fn test_simple_referenced_same_group() { for _ in 0..100 { @@ -1473,6 +1417,35 @@ mod tests { } } + #[test] + fn test_reference_same() { + for _ in 0..100 { + let mut similar_images = SimilarImages { + similarity: 1, + common_data: CommonToolData { + tool_type: ToolType::SimilarImages, + directories: Directories { + reference_directories: vec![PathBuf::from("/home/rr/")], + ..Default::default() + }, + use_reference_folders: true, + ..Default::default() + }, + ..Default::default() + }; + + let fe1 = create_random_file_entry(vec![1, 1, 1, 1, 1, 1, 1, 1], "/home/rr/abc.txt"); + let fe2 = create_random_file_entry(vec![1, 1, 1, 1, 1, 1, 1, 1], "/home/kk/bcd.txt"); + + add_hashes(&mut similar_images.image_hashes, vec![fe1, fe2]); + + similar_images.find_similar_hashes(None, None); + let res = similar_images.get_similar_images_referenced(); + assert_eq!(res.len(), 1); + assert_eq!(res[0].1.len(), 1); + } + } + #[test] fn test_reference_union() { for _ in 0..100 { @@ -1544,7 +1517,8 @@ mod tests { ImagesEntry { path: PathBuf::from(name.to_string()), size: 0, - dimensions: String::new(), + width: 100, + height: 100, modified_date: 0, hash, similarity: 0, diff --git a/czkawka_core/src/similar_videos.rs b/czkawka_core/src/similar_videos.rs index 60619a7..4fd3a73 100644 --- a/czkawka_core/src/similar_videos.rs +++ b/czkawka_core/src/similar_videos.rs @@ -20,7 +20,6 @@ use crate::common_dir_traversal::{inode, take_1_per_inode, CheckingMethod, DirTr use crate::common_tool::{CommonData, CommonToolData, DeleteMethod}; use crate::common_traits::{DebugPrint, PrintResults, ResultEntry}; use crate::flc; -use crate::localizer_core::generate_translation_hashmap; pub const MAX_TOLERANCE: i32 = 20; @@ -123,12 +122,12 @@ impl SimilarVideos { #[cfg(target_os = "windows")] self.common_data.text_messages.errors.push(flc!("core_ffmpeg_not_found_windows")); #[cfg(target_os = "linux")] - self.common_data.text_messages.errors.push(flc!( - "core_ffmpeg_missing_in_snap", - generate_translation_hashmap(vec![("url", "https://github.com/snapcrafters/ffmpeg/issues/73".to_string())]) - )); + self.common_data + .text_messages + .errors + .push(flc!("core_ffmpeg_missing_in_snap", url = "https://github.com/snapcrafters/ffmpeg/issues/73")); } else { - self.optimize_dirs_before_start(); + self.prepare_items(); self.common_data.use_reference_folders = !self.common_data.directories.reference_directories.is_empty(); if !self.check_for_similar_videos(stop_receiver, progress_sender) { self.common_data.stopped_search = true; @@ -145,8 +144,8 @@ impl SimilarVideos { // #[fun_time(message = "check_for_similar_videos", level = "debug")] fn check_for_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { - self.common_data.allowed_extensions.set_and_validate_extensions(VIDEO_FILES_EXTENSIONS); - if !self.common_data.allowed_extensions.set_any_extensions() { + self.common_data.extensions.set_and_validate_allowed_extensions(VIDEO_FILES_EXTENSIONS); + if !self.common_data.extensions.set_any_extensions() { return true; } diff --git a/czkawka_core/src/temporary.rs b/czkawka_core/src/temporary.rs index 4042aa8..c0f16fd 100644 --- a/czkawka_core/src/temporary.rs +++ b/czkawka_core/src/temporary.rs @@ -1,7 +1,6 @@ use std::fs; use std::fs::DirEntry; use std::io::prelude::*; - use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -60,7 +59,7 @@ impl Temporary { #[fun_time(message = "find_temporary_files", level = "info")] pub fn find_temporary_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { - self.optimize_dirs_before_start(); + self.prepare_items(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; return; diff --git a/czkawka_gui/Cargo.toml b/czkawka_gui/Cargo.toml index 4455e03..6ca2932 100644 --- a/czkawka_gui/Cargo.toml +++ b/czkawka_gui/Cargo.toml @@ -3,19 +3,19 @@ name = "czkawka_gui" version = "6.1.0" authors = ["RafaÅ‚ Mikrut "] edition = "2021" -rust-version = "1.72.1" +rust-version = "1.74.0" description = "GTK frontend of Czkawka" license = "MIT" homepage = "https://github.com/qarmin/czkawka" repository = "https://github.com/qarmin/czkawka" [dependencies] -gdk4 = "0.7" -glib = "0.18" -gtk4 = { version = "0.7", default-features = false, features = ["v4_6"] } +gdk4 = "0.8" +glib = "0.19" +gtk4 = { version = "0.8", default-features = false, features = ["v4_6"] } humansize = "2.1" -chrono = "0.4.31" +chrono = "0.4.34" # Used for sending stop signal across threads crossbeam-channel = "0.5" @@ -36,14 +36,14 @@ regex = "1.10" image_hasher = "1.2" # Move files to trash -trash = "3.2" +trash = "3.3" # For moving files(why std::fs doesn't have such features?) fs_extra = "1.3" # Language i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] } -i18n-embed-fl = "0.7" +i18n-embed-fl = "0.8" rust-embed = { version = "8.2", features = ["debug-embed"] } once_cell = "1.19" diff --git a/czkawka_gui/LICENSE b/czkawka_gui/LICENSE index fe356f6..c3996b3 100644 --- a/czkawka_gui/LICENSE +++ b/czkawka_gui/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020-2023 RafaÅ‚ Mikrut +Copyright (c) 2020-2024 RafaÅ‚ Mikrut Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/czkawka_gui/README.md b/czkawka_gui/README.md index 612e911..66f6062 100644 --- a/czkawka_gui/README.md +++ b/czkawka_gui/README.md @@ -36,7 +36,7 @@ Compiling the gui is harder than compiling cli or core, because it uses gtk4 whi ### Requirements | Program | Minimal version | |:---------:|:-----------------:| -| Rust | 1.72.1 | +| Rust | 1.74.0 | | GTK | 4.6 | ### Linux (Ubuntu, but on other OS should work similar) diff --git a/czkawka_gui/i18n/en/czkawka_gui.ftl b/czkawka_gui/i18n/en/czkawka_gui.ftl index 8215c4b..6d6ec95 100644 --- a/czkawka_gui/i18n/en/czkawka_gui.ftl +++ b/czkawka_gui/i18n/en/czkawka_gui.ftl @@ -192,12 +192,18 @@ upper_allowed_extensions_tooltip = Usage example ".exe, IMAGE, VIDEO, .rar, 7z" - this means that images (e.g. jpg, png), videos (e.g. avi, mp4), exe, rar, and 7z files will be scanned. +upper_excluded_extensions_tooltip = + List of disabled files which will be ignored in scan. + + When using both allowed and disabled extensions, this one has higher priority, so file will not be checked. + upper_excluded_items_tooltip = Excluded items must contain * wildcard and should be separated by commas. This is slower than Excluded Directories, so use it carefully. upper_excluded_items = Excluded Items: upper_allowed_extensions = Allowed Extensions: +upper_excluded_extensions = Disabled Extensions: # Popovers diff --git a/czkawka_gui/icons/czk_hide_down.svg b/czkawka_gui/icons/czk_hide_down.svg index 7b2ced9..75df6a0 100644 --- a/czkawka_gui/icons/czk_hide_down.svg +++ b/czkawka_gui/icons/czk_hide_down.svg @@ -1,15 +1,15 @@ + enable-background="new 0 0 512 512" + viewBox="0 0 512 512" + version="1.1" + id="svg16" + sodipodi:docname="czk_hide_down.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" +> + enable-background="new 0 0 512 512" + viewBox="0 0 512 512" + version="1.1" + id="svg16" + sodipodi:docname="czk_hide_up.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" +> - + 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(), - ); + entry_info.set_text(flg!("compute_found_duplicates_name", number_files = duplicates_number, number_groups = duplicates_group).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)) - ]) + number_files = duplicates_number, + number_groups = duplicates_group, + size = format_size(duplicates_size, BINARY) ) .as_str(), ); diff --git a/czkawka_gui/src/connect_things/connect_button_compare.rs b/czkawka_gui/src/connect_things/connect_button_compare.rs index 07fd227..679d74f 100644 --- a/czkawka_gui/src/connect_things/connect_button_compare.rs +++ b/czkawka_gui/src/connect_things/connect_button_compare.rs @@ -15,7 +15,6 @@ use crate::gui_structs::gui_data::GuiData; use crate::help_functions::{ count_number_of_groups, get_all_direct_children, get_full_name_from_path_name, get_max_file_name, get_pixbuf_from_dynamic_image, resize_pixbuf_dimension, }; -use crate::localizer_core::generate_translation_hashmap; use crate::notebook_info::{NotebookObject, NOTEBOOKS_INFO}; const BIG_PREVIEW_SIZE: i32 = 600; @@ -303,11 +302,9 @@ fn populate_groups_at_start( label_group_info.set_text( flg!( "compare_groups_number", - generate_translation_hashmap(vec![ - ("current_group", current_group.to_string()), - ("all_groups", group_number.to_string()), - ("images_in_group", cache_all_images.len().to_string()) - ]) + current_group = current_group, + all_groups = group_number, + images_in_group = cache_all_images.len() ) .as_str(), ); diff --git a/czkawka_gui/src/connect_things/connect_button_delete.rs b/czkawka_gui/src/connect_things/connect_button_delete.rs index 9b60e99..22ac25a 100644 --- a/czkawka_gui/src/connect_things/connect_button_delete.rs +++ b/czkawka_gui/src/connect_things/connect_button_delete.rs @@ -8,7 +8,6 @@ use gtk4::{Align, CheckButton, Dialog, Orientation, ResponseType, TextView}; use crate::flg; use crate::gui_structs::gui_data::GuiData; use crate::help_functions::*; -use crate::localizer_core::generate_translation_hashmap; use crate::notebook_enums::*; use crate::notebook_info::NOTEBOOKS_INFO; @@ -148,13 +147,11 @@ fn create_dialog_ask_for_deletion(window_main: >k4::Window, number_of_selected let label: gtk4::Label = gtk4::Label::new(Some(&flg!("delete_question_label"))); let label2: gtk4::Label = match number_of_selected_groups { - 0 => gtk4::Label::new(Some(&flg!( - "delete_items_label", - generate_translation_hashmap(vec![("items", number_of_selected_items.to_string())]) - ))), + 0 => gtk4::Label::new(Some(&flg!("delete_items_label", items = number_of_selected_items))), _ => gtk4::Label::new(Some(&flg!( "delete_items_groups_label", - generate_translation_hashmap(vec![("items", number_of_selected_items.to_string()), ("groups", number_of_selected_groups.to_string())]) + items = number_of_selected_items, + groups = number_of_selected_groups ))), }; @@ -368,10 +365,7 @@ pub fn empty_folder_remover( } } if error_happened { - messages += &flg!( - "delete_folder_failed", - generate_translation_hashmap(vec![("dir", get_full_name_from_path_name(&path, &name))]) - ); + messages += &flg!("delete_folder_failed", dir = get_full_name_from_path_name(&path, &name)); messages += "\n"; } } @@ -425,11 +419,7 @@ pub fn basic_remove( } Err(e) => { - messages += flg!( - "delete_file_failed", - generate_translation_hashmap(vec![("name", get_full_name_from_path_name(&path, &name)), ("reason", e.to_string())]) - ) - .as_str(); + messages += flg!("delete_file_failed", name = get_full_name_from_path_name(&path, &name), reason = e.to_string()).as_str(); messages += "\n"; } } @@ -439,11 +429,7 @@ pub fn basic_remove( model.remove(&iter); } Err(e) => { - messages += flg!( - "delete_file_failed", - generate_translation_hashmap(vec![("name", get_full_name_from_path_name(&path, &name)), ("reason", e.to_string())]) - ) - .as_str(); + messages += flg!("delete_file_failed", name = get_full_name_from_path_name(&path, &name), reason = e.to_string()).as_str(); messages += "\n"; } } @@ -513,19 +499,11 @@ pub fn tree_remove( for file_name in vec_file_name { if !use_trash { if let Err(e) = fs::remove_file(get_full_name_from_path_name(&path, &file_name)) { - messages += flg!( - "delete_file_failed", - generate_translation_hashmap(vec![("name", get_full_name_from_path_name(&path, &file_name)), ("reason", e.to_string())]) - ) - .as_str(); + messages += flg!("delete_file_failed", name = get_full_name_from_path_name(&path, &file_name), reason = e.to_string()).as_str(); messages += "\n"; } } else if let Err(e) = trash::delete(get_full_name_from_path_name(&path, &file_name)) { - messages += flg!( - "delete_file_failed", - generate_translation_hashmap(vec![("name", get_full_name_from_path_name(&path, &file_name)), ("reason", e.to_string())]) - ) - .as_str(); + messages += flg!("delete_file_failed", name = get_full_name_from_path_name(&path, &file_name), reason = e.to_string()).as_str(); messages += "\n"; } diff --git a/czkawka_gui/src/connect_things/connect_button_hardlink.rs b/czkawka_gui/src/connect_things/connect_button_hardlink.rs index 1f570b7..4adaf9e 100644 --- a/czkawka_gui/src/connect_things/connect_button_hardlink.rs +++ b/czkawka_gui/src/connect_things/connect_button_hardlink.rs @@ -9,7 +9,6 @@ use czkawka_core::duplicate::make_hard_link; use crate::flg; use crate::gui_structs::gui_data::GuiData; use crate::help_functions::*; -use crate::localizer_core::generate_translation_hashmap; use crate::notebook_enums::*; use crate::notebook_info::NOTEBOOKS_INFO; @@ -211,42 +210,21 @@ fn hardlink_symlink( for symhardlink_data in vec_symhardlink_data { for file_to_symlink in symhardlink_data.files_to_symhardlink { if let Err(e) = fs::remove_file(&file_to_symlink) { - add_text_to_text_view( - text_view_errors, - flg!( - "delete_file_failed", - generate_translation_hashmap(vec![("name", file_to_symlink.to_string()), ("reason", e.to_string())]) - ) - .as_str(), - ); + add_text_to_text_view(text_view_errors, flg!("delete_file_failed", name = file_to_symlink, reason = e.to_string()).as_str()); continue; }; #[cfg(target_family = "unix")] { if let Err(e) = std::os::unix::fs::symlink(&symhardlink_data.original_data, &file_to_symlink) { - add_text_to_text_view( - text_view_errors, - flg!( - "delete_file_failed", - generate_translation_hashmap(vec![("name", file_to_symlink.to_string()), ("reason", e.to_string())]) - ) - .as_str(), - ); + add_text_to_text_view(text_view_errors, flg!("delete_file_failed", name = file_to_symlink, reason = e.to_string()).as_str()); continue; }; } #[cfg(target_family = "windows")] { if let Err(e) = std::os::windows::fs::symlink_file(&symhardlink_data.original_data, &file_to_symlink) { - add_text_to_text_view( - text_view_errors, - flg!( - "delete_file_failed", - generate_translation_hashmap(vec![("name", file_to_symlink.to_string()), ("reason", e.to_string())]) - ) - .as_str(), - ); + add_text_to_text_view(text_view_errors, flg!("delete_file_failed", name = file_to_symlink, reason = e.to_string()).as_str()); continue; }; } diff --git a/czkawka_gui/src/connect_things/connect_button_move.rs b/czkawka_gui/src/connect_things/connect_button_move.rs index 2c1d500..3346638 100644 --- a/czkawka_gui/src/connect_things/connect_button_move.rs +++ b/czkawka_gui/src/connect_things/connect_button_move.rs @@ -7,7 +7,6 @@ use gtk4::{ResponseType, TreePath}; use crate::flg; use crate::gui_structs::gui_data::GuiData; use crate::help_functions::*; -use crate::localizer_core::generate_translation_hashmap; use crate::notebook_enums::*; use crate::notebook_info::NOTEBOOKS_INFO; @@ -54,14 +53,7 @@ pub fn connect_button_move(gui_data: &GuiData) { } if folders.len() != 1 { - add_text_to_text_view( - &text_view_errors, - flg!( - "move_files_choose_more_than_1_path", - generate_translation_hashmap(vec![("path_number", folders.len().to_string())]) - ) - .as_str(), - ); + add_text_to_text_view(&text_view_errors, flg!("move_files_choose_more_than_1_path", path_number = folders.len()).as_str()); } else { let folder = folders[0].clone(); if let Some(column_header) = nb_object.column_header { @@ -201,13 +193,13 @@ fn move_files_common( let destination_file = destination_folder.join(file_name); if Path::new(&thing).is_dir() { if let Err(e) = fs_extra::dir::move_dir(&thing, &destination_file, &CopyOptions::new()) { - messages += flg!("move_folder_failed", generate_translation_hashmap(vec![("name", thing), ("reason", e.to_string())])).as_str(); + messages += flg!("move_folder_failed", name = thing, reason = e.to_string()).as_str(); messages += "\n"; continue 'next_result; } } else { if let Err(e) = fs_extra::file::move_file(&thing, &destination_file, &fs_extra::file::CopyOptions::new()) { - messages += flg!("move_file_failed", generate_translation_hashmap(vec![("name", thing), ("reason", e.to_string())])).as_str(); + messages += flg!("move_file_failed", name = thing, reason = e.to_string()).as_str(); messages += "\n"; continue 'next_result; @@ -217,13 +209,7 @@ fn move_files_common( moved_files += 1; } - entry_info.set_text( - flg!( - "move_stats", - generate_translation_hashmap(vec![("num_files", moved_files.to_string()), ("all_files", selected_rows.len().to_string())]) - ) - .as_str(), - ); + entry_info.set_text(flg!("move_stats", num_files = moved_files, all_files = selected_rows.len()).as_str()); text_view_errors.buffer().set_text(messages.as_str()); } diff --git a/czkawka_gui/src/connect_things/connect_button_save.rs b/czkawka_gui/src/connect_things/connect_button_save.rs index c06e5c4..75b1f7e 100644 --- a/czkawka_gui/src/connect_things/connect_button_save.rs +++ b/czkawka_gui/src/connect_things/connect_button_save.rs @@ -11,7 +11,6 @@ use czkawka_core::common_traits::PrintResults; use crate::flg; use crate::gui_structs::gui_data::GuiData; use crate::help_functions::BottomButtonsEnum; -use crate::localizer_core::generate_translation_hashmap; use crate::notebook_enums::*; pub fn connect_button_save(gui_data: &GuiData) { @@ -76,7 +75,7 @@ fn post_save_things( buttons_save: &Button, current_path: String, ) { - entry_info.set_text(&flg!("save_results_to_file", generate_translation_hashmap(vec![("name", current_path),]))); + entry_info.set_text(&flg!("save_results_to_file", name = current_path)); // Set state { buttons_save.hide(); diff --git a/czkawka_gui/src/connect_things/connect_button_search.rs b/czkawka_gui/src/connect_things/connect_button_search.rs index 72c4784..d648fb7 100644 --- a/czkawka_gui/src/connect_things/connect_button_search.rs +++ b/czkawka_gui/src/connect_things/connect_button_search.rs @@ -5,7 +5,6 @@ use std::thread; use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use glib::Sender as glibSender; use gtk4::prelude::*; use gtk4::Grid; @@ -35,10 +34,10 @@ use crate::taskbar_progress::tbp_flags::TBPF_NOPROGRESS; use crate::{flg, DEFAULT_MAXIMAL_FILE_SIZE, DEFAULT_MINIMAL_CACHE_SIZE, DEFAULT_MINIMAL_FILE_SIZE}; #[allow(clippy::too_many_arguments)] -pub fn connect_button_search(gui_data: &GuiData, glib_stop_sender: glibSender, progress_sender: Sender) { +pub fn connect_button_search(gui_data: &GuiData, result_sender: Sender, progress_sender: Sender) { let buttons_array = gui_data.bottom_buttons.buttons_array.clone(); let buttons_search_clone = gui_data.bottom_buttons.buttons_search.clone(); - let grid_progress_stages = gui_data.progress_window.grid_progress_stages.clone(); + let grid_progress = gui_data.progress_window.grid_progress.clone(); let label_stage = gui_data.progress_window.label_stage.clone(); let notebook_main = gui_data.main_notebook.notebook_main.clone(); let notebook_upper = gui_data.upper_notebook.notebook_upper.clone(); @@ -55,7 +54,7 @@ pub fn connect_button_search(gui_data: &GuiData, glib_stop_sender: glibSender duplicate_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - ), - NotebookMainEnum::EmptyFiles => empty_files_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - ), - NotebookMainEnum::EmptyDirectories => empty_directories_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - ), - NotebookMainEnum::BigFiles => big_files_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - ), - NotebookMainEnum::Temporary => temporary_files_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - ), - NotebookMainEnum::SimilarImages => similar_image_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - ), - NotebookMainEnum::SimilarVideos => similar_video_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - ), - NotebookMainEnum::SameMusic => same_music_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - &show_dialog, - ), - NotebookMainEnum::Symlinks => bad_symlinks_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - ), - NotebookMainEnum::BrokenFiles => broken_files_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - &show_dialog, - ), - NotebookMainEnum::BadExtensions => bad_extensions_search( - &gui_data, - loaded_common_items, - stop_receiver, - glib_stop_sender, - &grid_progress_stages, - progress_sender.clone(), - ), + NotebookMainEnum::Duplicate => duplicate_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender), + NotebookMainEnum::EmptyFiles => empty_files_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender), + NotebookMainEnum::EmptyDirectories => empty_dirs_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender), + NotebookMainEnum::BigFiles => big_files_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender), + NotebookMainEnum::Temporary => temporary_files_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender), + NotebookMainEnum::SimilarImages => similar_image_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender), + NotebookMainEnum::SimilarVideos => similar_video_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender), + NotebookMainEnum::SameMusic => same_music_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender, &show_dialog), + NotebookMainEnum::Symlinks => bad_symlinks_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender), + NotebookMainEnum::BrokenFiles => broken_files_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender, &show_dialog), + NotebookMainEnum::BadExtensions => bad_extensions_search(&gui_data, loaded_commons, stop_receiver, result_sender, &grid_progress, progress_sender), } window_progress.set_default_size(1, 1); @@ -201,6 +123,7 @@ struct LoadedCommonItems { recursive_search: bool, excluded_items: Vec, allowed_extensions: String, + excluded_extensions: String, hide_hard_links: bool, use_cache: bool, save_also_as_json: bool, @@ -217,6 +140,7 @@ impl LoadedCommonItems { let check_button_settings_hide_hard_links = gui_data.settings.check_button_settings_hide_hard_links.clone(); let check_button_settings_use_cache = gui_data.settings.check_button_settings_use_cache.clone(); let entry_allowed_extensions = gui_data.upper_notebook.entry_allowed_extensions.clone(); + let entry_excluded_extensions = gui_data.upper_notebook.entry_excluded_extensions.clone(); let entry_excluded_items = gui_data.upper_notebook.entry_excluded_items.clone(); let entry_general_maximal_size = gui_data.upper_notebook.entry_general_maximal_size.clone(); let entry_general_minimal_size = gui_data.upper_notebook.entry_general_minimal_size.clone(); @@ -241,6 +165,7 @@ impl LoadedCommonItems { .map(std::string::ToString::to_string) .collect::>(); let allowed_extensions = entry_allowed_extensions.text().as_str().to_string(); + let excluded_extensions = entry_excluded_extensions.text().as_str().to_string(); let hide_hard_links = check_button_settings_hide_hard_links.is_active(); let use_cache = check_button_settings_use_cache.is_active(); let save_also_as_json = check_button_settings_save_also_json.is_active(); @@ -269,6 +194,7 @@ impl LoadedCommonItems { recursive_search, excluded_items, allowed_extensions, + excluded_extensions, hide_hard_links, use_cache, save_also_as_json, @@ -282,13 +208,13 @@ impl LoadedCommonItems { fn duplicate_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, ) { - grid_progress_stages.show(); + grid_progress.show(); let combo_box_duplicate_check_method = gui_data.main_notebook.combo_box_duplicate_check_method.clone(); let combo_box_duplicate_hash_type = gui_data.main_notebook.combo_box_duplicate_hash_type.clone(); @@ -319,41 +245,32 @@ fn duplicate_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut df = DuplicateFinder::new(); - df.set_included_directory(loaded_common_items.included_directories); - df.set_excluded_directory(loaded_common_items.excluded_directories); - df.set_reference_directory(loaded_common_items.reference_directories); - df.set_recursive_search(loaded_common_items.recursive_search); - df.set_excluded_items(loaded_common_items.excluded_items); - df.set_allowed_extensions(loaded_common_items.allowed_extensions); - df.set_minimal_file_size(loaded_common_items.minimal_file_size); - df.set_maximal_file_size(loaded_common_items.maximal_file_size); - df.set_minimal_cache_file_size(loaded_common_items.minimal_cache_file_size); - df.set_minimal_prehash_cache_file_size(minimal_prehash_cache_file_size); - df.set_check_method(check_method); - df.set_hash_type(hash_type); - df.set_save_also_as_json(loaded_common_items.save_also_as_json); - df.set_ignore_hard_links(loaded_common_items.hide_hard_links); - df.set_use_cache(loaded_common_items.use_cache); - df.set_use_prehash_cache(use_prehash_cache); - df.set_delete_outdated_cache(delete_outdated_cache); - df.set_case_sensitive_name_comparison(case_sensitive_name_comparison); - df.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - df.find_duplicates(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::Duplicates(df)).unwrap(); + let mut item = DuplicateFinder::new(); + + set_common_settings(&mut item, &loaded_commons); + item.set_minimal_cache_file_size(loaded_commons.minimal_cache_file_size); + item.set_minimal_prehash_cache_file_size(minimal_prehash_cache_file_size); + item.set_check_method(check_method); + item.set_hash_type(hash_type); + item.set_ignore_hard_links(loaded_commons.hide_hard_links); + item.set_use_prehash_cache(use_prehash_cache); + item.set_delete_outdated_cache(delete_outdated_cache); + item.set_case_sensitive_name_comparison(case_sensitive_name_comparison); + item.find_duplicates(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::Duplicates(item)).unwrap(); }) .unwrap(); } fn empty_files_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, ) { - grid_progress_stages.hide(); + grid_progress.hide(); let tree_view_empty_files_finder = gui_data.main_notebook.tree_view_empty_files_finder.clone(); clean_tree_view(&tree_view_empty_files_finder); @@ -361,29 +278,24 @@ fn empty_files_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut vf = EmptyFiles::new(); + let mut item = EmptyFiles::new(); - vf.set_included_directory(loaded_common_items.included_directories); - vf.set_excluded_directory(loaded_common_items.excluded_directories); - vf.set_recursive_search(loaded_common_items.recursive_search); - vf.set_excluded_items(loaded_common_items.excluded_items); - vf.set_allowed_extensions(loaded_common_items.allowed_extensions); - vf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - vf.find_empty_files(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::EmptyFiles(vf)).unwrap(); + set_common_settings(&mut item, &loaded_commons); + item.find_empty_files(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::EmptyFiles(item)).unwrap(); }) .unwrap(); } -fn empty_directories_search( +fn empty_dirs_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, ) { - grid_progress_stages.hide(); + grid_progress.hide(); let tree_view_empty_folder_finder = gui_data.main_notebook.tree_view_empty_folder_finder.clone(); clean_tree_view(&tree_view_empty_folder_finder); @@ -391,26 +303,24 @@ fn empty_directories_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut ef = EmptyFolder::new(); - ef.set_included_directory(loaded_common_items.included_directories); - ef.set_excluded_directory(loaded_common_items.excluded_directories); - ef.set_excluded_items(loaded_common_items.excluded_items); - ef.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - ef.find_empty_folders(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::EmptyFolders(ef)).unwrap(); + let mut item = EmptyFolder::new(); + + set_common_settings(&mut item, &loaded_commons); + item.find_empty_folders(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::EmptyFolders(item)).unwrap(); }) .unwrap(); } fn big_files_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, ) { - grid_progress_stages.hide(); + grid_progress.hide(); let combo_box_big_files_mode = gui_data.main_notebook.combo_box_big_files_mode.clone(); let entry_big_files_number = gui_data.main_notebook.entry_big_files_number.clone(); @@ -425,31 +335,26 @@ fn big_files_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut bf = BigFile::new(); + let mut item = BigFile::new(); - bf.set_included_directory(loaded_common_items.included_directories); - bf.set_excluded_directory(loaded_common_items.excluded_directories); - bf.set_recursive_search(loaded_common_items.recursive_search); - bf.set_excluded_items(loaded_common_items.excluded_items); - bf.set_allowed_extensions(loaded_common_items.allowed_extensions); - bf.set_number_of_files_to_check(numbers_of_files_to_check); - bf.set_search_mode(big_files_mode); - bf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - bf.find_big_files(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::BigFiles(bf)).unwrap(); + set_common_settings(&mut item, &loaded_commons); + item.set_number_of_files_to_check(numbers_of_files_to_check); + item.set_search_mode(big_files_mode); + item.find_big_files(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::BigFiles(item)).unwrap(); }) .unwrap(); } fn temporary_files_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, ) { - grid_progress_stages.hide(); + grid_progress.hide(); let tree_view_temporary_files_finder = gui_data.main_notebook.tree_view_temporary_files_finder.clone(); clean_tree_view(&tree_view_temporary_files_finder); @@ -457,29 +362,25 @@ fn temporary_files_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut tf = Temporary::new(); + let mut item = Temporary::new(); - tf.set_included_directory(loaded_common_items.included_directories); - tf.set_excluded_directory(loaded_common_items.excluded_directories); - tf.set_recursive_search(loaded_common_items.recursive_search); - tf.set_excluded_items(loaded_common_items.excluded_items); - tf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - tf.find_temporary_files(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::Temporary(tf)).unwrap(); + set_common_settings(&mut item, &loaded_commons); + item.find_temporary_files(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::Temporary(item)).unwrap(); }) .unwrap(); } fn same_music_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, show_dialog: &Arc, ) { - grid_progress_stages.show(); + grid_progress.show(); let check_button_music_artist: gtk4::CheckButton = gui_data.main_notebook.check_button_music_artist.clone(); let check_button_music_title: gtk4::CheckButton = gui_data.main_notebook.check_button_music_title.clone(); @@ -528,26 +429,16 @@ fn same_music_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut mf = SameMusic::new(); + let mut item = SameMusic::new(); - mf.set_included_directory(loaded_common_items.included_directories); - mf.set_excluded_directory(loaded_common_items.excluded_directories); - mf.set_reference_directory(loaded_common_items.reference_directories); - mf.set_excluded_items(loaded_common_items.excluded_items); - mf.set_use_cache(loaded_common_items.use_cache); - mf.set_minimal_file_size(loaded_common_items.minimal_file_size); - mf.set_maximal_file_size(loaded_common_items.maximal_file_size); - mf.set_allowed_extensions(loaded_common_items.allowed_extensions); - mf.set_recursive_search(loaded_common_items.recursive_search); - mf.set_music_similarity(music_similarity); - mf.set_maximum_difference(maximum_difference); - mf.set_minimum_segment_duration(minimum_segment_duration); - mf.set_check_type(check_method); - mf.set_approximate_comparison(approximate_comparison); - mf.set_save_also_as_json(loaded_common_items.save_also_as_json); - mf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - mf.find_same_music(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::SameMusic(mf)).unwrap(); + set_common_settings(&mut item, &loaded_commons); + item.set_music_similarity(music_similarity); + item.set_maximum_difference(maximum_difference); + item.set_minimum_segment_duration(minimum_segment_duration); + item.set_check_type(check_method); + item.set_approximate_comparison(approximate_comparison); + item.find_same_music(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::SameMusic(item)).unwrap(); }) .unwrap(); } else { @@ -577,14 +468,14 @@ fn same_music_search( fn broken_files_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, show_dialog: &Arc, ) { - grid_progress_stages.show(); + grid_progress.show(); let check_button_broken_files_archive: gtk4::CheckButton = gui_data.main_notebook.check_button_broken_files_archive.clone(); let check_button_broken_files_pdf: gtk4::CheckButton = gui_data.main_notebook.check_button_broken_files_pdf.clone(); @@ -613,19 +504,12 @@ fn broken_files_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut br = BrokenFiles::new(); + let mut item = BrokenFiles::new(); - br.set_included_directory(loaded_common_items.included_directories); - br.set_excluded_directory(loaded_common_items.excluded_directories); - br.set_recursive_search(loaded_common_items.recursive_search); - br.set_excluded_items(loaded_common_items.excluded_items); - br.set_use_cache(loaded_common_items.use_cache); - br.set_allowed_extensions(loaded_common_items.allowed_extensions); - br.set_save_also_as_json(loaded_common_items.save_also_as_json); - br.set_checked_types(checked_types); - br.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - br.find_broken_files(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::BrokenFiles(br)).unwrap(); + set_common_settings(&mut item, &loaded_commons); + item.set_checked_types(checked_types); + item.find_broken_files(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::BrokenFiles(item)).unwrap(); }) .unwrap(); } else { @@ -655,13 +539,13 @@ fn broken_files_search( fn similar_image_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, ) { - grid_progress_stages.show(); + grid_progress.show(); let combo_box_image_hash_size = gui_data.main_notebook.combo_box_image_hash_size.clone(); let combo_box_image_hash_algorithm = gui_data.main_notebook.combo_box_image_hash_algorithm.clone(); @@ -693,40 +577,30 @@ fn similar_image_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut sf = SimilarImages::new(); + let mut item = SimilarImages::new(); - sf.set_included_directory(loaded_common_items.included_directories); - sf.set_excluded_directory(loaded_common_items.excluded_directories); - sf.set_reference_directory(loaded_common_items.reference_directories); - sf.set_recursive_search(loaded_common_items.recursive_search); - sf.set_excluded_items(loaded_common_items.excluded_items); - sf.set_minimal_file_size(loaded_common_items.minimal_file_size); - sf.set_maximal_file_size(loaded_common_items.maximal_file_size); - sf.set_similarity(similarity); - sf.set_use_cache(loaded_common_items.use_cache); - sf.set_hash_alg(hash_alg); - sf.set_hash_size(hash_size); - sf.set_image_filter(image_filter); - sf.set_allowed_extensions(loaded_common_items.allowed_extensions); - sf.set_delete_outdated_cache(delete_outdated_cache); - sf.set_exclude_images_with_same_size(ignore_same_size); - sf.set_save_also_as_json(loaded_common_items.save_also_as_json); - sf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - sf.find_similar_images(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::SimilarImages(sf)).unwrap(); + set_common_settings(&mut item, &loaded_commons); + item.set_similarity(similarity); + item.set_hash_alg(hash_alg); + item.set_hash_size(hash_size); + item.set_image_filter(image_filter); + item.set_delete_outdated_cache(delete_outdated_cache); + item.set_exclude_images_with_same_size(ignore_same_size); + item.find_similar_images(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::SimilarImages(item)).unwrap(); }) .unwrap(); } fn similar_video_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, ) { - grid_progress_stages.show(); + grid_progress.show(); let check_button_video_ignore_same_size = gui_data.main_notebook.check_button_video_ignore_same_size.clone(); let check_button_settings_similar_videos_delete_outdated_cache = gui_data.settings.check_button_settings_similar_videos_delete_outdated_cache.clone(); @@ -743,37 +617,27 @@ fn similar_video_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut sf = SimilarVideos::new(); + let mut item = SimilarVideos::new(); - sf.set_included_directory(loaded_common_items.included_directories); - sf.set_excluded_directory(loaded_common_items.excluded_directories); - sf.set_reference_directory(loaded_common_items.reference_directories); - sf.set_recursive_search(loaded_common_items.recursive_search); - sf.set_excluded_items(loaded_common_items.excluded_items); - sf.set_minimal_file_size(loaded_common_items.minimal_file_size); - sf.set_maximal_file_size(loaded_common_items.maximal_file_size); - sf.set_allowed_extensions(loaded_common_items.allowed_extensions); - sf.set_use_cache(loaded_common_items.use_cache); - sf.set_tolerance(tolerance); - sf.set_delete_outdated_cache(delete_outdated_cache); - sf.set_exclude_videos_with_same_size(ignore_same_size); - sf.set_save_also_as_json(loaded_common_items.save_also_as_json); - sf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - sf.find_similar_videos(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::SimilarVideos(sf)).unwrap(); + set_common_settings(&mut item, &loaded_commons); + item.set_tolerance(tolerance); + item.set_delete_outdated_cache(delete_outdated_cache); + item.set_exclude_videos_with_same_size(ignore_same_size); + item.find_similar_videos(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::SimilarVideos(item)).unwrap(); }) .unwrap(); } fn bad_symlinks_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, ) { - grid_progress_stages.hide(); + grid_progress.hide(); let tree_view_invalid_symlinks = gui_data.main_notebook.tree_view_invalid_symlinks.clone(); clean_tree_view(&tree_view_invalid_symlinks); @@ -781,29 +645,24 @@ fn bad_symlinks_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut isf = InvalidSymlinks::new(); + let mut item = InvalidSymlinks::new(); - isf.set_included_directory(loaded_common_items.included_directories); - isf.set_excluded_directory(loaded_common_items.excluded_directories); - isf.set_recursive_search(loaded_common_items.recursive_search); - isf.set_excluded_items(loaded_common_items.excluded_items); - isf.set_allowed_extensions(loaded_common_items.allowed_extensions); - isf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - isf.find_invalid_links(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::InvalidSymlinks(isf)).unwrap(); + set_common_settings(&mut item, &loaded_commons); + item.find_invalid_links(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::InvalidSymlinks(item)).unwrap(); }) .unwrap(); } fn bad_extensions_search( gui_data: &GuiData, - loaded_common_items: LoadedCommonItems, + loaded_commons: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: glibSender, - grid_progress_stages: &Grid, + result_sender: Sender, + grid_progress: &Grid, progress_data_sender: Sender, ) { - grid_progress_stages.show(); + grid_progress.show(); let tree_view_bad_extensions = gui_data.main_notebook.tree_view_bad_extensions.clone(); clean_tree_view(&tree_view_bad_extensions); @@ -811,22 +670,33 @@ fn bad_extensions_search( thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) .spawn(move || { - let mut be = BadExtensions::new(); + let mut item = BadExtensions::new(); - be.set_included_directory(loaded_common_items.included_directories); - be.set_excluded_directory(loaded_common_items.excluded_directories); - be.set_excluded_items(loaded_common_items.excluded_items); - be.set_minimal_file_size(loaded_common_items.minimal_file_size); - be.set_maximal_file_size(loaded_common_items.maximal_file_size); - be.set_allowed_extensions(loaded_common_items.allowed_extensions); - be.set_recursive_search(loaded_common_items.recursive_search); - be.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - be.find_bad_extensions_files(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::BadExtensions(be)).unwrap(); + set_common_settings(&mut item, &loaded_commons); + item.find_bad_extensions_files(Some(&stop_receiver), Some(&progress_data_sender)); + result_sender.send(Message::BadExtensions(item)).unwrap(); }) .unwrap(); } +fn set_common_settings(component: &mut T, loaded_commons: &LoadedCommonItems) +where + T: CommonData, +{ + component.set_included_directory(loaded_commons.included_directories.clone()); + component.set_excluded_directory(loaded_commons.excluded_directories.clone()); + component.set_reference_directory(loaded_commons.reference_directories.clone()); + component.set_recursive_search(loaded_commons.recursive_search); + component.set_allowed_extensions(loaded_commons.allowed_extensions.clone()); + component.set_excluded_extensions(loaded_commons.excluded_extensions.clone()); + component.set_excluded_items(loaded_commons.excluded_items.clone()); + component.set_exclude_other_filesystems(loaded_commons.ignore_other_filesystems); + component.set_use_cache(loaded_commons.use_cache); + component.set_save_also_as_json(loaded_commons.save_also_as_json); + component.set_minimal_file_size(loaded_commons.minimal_file_size); + component.set_maximal_file_size(loaded_commons.maximal_file_size); +} + #[fun_time(message = "clean_tree_view", level = "debug")] fn clean_tree_view(tree_view: >k4::TreeView) { let list_store = get_list_store(tree_view); diff --git a/czkawka_gui/src/connect_things/connect_popovers_select.rs b/czkawka_gui/src/connect_things/connect_popovers_select.rs index ea89863..9aa8533 100644 --- a/czkawka_gui/src/connect_things/connect_popovers_select.rs +++ b/czkawka_gui/src/connect_things/connect_popovers_select.rs @@ -1,9 +1,10 @@ -use czkawka_core::common::regex_check; -use czkawka_core::common_items::new_excluded_item; use gtk4::prelude::*; use gtk4::{ResponseType, TreeIter, Window}; use regex::Regex; +use czkawka_core::common::regex_check; +use czkawka_core::common_items::new_excluded_item; + use crate::flg; use crate::gui_structs::gui_data::GuiData; use crate::help_functions::*; diff --git a/czkawka_gui/src/connect_things/connect_progress_window.rs b/czkawka_gui/src/connect_things/connect_progress_window.rs index 2e26df1..09c5e08 100644 --- a/czkawka_gui/src/connect_things/connect_progress_window.rs +++ b/czkawka_gui/src/connect_things/connect_progress_window.rs @@ -1,10 +1,9 @@ -use crossbeam_channel::Receiver; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; - use std::time::Duration; +use crossbeam_channel::Receiver; use glib::MainContext; use gtk4::prelude::*; use gtk4::ProgressBar; @@ -164,10 +163,7 @@ fn progress_collect_items(gui_data: &GuiData, item: &ProgressData, files: bool) if files { label_stage.set_text(&flg!("progress_scanning_general_file", file_number_tm(item))); } else { - label_stage.set_text(&flg!( - "progress_scanning_empty_folders", - generate_translation_hashmap(vec![("folder_number", item.entries_checked.to_string())]) - )); + label_stage.set_text(&flg!("progress_scanning_empty_folders", folder_number = item.entries_checked)); } } diff --git a/czkawka_gui/src/connect_things/connect_selection_of_directories.rs b/czkawka_gui/src/connect_things/connect_selection_of_directories.rs index de27957..93074fd 100644 --- a/czkawka_gui/src/connect_things/connect_selection_of_directories.rs +++ b/czkawka_gui/src/connect_things/connect_selection_of_directories.rs @@ -1,7 +1,7 @@ -use gdk4::{DragAction, FileList}; use std::collections::HashSet; use std::path::PathBuf; +use gdk4::{DragAction, FileList}; use gtk4::prelude::*; use gtk4::{DropTarget, FileChooserNative, Notebook, Orientation, ResponseType, TreeView, Window}; diff --git a/czkawka_gui/src/connect_things/connect_settings.rs b/czkawka_gui/src/connect_things/connect_settings.rs index ee98c94..4426610 100644 --- a/czkawka_gui/src/connect_things/connect_settings.rs +++ b/czkawka_gui/src/connect_things/connect_settings.rs @@ -1,16 +1,16 @@ use std::collections::BTreeMap; use std::default::Default; -use czkawka_core::common_cache::{ - get_duplicate_cache_file, get_similar_images_cache_file, get_similar_videos_cache_file, load_cache_from_file_generalized_by_path, load_cache_from_file_generalized_by_size, - save_cache_to_file_generalized, -}; use directories_next::ProjectDirs; use gtk4::prelude::*; use gtk4::{Label, ResponseType, Window}; use image::imageops::FilterType; use image_hasher::HashAlg; +use czkawka_core::common_cache::{ + get_duplicate_cache_file, get_similar_images_cache_file, get_similar_videos_cache_file, load_cache_from_file_generalized_by_path, load_cache_from_file_generalized_by_size, + save_cache_to_file_generalized, +}; use czkawka_core::common_messages::Messages; use czkawka_core::duplicate::HashType; diff --git a/czkawka_gui/src/gui_structs/gui_about.rs b/czkawka_gui/src/gui_structs/gui_about.rs index f356533..bd3b6f2 100644 --- a/czkawka_gui/src/gui_structs/gui_about.rs +++ b/czkawka_gui/src/gui_structs/gui_about.rs @@ -108,7 +108,7 @@ impl GuiAbout { } pub fn update_language(&self) { - let mut comment_text: String = "2020 - 2023 RafaÅ‚ Mikrut(qarmin)\n\n".to_string(); + let mut comment_text: String = "2020 - 2024 RafaÅ‚ Mikrut(qarmin)\n\n".to_string(); comment_text += &flg!("about_window_motto"); self.about_dialog.set_comments(Some(&comment_text)); diff --git a/czkawka_gui/src/gui_structs/gui_progress_dialog.rs b/czkawka_gui/src/gui_structs/gui_progress_dialog.rs index a424286..ed27c04 100644 --- a/czkawka_gui/src/gui_structs/gui_progress_dialog.rs +++ b/czkawka_gui/src/gui_structs/gui_progress_dialog.rs @@ -15,7 +15,7 @@ pub struct GuiProgressDialog { pub label_progress_current_stage: gtk4::Label, pub label_progress_all_stages: gtk4::Label, - pub grid_progress_stages: gtk4::Grid, + pub grid_progress: gtk4::Grid, pub button_stop_in_dialog: gtk4::Button, pub evk_button_stop_in_dialog: EventControllerKey, @@ -38,7 +38,7 @@ impl GuiProgressDialog { let label_progress_current_stage: gtk4::Label = builder.object("label_progress_current_stage").unwrap(); let label_progress_all_stages: gtk4::Label = builder.object("label_progress_all_stages").unwrap(); - let grid_progress_stages: gtk4::Grid = builder.object("grid_progress_stages").unwrap(); + let grid_progress: gtk4::Grid = builder.object("grid_progress").unwrap(); let button_stop_in_dialog: gtk4::Button = builder.object("button_stop_in_dialog").unwrap(); let evk_button_stop_in_dialog = EventControllerKey::new(); @@ -53,7 +53,7 @@ impl GuiProgressDialog { label_stage, label_progress_current_stage, label_progress_all_stages, - grid_progress_stages, + grid_progress, button_stop_in_dialog, evk_button_stop_in_dialog, } diff --git a/czkawka_gui/src/gui_structs/gui_upper_notebook.rs b/czkawka_gui/src/gui_structs/gui_upper_notebook.rs index 0a503f6..35b2a91 100644 --- a/czkawka_gui/src/gui_structs/gui_upper_notebook.rs +++ b/czkawka_gui/src/gui_structs/gui_upper_notebook.rs @@ -23,6 +23,7 @@ pub struct GuiUpperNotebook { pub entry_excluded_items: gtk4::Entry, pub entry_allowed_extensions: gtk4::Entry, + pub entry_excluded_extensions: gtk4::Entry, pub check_button_recursive: gtk4::CheckButton, @@ -35,6 +36,7 @@ pub struct GuiUpperNotebook { pub label_excluded_items: gtk4::Label, pub label_allowed_extensions: gtk4::Label, + pub label_excluded_extensions: gtk4::Label, pub entry_general_minimal_size: gtk4::Entry, pub entry_general_maximal_size: gtk4::Entry, @@ -64,6 +66,7 @@ impl GuiUpperNotebook { tree_view_excluded_directories.add_controller(gc_tree_view_excluded_directories.clone()); let entry_allowed_extensions: gtk4::Entry = builder.object("entry_allowed_extensions").unwrap(); + let entry_excluded_extensions: gtk4::Entry = builder.object("entry_excluded_extensions").unwrap(); let entry_excluded_items: gtk4::Entry = builder.object("entry_excluded_items").unwrap(); let check_button_recursive: gtk4::CheckButton = builder.object("check_button_recursive").unwrap(); @@ -77,6 +80,7 @@ impl GuiUpperNotebook { let label_excluded_items: gtk4::Label = builder.object("label_excluded_items").unwrap(); let label_allowed_extensions: gtk4::Label = builder.object("label_allowed_extensions").unwrap(); + let label_excluded_extensions: gtk4::Label = builder.object("label_excluded_extensions").unwrap(); let entry_general_minimal_size: gtk4::Entry = builder.object("entry_general_minimal_size").unwrap(); let entry_general_maximal_size: gtk4::Entry = builder.object("entry_general_maximal_size").unwrap(); @@ -103,6 +107,7 @@ impl GuiUpperNotebook { gc_tree_view_excluded_directories, entry_excluded_items, entry_allowed_extensions, + entry_excluded_extensions, check_button_recursive, buttons_manual_add_included_directory, buttons_add_included_directory, @@ -112,6 +117,7 @@ impl GuiUpperNotebook { buttons_remove_excluded_directory, label_excluded_items, label_allowed_extensions, + label_excluded_extensions, entry_general_minimal_size, entry_general_maximal_size, label_general_size_bytes, @@ -141,11 +147,14 @@ impl GuiUpperNotebook { self.label_allowed_extensions.set_tooltip_text(Some(&flg!("upper_allowed_extensions_tooltip"))); self.entry_allowed_extensions.set_tooltip_text(Some(&flg!("upper_allowed_extensions_tooltip"))); + self.label_excluded_extensions.set_tooltip_text(Some(&flg!("upper_excluded_extensions_tooltip"))); + self.entry_excluded_extensions.set_tooltip_text(Some(&flg!("upper_excluded_extensions_tooltip"))); self.label_excluded_items.set_tooltip_text(Some(&flg!("upper_excluded_items_tooltip"))); self.entry_excluded_items.set_tooltip_text(Some(&flg!("upper_excluded_items_tooltip"))); self.label_excluded_items.set_label(&flg!("upper_excluded_items")); self.label_allowed_extensions.set_label(&flg!("upper_allowed_extensions")); + self.label_excluded_extensions.set_label(&flg!("upper_excluded_extensions")); self.label_general_size_bytes.set_label(&flg!("main_label_size_bytes")); self.label_general_min_size.set_label(&flg!("main_label_min_size")); diff --git a/czkawka_gui/src/help_functions.rs b/czkawka_gui/src/help_functions.rs index 649b6f7..0c778be 100644 --- a/czkawka_gui/src/help_functions.rs +++ b/czkawka_gui/src/help_functions.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use std::collections::HashMap; use std::io::BufReader; -use std::path::PathBuf; +use std::path::{PathBuf, MAIN_SEPARATOR}; use gdk4::gdk_pixbuf::{InterpType, Pixbuf}; use glib::Error; @@ -14,7 +14,6 @@ use once_cell::sync::OnceCell; use czkawka_core::bad_extensions::BadExtensions; use czkawka_core::big_file::BigFile; use czkawka_core::broken_files::BrokenFiles; -use czkawka_core::common::CHARACTER; use czkawka_core::common_dir_traversal; use czkawka_core::common_messages::Messages; use czkawka_core::duplicate::DuplicateFinder; @@ -414,7 +413,7 @@ pub fn get_notebook_object_from_tree_view(tree_view: &TreeView) -> &NotebookObje pub fn get_full_name_from_path_name(path: &str, name: &str) -> String { let mut string = String::with_capacity(path.len() + name.len() + 1); string.push_str(path); - string.push(CHARACTER); + string.push(MAIN_SEPARATOR); string.push_str(name); string } diff --git a/czkawka_gui/src/initialize_gui.rs b/czkawka_gui/src/initialize_gui.rs index 79a3dd2..200f80c 100644 --- a/czkawka_gui/src/initialize_gui.rs +++ b/czkawka_gui/src/initialize_gui.rs @@ -510,14 +510,7 @@ fn show_preview( let image = match get_dynamic_image_from_heic(file_name) { Ok(t) => t, Err(e) => { - add_text_to_text_view( - text_view_errors, - flg!( - "preview_image_opening_failure", - generate_translation_hashmap(vec![("name", file_name.to_string()), ("reason", e.to_string())]) - ) - .as_str(), - ); + add_text_to_text_view(text_view_errors, flg!("preview_image_opening_failure", name = file_name, reason = e.to_string()).as_str()); break 'dir; } }; @@ -526,14 +519,7 @@ fn show_preview( match get_pixbuf_from_dynamic_image(&image) { Ok(t) => t, Err(e) => { - add_text_to_text_view( - text_view_errors, - flg!( - "preview_image_opening_failure", - generate_translation_hashmap(vec![("name", file_name.to_string()), ("reason", e.to_string())]) - ) - .as_str(), - ); + add_text_to_text_view(text_view_errors, flg!("preview_image_opening_failure", name = file_name, reason = e.to_string()).as_str()); break 'dir; } } @@ -559,10 +545,7 @@ fn show_preview( pixbuf = match resize_pixbuf_dimension(&pixbuf, (800, 800), InterpType::Bilinear) { None => { - add_text_to_text_view( - text_view_errors, - flg!("preview_image_resize_failure", generate_translation_hashmap(vec![("name", file_name.to_string())])).as_str(), - ); + add_text_to_text_view(text_view_errors, flg!("preview_image_resize_failure", name = file_name).as_str()); break 'dir; } Some(pixbuf) => pixbuf, diff --git a/czkawka_gui/src/main.rs b/czkawka_gui/src/main.rs index 0601866..5275b91 100644 --- a/czkawka_gui/src/main.rs +++ b/czkawka_gui/src/main.rs @@ -5,11 +5,10 @@ #![allow(clippy::type_complexity)] #![allow(clippy::needless_late_init)] -use crossbeam_channel::{unbounded, Receiver, Sender}; use std::env; use std::ffi::OsString; -use glib::Priority; +use crossbeam_channel::{unbounded, Receiver, Sender}; use gtk4::gio::ApplicationFlags; use gtk4::prelude::*; use gtk4::Application; @@ -82,10 +81,7 @@ fn main() { fn build_ui(application: &Application, arguments: &[OsString]) { let gui_data: GuiData = GuiData::new_with_application(application); - // Used for getting data from thread - // TODO - deprecation happened without any example, so not sure how new code should look like - #[allow(deprecated)] - let (glib_stop_sender, glib_stop_receiver) = glib::MainContext::channel(Priority::default()); + let (result_sender, result_receiver) = unbounded(); // Futures progress report let (progress_sender, progress_receiver): (Sender, Receiver) = unbounded(); @@ -111,7 +107,7 @@ fn build_ui(application: &Application, arguments: &[OsString]) { connect_button_delete(&gui_data); connect_button_save(&gui_data); - connect_button_search(&gui_data, glib_stop_sender, progress_sender); + connect_button_search(&gui_data, result_sender, progress_sender); connect_button_select(&gui_data); connect_button_sort(&gui_data); connect_button_stop(&gui_data); @@ -124,7 +120,7 @@ fn build_ui(application: &Application, arguments: &[OsString]) { connect_selection_of_directories(&gui_data); connect_popover_select(&gui_data); connect_popover_sort(&gui_data); - connect_compute_results(&gui_data, glib_stop_receiver); + connect_compute_results(&gui_data, result_receiver); connect_progress_window(&gui_data, progress_receiver); connect_show_hide_ui(&gui_data); connect_settings(&gui_data); diff --git a/czkawka_gui/src/saving_loading.rs b/czkawka_gui/src/saving_loading.rs index ffc05c8..1a68723 100644 --- a/czkawka_gui/src/saving_loading.rs +++ b/czkawka_gui/src/saving_loading.rs @@ -9,7 +9,7 @@ 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::get_all_available_threads; use czkawka_core::common_dir_traversal::CheckingMethod; use czkawka_core::common_items::DEFAULT_EXCLUDED_ITEMS; use czkawka_core::similar_images::SIMILAR_VALUES; @@ -21,7 +21,6 @@ use crate::gui_structs::gui_upper_notebook::GuiUpperNotebook; use crate::help_combo_box::DUPLICATES_CHECK_METHOD_COMBO_BOX; use crate::help_functions::*; use crate::language_functions::{get_language_from_combo_box_text, LANGUAGES_ALL}; -use crate::localizer_core::generate_translation_hashmap; const SAVE_FILE_NAME: &str = "czkawka_gui_config_4.txt"; @@ -82,7 +81,7 @@ impl LoadSaveStruct { pub fn get_vector_string(&self, key: &str, default_value: Vec) -> Vec { if self.loaded_items.contains_key(key) { let mut new_vector = Vec::new(); - for i in self.loaded_items.get(key).unwrap() { + for i in &self.loaded_items[key] { if !i.trim().is_empty() { new_vector.push(i.trim().to_string()); } @@ -105,19 +104,13 @@ impl LoadSaveStruct { } pub fn get_string(&self, key: String, default_value: String) -> String { if self.loaded_items.contains_key(&key) { - let item = self.loaded_items.get(&key).unwrap().clone().into_iter().filter(|e| !e.is_empty()).collect::>(); + let item = &self.loaded_items[&key].clone().into_iter().filter(|e| !e.is_empty()).collect::>(); return if item.len() == 1 { item[0].clone() } else if item.is_empty() { String::new() } else { - add_text_to_text_view( - &self.text_view, - &flg!( - "saving_loading_invalid_string", - generate_translation_hashmap(vec![("key", key), ("result", format!("{item:?}"))]) - ), - ); + add_text_to_text_view(&self.text_view, &flg!("saving_loading_invalid_string", key = key, result = format!("{item:?}"))); default_value }; } @@ -126,7 +119,7 @@ impl LoadSaveStruct { } pub fn get_object(&self, key: String, default_value: T) -> T { if self.loaded_items.contains_key(&key) { - let item = self.loaded_items.get(&key).unwrap().clone().into_iter().filter(|e| !e.is_empty()).collect::>(); + let item = &self.loaded_items[&key].clone().into_iter().filter(|e| !e.is_empty()).collect::>(); return if item.len() == 1 { if let Ok(t) = item[0].parse::() { @@ -136,13 +129,7 @@ impl LoadSaveStruct { default_value } } else { - add_text_to_text_view( - &self.text_view, - &flg!( - "saving_loading_invalid_int", - generate_translation_hashmap(vec![("key", key), ("result", format!("{item:?}"))]) - ), - ); + add_text_to_text_view(&self.text_view, &flg!("saving_loading_invalid_int", key = key, result = format!("{item:?}"))); default_value }; } @@ -151,7 +138,7 @@ impl LoadSaveStruct { } pub fn get_bool(&self, key: String, default_value: bool) -> bool { if self.loaded_items.contains_key(&key) { - let item = self.loaded_items.get(&key).unwrap().clone().into_iter().filter(|e| !e.is_empty()).collect::>(); + let item = &self.loaded_items[&key].clone().into_iter().filter(|e| !e.is_empty()).collect::>(); return if item.len() == 1 { let text = item[0].trim().to_lowercase(); if text == "false" || text == "0" { @@ -159,23 +146,11 @@ impl LoadSaveStruct { } else if text == "true" || text == "1" { true } else { - add_text_to_text_view( - &self.text_view, - &flg!( - "saving_loading_decode_problem_bool", - generate_translation_hashmap(vec![("key", key), ("result", item[0].to_string())]) - ), - ); + add_text_to_text_view(&self.text_view, &flg!("saving_loading_decode_problem_bool", key = key, result = item[0].to_string())); default_value } } else { - add_text_to_text_view( - &self.text_view, - &flg!( - "saving_loading_invalid_bool", - generate_translation_hashmap(vec![("key", key), ("result", format!("{item:?}"))]) - ), - ); + add_text_to_text_view(&self.text_view, &flg!("saving_loading_invalid_bool", key = key, result = format!("{item:?}"))); default_value }; } @@ -186,10 +161,7 @@ impl LoadSaveStruct { // Bool, int, string pub fn save_var(&mut self, key: String, value: &T) { if self.loaded_items.contains_key(&key) { - add_text_to_text_view( - &self.text_view, - &flg!("saving_loading_saving_same_keys", generate_translation_hashmap(vec![("key", key.clone())])), - ); + add_text_to_text_view(&self.text_view, &flg!("saving_loading_saving_same_keys", key = key.clone())); } self.loaded_items.insert(key, vec![value.to_string()]); @@ -224,10 +196,7 @@ impl LoadSaveStruct { if !config_dir.is_dir() { add_text_to_text_view( text_view_errors, - &flg!( - "saving_loading_folder_config_instead_file", - generate_translation_hashmap(vec![("path", config_dir.to_string_lossy().to_string())]) - ), + &flg!("saving_loading_folder_config_instead_file", path = config_dir.to_string_lossy().to_string()), ); return None; } @@ -236,7 +205,8 @@ impl LoadSaveStruct { text_view_errors, &flg!( "saving_loading_failed_to_create_configuration_folder", - generate_translation_hashmap(vec![("path", config_dir.to_string_lossy().to_string()), ("reason", e.to_string())]) + path = config_dir.to_string_lossy().to_string(), + reason = e.to_string() ), ); return None; @@ -249,7 +219,8 @@ impl LoadSaveStruct { text_view_errors, &flg!( "saving_loading_failed_to_create_config_file", - generate_translation_hashmap(vec![("path", config_file.to_string_lossy().to_string()), ("reason", e.to_string())]) + path = config_file.to_string_lossy().to_string(), + reason = e.to_string() ), ); return None; @@ -262,10 +233,7 @@ impl LoadSaveStruct { // Don't show errors when there is no configuration file when starting app add_text_to_text_view( text_view_errors, - &flg!( - "saving_loading_failed_to_read_config_file", - generate_translation_hashmap(vec![("path", config_file.to_string_lossy().to_string())]) - ), + &flg!("saving_loading_failed_to_read_config_file", path = config_file.to_string_lossy().to_string()), ); } return None; @@ -278,7 +246,8 @@ impl LoadSaveStruct { text_view_errors, &flg!( "saving_loading_failed_to_create_config_file", - generate_translation_hashmap(vec![("path", config_file.to_string_lossy().to_string()), ("reason", e.to_string())]) + path = config_file.to_string_lossy().to_string(), + reason = e.to_string() ), ); return None; @@ -299,7 +268,8 @@ impl LoadSaveStruct { text_view_errors, &flg!( "saving_loading_failed_to_read_data_from_file", - generate_translation_hashmap(vec![("path", config_file.to_string_lossy().to_string()), ("reason", e.to_string())]) + path = config_file.to_string_lossy().to_string(), + reason = e.to_string() ), ); return; @@ -314,23 +284,14 @@ impl LoadSaveStruct { } else if !header.is_empty() { self.loaded_items.entry(header.clone()).or_default().push(line.to_string()); } else { - add_text_to_text_view( - text_view_errors, - &flg!( - "saving_loading_orphan_data", - generate_translation_hashmap(vec![("data", line.to_string()), ("index", index.to_string())]) - ), - ); + add_text_to_text_view(text_view_errors, &flg!("saving_loading_orphan_data", data = line, line = index.to_string())); } } let (_, hashmap_sl) = create_hash_map(); for setting in self.loaded_items.keys() { if !hashmap_sl.contains_key(setting) { - add_text_to_text_view( - text_view_errors, - &flg!("saving_loading_not_valid", generate_translation_hashmap(vec![("data", setting.to_string())])), - ); + add_text_to_text_view(text_view_errors, &flg!("saving_loading_not_valid", data = setting.clone())); } } @@ -368,20 +329,12 @@ impl LoadSaveStruct { if data_saved { add_text_to_text_view( text_view_errors, - flg!( - "saving_loading_saving_success", - generate_translation_hashmap(vec![("name", config_file.to_string_lossy().to_string())]) - ) - .as_str(), + flg!("saving_loading_saving_success", name = config_file.to_string_lossy().to_string()).as_str(), ); } else { add_text_to_text_view( text_view_errors, - flg!( - "saving_loading_saving_failure", - generate_translation_hashmap(vec![("name", config_file.to_string_lossy().to_string())]) - ) - .as_str(), + flg!("saving_loading_saving_failure", name = config_file.to_string_lossy().to_string()).as_str(), ); } } @@ -509,183 +462,138 @@ pub fn save_configuration(manual_execution: bool, upper_notebook: &GuiUpperNoteb // Upper notebook saving_struct.save_list_store( - hashmap_ls.get(&LoadText::IncludedDirectories).unwrap().to_string(), + hashmap_ls[&LoadText::IncludedDirectories].clone(), &upper_notebook.tree_view_included_directories.clone(), ColumnsIncludedDirectory::Path as i32, ); saving_struct.save_list_store( - hashmap_ls.get(&LoadText::ExcludedDirectories).unwrap().to_string(), + hashmap_ls[&LoadText::ExcludedDirectories].clone(), &upper_notebook.tree_view_excluded_directories.clone(), ColumnsExcludedDirectory::Path as i32, ); - saving_struct.save_var(hashmap_ls.get(&LoadText::ExcludedItems).unwrap().to_string(), &upper_notebook.entry_excluded_items.text()); - saving_struct.save_var( - hashmap_ls.get(&LoadText::AllowedExtensions).unwrap().to_string(), - &upper_notebook.entry_allowed_extensions.text(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::MinimalFileSize).unwrap().to_string(), - &upper_notebook.entry_general_minimal_size.text(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::MaximalFileSize).unwrap().to_string(), - &upper_notebook.entry_general_maximal_size.text(), - ); + saving_struct.save_var(hashmap_ls[&LoadText::ExcludedItems].clone(), &upper_notebook.entry_excluded_items.text()); + saving_struct.save_var(hashmap_ls[&LoadText::AllowedExtensions].clone(), &upper_notebook.entry_allowed_extensions.text()); + saving_struct.save_var(hashmap_ls[&LoadText::MinimalFileSize].clone(), &upper_notebook.entry_general_minimal_size.text()); + saving_struct.save_var(hashmap_ls[&LoadText::MaximalFileSize].clone(), &upper_notebook.entry_general_maximal_size.text()); // Check buttons + saving_struct.save_var(hashmap_ls[&LoadText::SaveAtExit].clone(), &settings.check_button_settings_save_at_exit.is_active()); + saving_struct.save_var(hashmap_ls[&LoadText::LoadAtStart].clone(), &settings.check_button_settings_load_at_start.is_active()); saving_struct.save_var( - hashmap_ls.get(&LoadText::SaveAtExit).unwrap().to_string(), - &settings.check_button_settings_save_at_exit.is_active(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::LoadAtStart).unwrap().to_string(), - &settings.check_button_settings_load_at_start.is_active(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::ConfirmDeletionFiles).unwrap().to_string(), + hashmap_ls[&LoadText::ConfirmDeletionFiles].clone(), &settings.check_button_settings_confirm_deletion.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::ConfirmDeletionAllFilesInGroup).unwrap().to_string(), + hashmap_ls[&LoadText::ConfirmDeletionAllFilesInGroup].clone(), &settings.check_button_settings_confirm_group_deletion.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::ImagePreviewImage).unwrap().to_string(), + hashmap_ls[&LoadText::ImagePreviewImage].clone(), &settings.check_button_settings_show_preview_similar_images.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::DuplicatePreviewImage).unwrap().to_string(), + hashmap_ls[&LoadText::DuplicatePreviewImage].clone(), &settings.check_button_settings_show_preview_duplicates.is_active(), ); + saving_struct.save_var(hashmap_ls[&LoadText::HideHardLinks].clone(), &settings.check_button_settings_hide_hard_links.is_active()); + saving_struct.save_var(hashmap_ls[&LoadText::UseCache].clone(), &settings.check_button_settings_use_cache.is_active()); + saving_struct.save_var(hashmap_ls[&LoadText::UseJsonCacheFile].clone(), &settings.check_button_settings_save_also_json.is_active()); + saving_struct.save_var(hashmap_ls[&LoadText::DeleteToTrash].clone(), &settings.check_button_settings_use_trash.is_active()); saving_struct.save_var( - hashmap_ls.get(&LoadText::HideHardLinks).unwrap().to_string(), - &settings.check_button_settings_hide_hard_links.is_active(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::UseCache).unwrap().to_string(), - &settings.check_button_settings_use_cache.is_active(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::UseJsonCacheFile).unwrap().to_string(), - &settings.check_button_settings_save_also_json.is_active(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::DeleteToTrash).unwrap().to_string(), - &settings.check_button_settings_use_trash.is_active(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::ImageDeleteOutdatedCacheEntries).unwrap().to_string(), + hashmap_ls[&LoadText::ImageDeleteOutdatedCacheEntries].clone(), &settings.check_button_settings_similar_images_delete_outdated_cache.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::DuplicateDeleteOutdatedCacheEntries).unwrap().to_string(), + hashmap_ls[&LoadText::DuplicateDeleteOutdatedCacheEntries].clone(), &settings.check_button_settings_duplicates_delete_outdated_cache.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::VideoDeleteOutdatedCacheEntries).unwrap().to_string(), + hashmap_ls[&LoadText::VideoDeleteOutdatedCacheEntries].clone(), &settings.check_button_settings_similar_videos_delete_outdated_cache.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::UsePrehashCache).unwrap().to_string(), + hashmap_ls[&LoadText::UsePrehashCache].clone(), &settings.check_button_duplicates_use_prehash_cache.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::ShowBottomTextPanel).unwrap().to_string(), + hashmap_ls[&LoadText::ShowBottomTextPanel].clone(), &settings.check_button_settings_show_text_view.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::GeneralIgnoreOtherFilesystems).unwrap().to_string(), + hashmap_ls[&LoadText::GeneralIgnoreOtherFilesystems].clone(), &settings.check_button_settings_one_filesystem.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::BrokenFilesArchive).unwrap().to_string(), + hashmap_ls[&LoadText::BrokenFilesArchive].clone(), &main_notebook.check_button_broken_files_archive.is_active(), ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::BrokenFilesImage).unwrap().to_string(), - &main_notebook.check_button_broken_files_image.is_active(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::BrokenFilesAudio).unwrap().to_string(), - &main_notebook.check_button_broken_files_audio.is_active(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::BrokenFilesPdf).unwrap().to_string(), - &main_notebook.check_button_broken_files_pdf.is_active(), - ); + saving_struct.save_var(hashmap_ls[&LoadText::BrokenFilesImage].clone(), &main_notebook.check_button_broken_files_image.is_active()); + saving_struct.save_var(hashmap_ls[&LoadText::BrokenFilesAudio].clone(), &main_notebook.check_button_broken_files_audio.is_active()); + saving_struct.save_var(hashmap_ls[&LoadText::BrokenFilesPdf].clone(), &main_notebook.check_button_broken_files_pdf.is_active()); // Others + saving_struct.save_var(hashmap_ls[&LoadText::ThreadNumber].clone(), &settings.scale_settings_number_of_threads.value().round()); + saving_struct.save_var(hashmap_ls[&LoadText::MinimalCacheSize].clone(), &settings.entry_settings_cache_file_minimal_size.text()); saving_struct.save_var( - hashmap_ls.get(&LoadText::ThreadNumber).unwrap().to_string(), - &settings.scale_settings_number_of_threads.value().round(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::MinimalCacheSize).unwrap().to_string(), - &settings.entry_settings_cache_file_minimal_size.text(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::MinimalPrehashCacheSize).unwrap().to_string(), + hashmap_ls[&LoadText::MinimalPrehashCacheSize].clone(), &settings.entry_settings_prehash_cache_file_minimal_size.text(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::Language).unwrap().to_string(), + hashmap_ls[&LoadText::Language].clone(), &get_language_from_combo_box_text(&settings.combo_box_settings_language.active_text().unwrap()).short_text, ); // Comboboxes main notebook saving_struct.save_var( - hashmap_ls.get(&LoadText::ComboBoxDuplicateHashType).unwrap().to_string(), + hashmap_ls[&LoadText::ComboBoxDuplicateHashType].clone(), &main_notebook.combo_box_duplicate_hash_type.active().unwrap_or(0), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::ComboBoxDuplicateCheckMethod).unwrap().to_string(), + hashmap_ls[&LoadText::ComboBoxDuplicateCheckMethod].clone(), &main_notebook.combo_box_duplicate_check_method.active().unwrap_or(0), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::ComboBoxImageResizeAlgorithm).unwrap().to_string(), + hashmap_ls[&LoadText::ComboBoxImageResizeAlgorithm].clone(), &main_notebook.combo_box_image_resize_algorithm.active().unwrap_or(0), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::ComboBoxImageHashType).unwrap().to_string(), + hashmap_ls[&LoadText::ComboBoxImageHashType].clone(), &main_notebook.combo_box_image_hash_algorithm.active().unwrap_or(0), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::ComboBoxImageHashSize).unwrap().to_string(), + hashmap_ls[&LoadText::ComboBoxImageHashSize].clone(), &main_notebook.combo_box_image_hash_size.active().unwrap_or(0), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::ComboBoxBigFiles).unwrap().to_string(), + hashmap_ls[&LoadText::ComboBoxBigFiles].clone(), &main_notebook.combo_box_big_files_mode.active().unwrap_or(0), ); // Other2 saving_struct.save_var( - hashmap_ls.get(&LoadText::DuplicateNameCaseSensitive).unwrap().to_string(), + hashmap_ls[&LoadText::DuplicateNameCaseSensitive].clone(), &main_notebook.check_button_duplicate_case_sensitive_name.is_active(), ); + saving_struct.save_var(hashmap_ls[&LoadText::NumberOfBiggestFiles].clone(), &main_notebook.entry_big_files_number.text()); saving_struct.save_var( - hashmap_ls.get(&LoadText::NumberOfBiggestFiles).unwrap().to_string(), - &main_notebook.entry_big_files_number.text(), - ); - saving_struct.save_var( - hashmap_ls.get(&LoadText::SimilarImagesSimilarity).unwrap().to_string(), + hashmap_ls[&LoadText::SimilarImagesSimilarity].clone(), &main_notebook.scale_similarity_similar_images.value(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::SimilarImagesIgnoreSameSize).unwrap().to_string(), + hashmap_ls[&LoadText::SimilarImagesIgnoreSameSize].clone(), &main_notebook.check_button_image_ignore_same_size.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::SimilarVideosSimilarity).unwrap().to_string(), + hashmap_ls[&LoadText::SimilarVideosSimilarity].clone(), &main_notebook.scale_similarity_similar_videos.value(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::SimilarVideosIgnoreSameSize).unwrap().to_string(), + hashmap_ls[&LoadText::SimilarVideosIgnoreSameSize].clone(), &main_notebook.check_button_video_ignore_same_size.is_active(), ); saving_struct.save_var( - hashmap_ls.get(&LoadText::MusicApproximateComparison).unwrap().to_string(), + hashmap_ls[&LoadText::MusicApproximateComparison].clone(), &main_notebook.check_button_music_approximate_comparison.is_active(), ); @@ -719,83 +627,59 @@ pub fn load_configuration( // Loading data from hashmaps let (hashmap_ls, _hashmap_sl) = create_hash_map(); - let mut included_directories: Vec = loaded_entries.get_vector_string(hashmap_ls.get(&LoadText::IncludedDirectories).unwrap(), included_directories); - let mut excluded_directories: Vec = loaded_entries.get_vector_string(hashmap_ls.get(&LoadText::ExcludedDirectories).unwrap(), excluded_directories); - let excluded_items: String = loaded_entries.get_string( - hashmap_ls.get(&LoadText::ExcludedItems).unwrap().clone(), - upper_notebook.entry_excluded_items.text().to_string(), - ); - let allowed_extensions: String = loaded_entries.get_string(hashmap_ls.get(&LoadText::AllowedExtensions).unwrap().clone(), String::new()); - let minimal_file_size: String = loaded_entries.get_integer_string(hashmap_ls.get(&LoadText::MinimalFileSize).unwrap().clone(), DEFAULT_MINIMAL_FILE_SIZE.to_string()); - let maximal_file_size: String = loaded_entries.get_integer_string(hashmap_ls.get(&LoadText::MaximalFileSize).unwrap().clone(), DEFAULT_MAXIMAL_FILE_SIZE.to_string()); + let mut included_directories: Vec = loaded_entries.get_vector_string(&hashmap_ls[&LoadText::IncludedDirectories], included_directories); + let mut excluded_directories: Vec = loaded_entries.get_vector_string(&hashmap_ls[&LoadText::ExcludedDirectories], excluded_directories); + let excluded_items: String = loaded_entries.get_string(hashmap_ls[&LoadText::ExcludedItems].clone(), upper_notebook.entry_excluded_items.text().to_string()); + let allowed_extensions: String = loaded_entries.get_string(hashmap_ls[&LoadText::AllowedExtensions].clone(), String::new()); + let minimal_file_size: String = loaded_entries.get_integer_string(hashmap_ls[&LoadText::MinimalFileSize].clone(), DEFAULT_MINIMAL_FILE_SIZE.to_string()); + let maximal_file_size: String = loaded_entries.get_integer_string(hashmap_ls[&LoadText::MaximalFileSize].clone(), DEFAULT_MAXIMAL_FILE_SIZE.to_string()); - let loading_at_start: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::LoadAtStart).unwrap().clone(), DEFAULT_LOAD_AT_START); - let mut saving_at_exit: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::SaveAtExit).unwrap().clone(), DEFAULT_SAVE_ON_EXIT); - let confirm_deletion: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::ConfirmDeletionFiles).unwrap().clone(), DEFAULT_CONFIRM_DELETION); - let confirm_group_deletion: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::ConfirmDeletionAllFilesInGroup).unwrap().clone(), DEFAULT_CONFIRM_GROUP_DELETION); - let show_previews_similar_images: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::ImagePreviewImage).unwrap().clone(), DEFAULT_SHOW_IMAGE_PREVIEW); - let show_previews_duplicates: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::DuplicatePreviewImage).unwrap().clone(), DEFAULT_SHOW_DUPLICATE_IMAGE_PREVIEW); - let bottom_text_panel: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::ShowBottomTextPanel).unwrap().clone(), DEFAULT_BOTTOM_TEXT_VIEW); - let hide_hard_links: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::HideHardLinks).unwrap().clone(), DEFAULT_HIDE_HARD_LINKS); - let use_cache: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::UseCache).unwrap().clone(), DEFAULT_USE_CACHE); - let use_json_cache: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::UseJsonCacheFile).unwrap().clone(), DEFAULT_SAVE_ALSO_AS_JSON); - let use_trash: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::DeleteToTrash).unwrap().clone(), DEFAULT_USE_TRASH); - let ignore_other_fs: bool = loaded_entries.get_bool( - hashmap_ls.get(&LoadText::GeneralIgnoreOtherFilesystems).unwrap().clone(), - DEFAULT_GENERAL_IGNORE_OTHER_FILESYSTEMS, - ); + let loading_at_start: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::LoadAtStart].clone(), DEFAULT_LOAD_AT_START); + let mut saving_at_exit: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::SaveAtExit].clone(), DEFAULT_SAVE_ON_EXIT); + let confirm_deletion: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::ConfirmDeletionFiles].clone(), DEFAULT_CONFIRM_DELETION); + let confirm_group_deletion: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::ConfirmDeletionAllFilesInGroup].clone(), DEFAULT_CONFIRM_GROUP_DELETION); + let show_previews_similar_images: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::ImagePreviewImage].clone(), DEFAULT_SHOW_IMAGE_PREVIEW); + let show_previews_duplicates: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::DuplicatePreviewImage].clone(), DEFAULT_SHOW_DUPLICATE_IMAGE_PREVIEW); + let bottom_text_panel: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::ShowBottomTextPanel].clone(), DEFAULT_BOTTOM_TEXT_VIEW); + let hide_hard_links: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::HideHardLinks].clone(), DEFAULT_HIDE_HARD_LINKS); + let use_cache: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::UseCache].clone(), DEFAULT_USE_CACHE); + let use_json_cache: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::UseJsonCacheFile].clone(), DEFAULT_SAVE_ALSO_AS_JSON); + let use_trash: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::DeleteToTrash].clone(), DEFAULT_USE_TRASH); + let ignore_other_fs: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::GeneralIgnoreOtherFilesystems].clone(), DEFAULT_GENERAL_IGNORE_OTHER_FILESYSTEMS); let delete_outdated_cache_duplicates: bool = loaded_entries.get_bool( - hashmap_ls.get(&LoadText::DuplicateDeleteOutdatedCacheEntries).unwrap().clone(), + hashmap_ls[&LoadText::DuplicateDeleteOutdatedCacheEntries].clone(), DEFAULT_DUPLICATE_REMOVE_AUTO_OUTDATED_CACHE, ); - let delete_outdated_cache_similar_images: bool = loaded_entries.get_bool( - hashmap_ls.get(&LoadText::ImageDeleteOutdatedCacheEntries).unwrap().clone(), - DEFAULT_IMAGE_REMOVE_AUTO_OUTDATED_CACHE, - ); - let delete_outdated_cache_similar_videos: bool = loaded_entries.get_bool( - hashmap_ls.get(&LoadText::VideoDeleteOutdatedCacheEntries).unwrap().clone(), - DEFAULT_VIDEO_REMOVE_AUTO_OUTDATED_CACHE, - ); - let use_prehash_cache: bool = loaded_entries.get_bool(hashmap_ls.get(&LoadText::UsePrehashCache).unwrap().clone(), DEFAULT_USE_PRECACHE); + let delete_outdated_cache_similar_images: bool = + loaded_entries.get_bool(hashmap_ls[&LoadText::ImageDeleteOutdatedCacheEntries].clone(), DEFAULT_IMAGE_REMOVE_AUTO_OUTDATED_CACHE); + let delete_outdated_cache_similar_videos: bool = + loaded_entries.get_bool(hashmap_ls[&LoadText::VideoDeleteOutdatedCacheEntries].clone(), DEFAULT_VIDEO_REMOVE_AUTO_OUTDATED_CACHE); + let use_prehash_cache: bool = loaded_entries.get_bool(hashmap_ls[&LoadText::UsePrehashCache].clone(), DEFAULT_USE_PRECACHE); - let cache_prehash_minimal_size: String = loaded_entries.get_integer_string( - hashmap_ls.get(&LoadText::MinimalPrehashCacheSize).unwrap().clone(), - DEFAULT_PREHASH_MINIMAL_CACHE_SIZE.to_string(), - ); - let cache_minimal_size: String = loaded_entries.get_integer_string(hashmap_ls.get(&LoadText::MinimalCacheSize).unwrap().clone(), DEFAULT_MINIMAL_CACHE_SIZE.to_string()); - let short_language = loaded_entries.get_string(hashmap_ls.get(&LoadText::Language).unwrap().clone(), short_language); + let cache_prehash_minimal_size: String = + loaded_entries.get_integer_string(hashmap_ls[&LoadText::MinimalPrehashCacheSize].clone(), DEFAULT_PREHASH_MINIMAL_CACHE_SIZE.to_string()); + let cache_minimal_size: String = loaded_entries.get_integer_string(hashmap_ls[&LoadText::MinimalCacheSize].clone(), DEFAULT_MINIMAL_CACHE_SIZE.to_string()); + let short_language = loaded_entries.get_string(hashmap_ls[&LoadText::Language].clone(), short_language); - let combo_box_duplicate_hash_type = loaded_entries.get_object(hashmap_ls.get(&LoadText::ComboBoxDuplicateHashType).unwrap().clone(), 0); - let combo_box_duplicate_checking_method = loaded_entries.get_object(hashmap_ls.get(&LoadText::ComboBoxDuplicateCheckMethod).unwrap().clone(), 0); - let combo_box_image_hash_size = loaded_entries.get_object(hashmap_ls.get(&LoadText::ComboBoxImageHashSize).unwrap().clone(), 1); // 16 instead default 8 - let combo_box_image_hash_algorithm = loaded_entries.get_object(hashmap_ls.get(&LoadText::ComboBoxImageHashType).unwrap().clone(), 0); - let combo_box_image_resize_algorithm = loaded_entries.get_object(hashmap_ls.get(&LoadText::ComboBoxImageResizeAlgorithm).unwrap().clone(), 0); - let combo_box_big_files_mode = loaded_entries.get_object(hashmap_ls.get(&LoadText::ComboBoxBigFiles).unwrap().clone(), 0); + let combo_box_duplicate_hash_type = loaded_entries.get_object(hashmap_ls[&LoadText::ComboBoxDuplicateHashType].clone(), 0); + let combo_box_duplicate_checking_method = loaded_entries.get_object(hashmap_ls[&LoadText::ComboBoxDuplicateCheckMethod].clone(), 0); + let combo_box_image_hash_size = loaded_entries.get_object(hashmap_ls[&LoadText::ComboBoxImageHashSize].clone(), 1); // 16 instead default 8 + let combo_box_image_hash_algorithm = loaded_entries.get_object(hashmap_ls[&LoadText::ComboBoxImageHashType].clone(), 0); + let combo_box_image_resize_algorithm = loaded_entries.get_object(hashmap_ls[&LoadText::ComboBoxImageResizeAlgorithm].clone(), 0); + let combo_box_big_files_mode = loaded_entries.get_object(hashmap_ls[&LoadText::ComboBoxBigFiles].clone(), 0); - let number_of_biggest_files = loaded_entries.get_integer_string( - hashmap_ls.get(&LoadText::NumberOfBiggestFiles).unwrap().clone(), - DEFAULT_NUMBER_OF_BIGGEST_FILES.to_string(), - ); - let similar_images_similarity = loaded_entries.get_object(hashmap_ls.get(&LoadText::SimilarImagesSimilarity).unwrap().clone(), DEFAULT_SIMILAR_IMAGES_SIMILARITY); - let similar_images_ignore_same_size = loaded_entries.get_bool( - hashmap_ls.get(&LoadText::SimilarImagesIgnoreSameSize).unwrap().clone(), - DEFAULT_SIMILAR_IMAGES_IGNORE_SAME_SIZE, - ); - let similar_videos_similarity = loaded_entries.get_object(hashmap_ls.get(&LoadText::SimilarVideosSimilarity).unwrap().clone(), DEFAULT_SIMILAR_VIDEOS_SIMILARITY); - let similar_videos_ignore_same_size = loaded_entries.get_bool( - hashmap_ls.get(&LoadText::SimilarVideosIgnoreSameSize).unwrap().clone(), - DEFAULT_SIMILAR_VIDEOS_IGNORE_SAME_SIZE, - ); - let check_button_case_sensitive_name = loaded_entries.get_object( - hashmap_ls.get(&LoadText::DuplicateNameCaseSensitive).unwrap().clone(), - DEFAULT_DUPLICATE_CASE_SENSITIVE_NAME_CHECKING, - ); + let number_of_biggest_files = loaded_entries.get_integer_string(hashmap_ls[&LoadText::NumberOfBiggestFiles].clone(), DEFAULT_NUMBER_OF_BIGGEST_FILES.to_string()); + let similar_images_similarity = loaded_entries.get_object(hashmap_ls[&LoadText::SimilarImagesSimilarity].clone(), DEFAULT_SIMILAR_IMAGES_SIMILARITY); + let similar_images_ignore_same_size = loaded_entries.get_bool(hashmap_ls[&LoadText::SimilarImagesIgnoreSameSize].clone(), DEFAULT_SIMILAR_IMAGES_IGNORE_SAME_SIZE); + let similar_videos_similarity = loaded_entries.get_object(hashmap_ls[&LoadText::SimilarVideosSimilarity].clone(), DEFAULT_SIMILAR_VIDEOS_SIMILARITY); + let similar_videos_ignore_same_size = loaded_entries.get_bool(hashmap_ls[&LoadText::SimilarVideosIgnoreSameSize].clone(), DEFAULT_SIMILAR_VIDEOS_IGNORE_SAME_SIZE); + let check_button_case_sensitive_name = loaded_entries.get_object(hashmap_ls[&LoadText::DuplicateNameCaseSensitive].clone(), DEFAULT_DUPLICATE_CASE_SENSITIVE_NAME_CHECKING); - let check_button_broken_files_archive = loaded_entries.get_object(hashmap_ls.get(&LoadText::BrokenFilesArchive).unwrap().clone(), DEFAULT_BROKEN_FILES_ARCHIVE); - let check_button_broken_files_pdf = loaded_entries.get_object(hashmap_ls.get(&LoadText::BrokenFilesPdf).unwrap().clone(), DEFAULT_BROKEN_FILES_PDF); - let check_button_broken_files_image = loaded_entries.get_object(hashmap_ls.get(&LoadText::BrokenFilesImage).unwrap().clone(), DEFAULT_BROKEN_FILES_IMAGE); - let check_button_broken_files_audio = loaded_entries.get_object(hashmap_ls.get(&LoadText::BrokenFilesAudio).unwrap().clone(), DEFAULT_BROKEN_FILES_AUDIO); - let thread_number = loaded_entries.get_object(hashmap_ls.get(&LoadText::ThreadNumber).unwrap().clone(), DEFAULT_THREAD_NUMBER); + let check_button_broken_files_archive = loaded_entries.get_object(hashmap_ls[&LoadText::BrokenFilesArchive].clone(), DEFAULT_BROKEN_FILES_ARCHIVE); + let check_button_broken_files_pdf = loaded_entries.get_object(hashmap_ls[&LoadText::BrokenFilesPdf].clone(), DEFAULT_BROKEN_FILES_PDF); + let check_button_broken_files_image = loaded_entries.get_object(hashmap_ls[&LoadText::BrokenFilesImage].clone(), DEFAULT_BROKEN_FILES_IMAGE); + let check_button_broken_files_audio = loaded_entries.get_object(hashmap_ls[&LoadText::BrokenFilesAudio].clone(), DEFAULT_BROKEN_FILES_AUDIO); + let thread_number = loaded_entries.get_object(hashmap_ls[&LoadText::ThreadNumber].clone(), DEFAULT_THREAD_NUMBER); let mut set_start_folders = false; if !manual_execution { @@ -949,8 +833,8 @@ pub fn load_configuration( main_notebook.scale_similarity_similar_images.connect_change_value(scale_step_function); main_notebook.scale_similarity_similar_images.set_value(similar_images_similarity as f64); - settings.scale_settings_number_of_threads.set_range(0_f64, get_default_number_of_threads() as f64); - settings.scale_settings_number_of_threads.set_fill_level(get_default_number_of_threads() as f64); + settings.scale_settings_number_of_threads.set_range(0_f64, get_all_available_threads() as f64); + settings.scale_settings_number_of_threads.set_fill_level(get_all_available_threads() as f64); settings.scale_settings_number_of_threads.connect_change_value(scale_step_function); settings.scale_settings_number_of_threads.set_value(thread_number as f64); } else { diff --git a/czkawka_gui/ui/about_dialog.ui b/czkawka_gui/ui/about_dialog.ui index c61d5af..beba11e 100644 --- a/czkawka_gui/ui/about_dialog.ui +++ b/czkawka_gui/ui/about_dialog.ui @@ -4,7 +4,7 @@ - 2020 - 2023 RafaÅ‚ Mikrut(qarmin) + 2020 - 2024 RafaÅ‚ Mikrut(qarmin) This program is free to use and will always be. diff --git a/czkawka_gui/ui/czkawka.cmb b/czkawka_gui/ui/czkawka.cmb index 798d5d4..111df58 100755 --- a/czkawka_gui/ui/czkawka.cmb +++ b/czkawka_gui/ui/czkawka.cmb @@ -242,6 +242,8 @@ (5,237,"GtkScale","scale_similarity_same_music",117,None,None,None,9,None), (5,238,"GtkLabel","label_same_music_seconds",117,None,None,None,6,None), (5,239,"GtkLabel","label_same_music_similarity",117,None,None,None,8,None), + (5,240,"GtkLabel","label_excluded_extensions",46,None,None,None,2,None), + (5,241,"GtkEntry","entry_excluded_extensions",46,None,None,None,3,None), (6,1,"GtkPopover","popover_right_click",None,None,None,None,None,None), (6,2,"GtkBox",None,1,None,None,None,None,None), (6,3,"GtkButton","buttons_popover_right_click_open_file",2,None,None,None,None,None), @@ -265,7 +267,7 @@ (7,17,"GtkButton","buttons_popover_unselect_all",2,None,None,None,14,None), (8,15,"GtkDialog","window_progress",None,None,None,None,None,None), (8,16,"GtkBox",None,15,None,None,None,None,None), - (8,17,"GtkGrid","grid_progress_stages",16,None,None,None,None,None), + (8,17,"GtkGrid","grid_progress",16,None,None,None,None,None), (8,18,"GtkLabel","label_progress_all_stages",17,None,None,None,None,None), (8,19,"GtkProgressBar","progress_bar_all_stages",17,None,None,None,1,None), (8,20,"GtkLabel","label_progress_current_stage",17,None,None,None,2,None), @@ -340,7 +342,7 @@ (10,7,"GtkButton","buttons_popover_sort_selection",2,None,None,None,4,None) - (3,1,"GtkAboutDialog","comments","2020 - 2023 RafaÅ‚ Mikrut(qarmin) + (3,1,"GtkAboutDialog","comments","2020 - 2024 RafaÅ‚ Mikrut(qarmin) This program is free to use and will always be. ",1,None,None,None,None,None,None,None,None), @@ -797,6 +799,9 @@ (5,239,"GtkLabel","label","Max difference",None,None,None,None,None,None,None,None,None), (5,239,"GtkWidget","margin-end","5",None,None,None,None,None,None,None,None,None), (5,239,"GtkWidget","margin-start","5",None,None,None,None,None,None,None,None,None), + (5,240,"GtkLabel","label","Disabled Extensions","yes",None,None,None,None,None,None,None,None), + (5,241,"GtkWidget","focusable","1",None,None,None,None,None,None,None,None,None), + (5,241,"GtkWidget","hexpand","1",None,None,None,None,None,None,None,None,None), (6,1,"GtkPopover","child",None,None,None,None,None,2,None,None,None,None), (6,1,"GtkPopover","position","left",None,None,None,None,None,None,None,None,None), (6,2,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None), diff --git a/czkawka_gui/ui/main_window.ui b/czkawka_gui/ui/main_window.ui index f267501..9629ced 100644 --- a/czkawka_gui/ui/main_window.ui +++ b/czkawka_gui/ui/main_window.ui @@ -269,6 +269,17 @@ 1 + + + Disabled Extensions + + + + + 1 + 1 + + diff --git a/czkawka_gui/ui/progress.ui b/czkawka_gui/ui/progress.ui index 8f20aae..8686a23 100644 --- a/czkawka_gui/ui/progress.ui +++ b/czkawka_gui/ui/progress.ui @@ -13,7 +13,7 @@ vertical 10 - + 2 2 2 diff --git a/instructions/Compilation.md b/instructions/Compilation.md index 287b647..eaff0c7 100644 --- a/instructions/Compilation.md +++ b/instructions/Compilation.md @@ -19,7 +19,7 @@ New versions of GTK fixes some bugs, so e.g. middle button selection will work o | Program | Min | What for | |---------|--------|--------------------------------------------------------------------------------------| -| Rust | 1.72.1 | The minimum version of rust does not depend on anything, so it can change frequently | +| Rust | 1.74.0 | The minimum version of rust does not depend on anything, so it can change frequently | | GTK | 4.6 | Only for the `GTK` backend | #### Debian / Ubuntu diff --git a/krokiet/Cargo.toml b/krokiet/Cargo.toml index 405ae32..8b56513 100644 --- a/krokiet/Cargo.toml +++ b/krokiet/Cargo.toml @@ -3,7 +3,7 @@ name = "krokiet" version = "6.1.0" authors = ["RafaÅ‚ Mikrut "] edition = "2021" -rust-version = "1.72.1" +rust-version = "1.74.0" description = "Slint frontend of Czkawka Core" license = "GPL-3" homepage = "https://github.com/qarmin/czkawka" @@ -13,11 +13,11 @@ build = "build.rs" [dependencies] rand = "0.8" czkawka_core = { version = "6.1.0", path = "../czkawka_core" } -chrono = "0.4.31" +chrono = "0.4.34" open = "5.0" crossbeam-channel = "0.5" handsome_logger = "0.8" -rfd = { version = "0.12", default-features = false, features = ["xdg-portal"] } +rfd = { version = "0.13", default-features = false, features = ["xdg-portal", "async-std"] } home = "0.5" log = "0.4.20" serde = "1.0" @@ -26,26 +26,26 @@ humansize = "2.1" image = "0.24" directories-next = "2.0" image_hasher = "1.2" -rayon = "1.8.0" +rayon = "1.8" # Translations i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] } -i18n-embed-fl = "0.7" +i18n-embed-fl = "0.8" rust-embed = { version = "8.2", features = ["debug-embed"] } once_cell = "1.19" # Try to use only needed features from https://github.com/slint-ui/slint/blob/master/api/rs/slint/Cargo.toml#L23-L31 #slint = { path = "/home/rafal/test/slint/api/rs/slint/", default-features = false, features = ["std", -slint = { git = "https://github.com/slint-ui/slint.git", default-features = false, features = [ -# slint = { version = "1.3", default-features = false, features = [ +#slint = { git = "https://github.com/slint-ui/slint.git", default-features = false, features = [ + slint = { version = "1.4", default-features = false, features = [ "std", "backend-winit", "compat-1-2" ] } [build-dependencies] #slint-build = { path = "/home/rafal/test/slint/api/rs/build/"} -slint-build = { git = "https://github.com/slint-ui/slint.git" } -# slint-build = "1.3" +#slint-build = { git = "https://github.com/slint-ui/slint.git" } + slint-build = "1.4" [features] default = ["winit_femtovg", "winit_software"] diff --git a/krokiet/LICENSE b/krokiet/LICENSE index 0ae9b64..bc95413 100644 --- a/krokiet/LICENSE +++ b/krokiet/LICENSE @@ -221,7 +221,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - Czkawka Slint Gui Copyright (C) 2023 RafaÅ‚ Mikrut + Czkawka Slint Gui Copyright (C) 2024 RafaÅ‚ Mikrut This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/krokiet/LICENSE_MIT_CODE b/krokiet/LICENSE_MIT_CODE index 8836e46..298d0d4 100644 --- a/krokiet/LICENSE_MIT_CODE +++ b/krokiet/LICENSE_MIT_CODE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020-2023 RafaÅ‚ Mikrut +Copyright (c) 2020-2024 RafaÅ‚ Mikrut Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/krokiet/README.md b/krokiet/README.md index 7ba9f93..45d0dff 100644 --- a/krokiet/README.md +++ b/krokiet/README.md @@ -1,6 +1,6 @@ # Krokiet -Krokiet is new Czkawka frontend written in Slint(written mostly in Rust) in opposite to Gtk 4 frontend which uses mostly +Krokiet is new Czkawka frontend written in Slint, which uses Rust in opposite to Gtk 4 frontend which uses mostly C code. Different toolkit means different look, limitations and features, so you should not expect same features like in Gtk 4 @@ -24,7 +24,7 @@ sudo apt install libfontconfig-dev libfreetype-dev Default compilation is done by `cargo build --release` and should work on most systems. -You need the latest available version of Rust to compile it, because Krokiet aims to support the latest slint verions, +You need the latest available version of Rust to compile it, because Krokiet aims to support the latest slint versions, that should provide best experience. The only exception is building skia renderer which is non default feature that can be enabled manually if you want to @@ -103,25 +103,6 @@ SLINT_STYLE=material-dark cargo run -- --path . preview - [slint live preview example](https://slint.dev/releases/1.3.0/editor/?load_demo=examples/printerdemo/ui/printerdemo.slint) - Improving app rust code -## Missing features available in GTK 4 frontend - -- icons in buttons -- resizable input files panel -- settings -- moving files -- deleting files -- sorting files -- saving results -- symlink/hardlink -- implementing all modes -- multiple selection -- proper popup windows - slint not handle them properly -- logo -- about window -- reference folders -- translations(problem is only with interface, messages like "Checking {x} file" can be easily translated from rust - side) - ## Why Slint? There are multiple reasons why I decided to use Slint as toolkit for Krokiet over other toolkits. diff --git a/krokiet/src/common.rs b/krokiet/src/common.rs index 2f29e37..2243774 100644 --- a/krokiet/src/common.rs +++ b/krokiet/src/common.rs @@ -1,9 +1,10 @@ -use crate::CurrentTab; -use slint::{ModelRc, SharedString, StandardListViewItem, VecModel}; use std::path::PathBuf; +use crate::{CurrentTab, ExcludedDirectoriesModel, IncludedDirectoriesModel, MainListModel, MainWindow}; +use slint::{ModelRc, SharedString, VecModel}; + // Remember to match updated this according to ui/main_lists.slint and connect_scan.rs files -pub fn get_path_idx(active_tab: CurrentTab) -> usize { +pub fn get_str_path_idx(active_tab: CurrentTab) -> usize { match active_tab { CurrentTab::EmptyFolders => 1, CurrentTab::EmptyFiles => 1, @@ -11,7 +12,7 @@ pub fn get_path_idx(active_tab: CurrentTab) -> usize { CurrentTab::Settings => panic!("Button should be disabled"), } } -pub fn get_name_idx(active_tab: CurrentTab) -> usize { +pub fn get_str_name_idx(active_tab: CurrentTab) -> usize { match active_tab { CurrentTab::EmptyFolders => 0, CurrentTab::EmptyFiles => 0, @@ -19,6 +20,39 @@ pub fn get_name_idx(active_tab: CurrentTab) -> usize { CurrentTab::Settings => panic!("Button should be disabled"), } } + +pub fn get_int_modification_date_idx(active_tab: CurrentTab) -> usize { + match active_tab { + CurrentTab::EmptyFiles => 0, + CurrentTab::SimilarImages => 0, + CurrentTab::EmptyFolders => 0, + CurrentTab::Settings => panic!("Button should be disabled"), + } +} +pub fn get_int_size_idx(active_tab: CurrentTab) -> usize { + match active_tab { + CurrentTab::EmptyFiles => 2, + CurrentTab::SimilarImages => 2, + CurrentTab::Settings => panic!("Button should be disabled"), + CurrentTab::EmptyFolders => panic!("Unable to get size from this tab"), + } +} + +pub fn get_int_width_idx(active_tab: CurrentTab) -> usize { + match active_tab { + CurrentTab::SimilarImages => 4, + CurrentTab::Settings => panic!("Button should be disabled"), + _ => panic!("Unable to get height from this tab"), + } +} +pub fn get_int_height_idx(active_tab: CurrentTab) -> usize { + match active_tab { + CurrentTab::SimilarImages => 5, + CurrentTab::Settings => panic!("Button should be disabled"), + _ => panic!("Unable to get height from this tab"), + } +} + pub fn get_is_header_mode(active_tab: CurrentTab) -> bool { match active_tab { CurrentTab::EmptyFolders | CurrentTab::EmptyFiles => false, @@ -27,6 +61,24 @@ pub fn get_is_header_mode(active_tab: CurrentTab) -> bool { } } +pub fn get_tool_model(app: &MainWindow, tab: CurrentTab) -> ModelRc { + match tab { + CurrentTab::EmptyFolders => app.get_empty_folder_model(), + CurrentTab::SimilarImages => app.get_similar_images_model(), + CurrentTab::EmptyFiles => app.get_empty_files_model(), + CurrentTab::Settings => panic!("Button should be disabled"), + } +} + +pub fn set_tool_model(app: &MainWindow, tab: CurrentTab, model: ModelRc) { + match tab { + CurrentTab::EmptyFolders => app.set_empty_folder_model(model), + CurrentTab::SimilarImages => app.set_similar_images_model(model), + CurrentTab::EmptyFiles => app.set_empty_files_model(model), + CurrentTab::Settings => panic!("Button should be disabled"), + } +} + // pub fn create_string_standard_list_view(items: &[String]) -> ModelRc { // let new_folders_standard_list_view = items // .iter() @@ -38,18 +90,98 @@ pub fn get_is_header_mode(active_tab: CurrentTab) -> bool { // .collect::>(); // ModelRc::new(VecModel::from(new_folders_standard_list_view)) // } -pub fn create_string_standard_list_view_from_pathbuf(items: &[PathBuf]) -> ModelRc { - let new_folders_standard_list_view = items +// pub fn create_string_standard_list_view_from_pathbuf(items: &[PathBuf]) -> ModelRc { +// let new_folders_standard_list_view = items +// .iter() +// .map(|x| { +// let mut element = StandardListViewItem::default(); +// element.text = x.to_string_lossy().to_string().into(); +// element +// }) +// .collect::>(); +// ModelRc::new(VecModel::from(new_folders_standard_list_view)) +// } + +pub fn create_included_directories_model_from_pathbuf(items: &[PathBuf], referenced: &[PathBuf]) -> ModelRc { + let referenced_as_string = referenced.iter().map(|x| x.to_string_lossy().to_string()).collect::>(); + let converted = items .iter() .map(|x| { - let mut element = StandardListViewItem::default(); - element.text = x.to_string_lossy().to_string().into(); - element + let path_as_string = x.to_string_lossy().to_string(); + IncludedDirectoriesModel { + path: x.to_string_lossy().to_string().into(), + referenced_folder: referenced_as_string.contains(&path_as_string), + selected_row: false, + } }) .collect::>(); - ModelRc::new(VecModel::from(new_folders_standard_list_view)) + ModelRc::new(VecModel::from(converted)) +} + +pub fn create_excluded_directories_model_from_pathbuf(items: &[PathBuf]) -> ModelRc { + let converted = items + .iter() + .map(|x| ExcludedDirectoriesModel { + path: x.to_string_lossy().to_string().into(), + selected_row: false, + }) + .collect::>(); + ModelRc::new(VecModel::from(converted)) } pub fn create_vec_model_from_vec_string(items: Vec) -> VecModel { VecModel::from(items.into_iter().map(SharedString::from).collect::>()) } + +// Workaround for https://github.com/slint-ui/slint/discussions/4596 +// Currently there is no way to save u64 in slint, so we need to split it into two i32 +pub fn split_u64_into_i32s(value: u64) -> (i32, i32) { + let part1: i32 = (value >> 32) as i32; + let part2: i32 = value as i32; + (part1, part2) +} +pub fn connect_i32_into_u64(part1: i32, part2: i32) -> u64 { + ((part1 as u64) << 32) | (part2 as u64 & 0xFFFFFFFF) +} + +#[cfg(test)] +mod test { + use crate::common::split_u64_into_i32s; + + #[test] + fn test_split_u64_into_i32s_small() { + let value = 1; + let (part1, part2) = split_u64_into_i32s(value); + assert_eq!(part1, 0); + assert_eq!(part2, 1); + } + #[test] + fn test_split_u64_into_i32s_big() { + let value = u64::MAX; + let (part1, part2) = split_u64_into_i32s(value); + assert_eq!(part1, -1); + assert_eq!(part2, -1); + } + #[test] + fn test_connect_i32_into_u64_small() { + let part1 = 0; + let part2 = 1; + let value = super::connect_i32_into_u64(part1, part2); + assert_eq!(value, 1); + } + #[test] + fn test_connect_i32_into_u64_big() { + let part1 = -1; + let part2 = -1; + let value = super::connect_i32_into_u64(part1, part2); + assert_eq!(value, u64::MAX); + } + #[test] + fn test_connect_split_zero() { + for start_value in [0, 1, 10, u32::MAX as u64, i32::MAX as u64, u64::MAX] { + let (part1, part2) = split_u64_into_i32s(start_value); + let end_value = super::connect_i32_into_u64(part1, part2); + assert_eq!(start_value, end_value); + } + } +} diff --git a/krokiet/src/connect_delete.rs b/krokiet/src/connect_delete.rs index 1df2c85..ea5ec6d 100644 --- a/krokiet/src/connect_delete.rs +++ b/krokiet/src/connect_delete.rs @@ -1,9 +1,12 @@ -use slint::{ComponentHandle, Model, ModelRc, VecModel}; - -use crate::common::{get_is_header_mode, get_name_idx, get_path_idx}; -use crate::{Callabler, CurrentTab, GuiState, MainListModel, MainWindow}; -use czkawka_core::common::{remove_folder_if_contains_only_empty_folders, CHARACTER}; use rayon::prelude::*; +use slint::{ComponentHandle, ModelRc, VecModel}; + +use czkawka_core::common::remove_folder_if_contains_only_empty_folders; +use czkawka_core::common_messages::Messages; + +use crate::common::{get_is_header_mode, get_tool_model, set_tool_model}; +use crate::model_operations::{collect_full_path_from_model, deselect_all_items, filter_out_checked_items}; +use crate::{Callabler, CurrentTab, GuiState, MainListModel, MainWindow}; pub fn connect_delete_button(app: &MainWindow) { let a = app.as_weak(); @@ -12,263 +15,56 @@ pub fn connect_delete_button(app: &MainWindow) { let active_tab = app.global::().get_active_tab(); - let model = match active_tab { - CurrentTab::EmptyFolders => app.get_empty_folder_model(), - CurrentTab::SimilarImages => app.get_similar_images_model(), - CurrentTab::EmptyFiles => app.get_empty_files_model(), - CurrentTab::Settings => panic!("Button should be disabled"), - }; + let model = get_tool_model(&app, active_tab); - let new_model = handle_delete_items(&model, active_tab); + let remove_to_trash = false; + + let (errors, new_model) = handle_delete_items(&model, active_tab, remove_to_trash); if let Some(new_model) = new_model { - match active_tab { - CurrentTab::EmptyFolders => app.set_empty_folder_model(new_model), - CurrentTab::SimilarImages => app.set_similar_images_model(new_model), - CurrentTab::EmptyFiles => app.set_empty_files_model(new_model), - CurrentTab::Settings => panic!("Button should be disabled"), - } + set_tool_model(&app, active_tab, new_model); } + app.global::().set_info_text(Messages::new_from_errors(errors).create_messages_text().into()); + app.global::().set_preview_visible(false); }); } -fn handle_delete_items(items: &ModelRc, active_tab: CurrentTab) -> Option> { +fn handle_delete_items(items: &ModelRc, active_tab: CurrentTab, remove_to_trash: bool) -> (Vec, Option>) { let (entries_to_delete, mut entries_left) = filter_out_checked_items(items, get_is_header_mode(active_tab)); if !entries_to_delete.is_empty() { - remove_selected_items(entries_to_delete, active_tab); + let vec_items_to_remove = collect_full_path_from_model(&entries_to_delete, active_tab); + let errors = remove_selected_items(vec_items_to_remove, active_tab, remove_to_trash); deselect_all_items(&mut entries_left); let r = ModelRc::new(VecModel::from(entries_left)); // TODO here maybe should also stay old model if entries cannot be removed - return Some(r); + return (errors, Some(r)); } - None + (vec![], None) } // TODO delete in parallel items, consider to add progress bar // For empty folders double check if folders are really empty - this function probably should be run in thread // and at the end should be send signal to main thread to update model // TODO handle also situations where cannot delete file/folder -fn remove_selected_items(items: Vec, active_tab: CurrentTab) { - let path_idx = get_path_idx(active_tab); - let name_idx = get_name_idx(active_tab); - let items_to_remove = items - .iter() - .map(|item| { - let path = item.val.iter().nth(path_idx).unwrap(); - let name = item.val.iter().nth(name_idx).unwrap(); - format!("{}{}{}", path, CHARACTER, name) - }) - .collect::>(); - +fn remove_selected_items(items_to_remove: Vec, active_tab: CurrentTab, remove_to_trash: bool) -> Vec { // Iterate over empty folders and not delete them if they are not empty if active_tab == CurrentTab::EmptyFolders { - items_to_remove.into_par_iter().for_each(|item| { - remove_folder_if_contains_only_empty_folders(item); - }); + items_to_remove + .into_par_iter() + .filter_map(|item| remove_folder_if_contains_only_empty_folders(item, remove_to_trash).err()) + .collect() } else { - items_to_remove.into_par_iter().for_each(|item| { - let _ = std::fs::remove_file(item); - }); - } -} - -fn deselect_all_items(items: &mut [MainListModel]) { - for item in items { - item.selected_row = false; - } -} - -fn filter_out_checked_items(items: &ModelRc, have_header: bool) -> (Vec, Vec) { - if cfg!(debug_assertions) { - check_if_header_is_checked(items); - check_if_header_is_selected_but_should_not_be(items, have_header); - } - - let (entries_to_delete, mut entries_left): (Vec<_>, Vec<_>) = items.iter().partition(|item| item.checked); - - // When have header, we must also throw out orphaned items - this needs to be - if have_header && !entries_left.is_empty() { - // First row must be header - assert!(entries_left[0].header_row); - - if entries_left.len() == 3 { - // First row is header, so if second or third is also header, then there is no enough items to fill model - if entries_left[1].header_row || entries_left[2].header_row { - entries_left = Vec::new(); - } - } else if entries_left.len() < 3 { - // Not have enough items to fill model - entries_left = Vec::new(); - } else { - let mut last_header = 0; - let mut new_items: Vec = Vec::new(); - for i in 1..entries_left.len() { - if entries_left[i].header_row { - if i - last_header > 2 { - new_items.extend(entries_left[last_header..i].iter().cloned()); - } - last_header = i; + items_to_remove + .into_par_iter() + .filter_map(|item| { + if let Err(e) = std::fs::remove_file(item) { + return Some(format!("Error while removing file: {e}")); } - } - if entries_left.len() - last_header > 2 { - new_items.extend(entries_left[last_header..].iter().cloned()); - } - - entries_left = new_items; - } - } - - (entries_to_delete, entries_left) -} - -// Function to verify if really headers are not checked -// Checked header is big bug -fn check_if_header_is_checked(items: &ModelRc) { - if cfg!(debug_assertions) { - for item in items.iter() { - if item.header_row { - assert!(!item.checked); - } - } - } -} - -// In some modes header should not be visible, but if are, then it is a bug -fn check_if_header_is_selected_but_should_not_be(items: &ModelRc, can_have_header: bool) { - if cfg!(debug_assertions) { - if !can_have_header { - for item in items.iter() { - assert!(!item.header_row); - } - } - } -} - -#[cfg(test)] -mod tests { - use slint::{Model, ModelRc, SharedString, VecModel}; - - use crate::connect_delete::filter_out_checked_items; - use crate::MainListModel; - - #[test] - fn test_filter_out_checked_items_empty() { - let items: ModelRc = create_new_model(vec![]); - - let (to_delete, left) = filter_out_checked_items(&items, false); - assert!(to_delete.is_empty()); - assert!(left.is_empty()); - let (to_delete, left) = filter_out_checked_items(&items, true); - assert!(to_delete.is_empty()); - assert!(left.is_empty()); - } - #[test] - fn test_filter_out_checked_items_one_element_valid_normal() { - let items = create_new_model(vec![(false, false, false, vec![])]); - let (to_delete, left) = filter_out_checked_items(&items, false); - assert!(to_delete.is_empty()); - assert_eq!(left.len(), items.iter().count()); - } - - #[test] - fn test_filter_out_checked_items_one_element_valid_header() { - let items = create_new_model(vec![(false, true, false, vec![])]); - let (to_delete, left) = filter_out_checked_items(&items, true); - assert!(to_delete.is_empty()); - assert!(left.is_empty()); - } - - #[test] - #[should_panic] - fn test_filter_out_checked_items_one_element_invalid_normal() { - let items = create_new_model(vec![(false, true, false, vec![])]); - filter_out_checked_items(&items, false); - } - #[test] - #[should_panic] - fn test_filter_out_checked_items_one_element_invalid_header() { - let items = create_new_model(vec![(false, false, false, vec![])]); - filter_out_checked_items(&items, true); - } - - #[test] - fn test_filter_out_checked_items_multiple_element_valid_normal() { - let items = create_new_model(vec![ - (false, false, false, vec!["1"]), - (false, false, false, vec!["2"]), - (true, false, false, vec!["3"]), - (true, false, false, vec!["4"]), - (false, false, false, vec!["5"]), - ]); - let (to_delete, left) = filter_out_checked_items(&items, false); - let to_delete_data = get_single_data_from_model(&to_delete); - let left_data = get_single_data_from_model(&left); - - assert_eq!(to_delete_data, vec!["3", "4"]); - assert_eq!(left_data, vec!["1", "2", "5"]); - } - - #[test] - fn test_filter_out_checked_items_multiple_element_valid_header() { - let items = create_new_model(vec![ - (false, true, false, vec!["1"]), - (false, false, false, vec!["2"]), - (true, false, false, vec!["3"]), - (false, true, false, vec!["4"]), - (false, false, false, vec!["5"]), - (false, true, false, vec!["6"]), - (false, false, false, vec!["7"]), - (false, false, false, vec!["8"]), - ]); - let (to_delete, left) = filter_out_checked_items(&items, true); - let to_delete_data = get_single_data_from_model(&to_delete); - let left_data = get_single_data_from_model(&left); - - assert_eq!(to_delete_data, vec!["3"]); - assert_eq!(left_data, vec!["6", "7", "8"]); - } - - #[test] - fn test_filter_out_checked_items_multiple2_element_valid_header() { - let items = create_new_model(vec![ - (false, true, false, vec!["1"]), - (false, false, false, vec!["2"]), - (true, false, false, vec!["3"]), - (false, false, false, vec!["4"]), - (false, false, false, vec!["5"]), - (false, false, false, vec!["6"]), - (false, true, false, vec!["7"]), - (false, false, false, vec!["8"]), - ]); - let (to_delete, left) = filter_out_checked_items(&items, true); - let to_delete_data = get_single_data_from_model(&to_delete); - let left_data = get_single_data_from_model(&left); - - assert_eq!(to_delete_data, vec!["3"]); - assert_eq!(left_data, vec!["1", "2", "4", "5", "6"]); - } - - fn get_single_data_from_model(model: &[MainListModel]) -> Vec { - let mut d = model.iter().map(|item| item.val.iter().next().unwrap().to_string()).collect::>(); - d.sort(); - d - } - - fn create_new_model(items: Vec<(bool, bool, bool, Vec<&'static str>)>) -> ModelRc { - let model = VecModel::default(); - for item in items { - let all_items: Vec = item.3.iter().map(|item| (*item).into()).collect::>(); - let all_items = VecModel::from(all_items); - model.push(MainListModel { - checked: item.0, - header_row: item.1, - selected_row: item.2, - val: ModelRc::new(all_items), - }); - } - ModelRc::new(model) + None + }) + .collect() } } diff --git a/krokiet/src/connect_directories_changes.rs b/krokiet/src/connect_directories_changes.rs index be8d5a7..d73c6dd 100644 --- a/krokiet/src/connect_directories_changes.rs +++ b/krokiet/src/connect_directories_changes.rs @@ -1,7 +1,7 @@ use rfd::FileDialog; use slint::{ComponentHandle, Model, ModelRc, VecModel}; -use crate::{Callabler, MainWindow, Settings}; +use crate::{Callabler, ExcludedDirectoriesModel, IncludedDirectoriesModel, MainWindow, Settings}; pub fn connect_add_remove_directories(app: &MainWindow) { connect_add_directories(app); @@ -12,66 +12,67 @@ pub fn connect_add_remove_directories(app: &MainWindow) { fn connect_add_manual_directories(app: &MainWindow) { let a = app.as_weak(); app.global::().on_added_manual_directories(move |included_directories, list_of_files_to_add| { - let non_empty_lines = list_of_files_to_add.lines().filter(|x| !x.is_empty()).collect::>(); - if non_empty_lines.is_empty() { + let folders = list_of_files_to_add.lines().filter(|x| !x.is_empty()).map(str::to_string).collect::>(); + if folders.is_empty() { return; } let app = a.upgrade().unwrap(); let settings = app.global::(); if included_directories { - let included_model = settings.get_included_directories(); - let mut included_model = included_model.iter().collect::>(); - included_model.extend(non_empty_lines.iter().map(|x| { - let mut element = slint::StandardListViewItem::default(); - element.text = (*x).into(); - element - })); - included_model.sort_by_cached_key(|x| x.text.to_string()); - included_model.dedup(); - settings.set_included_directories(ModelRc::new(VecModel::from(included_model))); + add_included_directories(&settings, &folders); } else { - let excluded_model = settings.get_excluded_directories(); - let mut excluded_model = excluded_model.iter().collect::>(); - excluded_model.extend(non_empty_lines.iter().map(|x| { - let mut element = slint::StandardListViewItem::default(); - element.text = (*x).into(); - element - })); - excluded_model.sort_by_cached_key(|x| x.text.to_string()); - excluded_model.dedup(); - settings.set_excluded_directories(ModelRc::new(VecModel::from(excluded_model))); + add_excluded_directories(&settings, &folders); } }); } fn connect_remove_directories(app: &MainWindow) { let a = app.as_weak(); - app.global::().on_remove_item_directories(move |included_directories, current_index| { + app.global::().on_remove_item_directories(move |included_directories| { + let app = a.upgrade().unwrap(); + let settings = app.global::(); + + let current_index = if included_directories { + settings.get_included_directories_model_selected_idx() + } else { + settings.get_excluded_directories_model_selected_idx() + }; + // Nothing selected if current_index == -1 { return; } - let app = a.upgrade().unwrap(); - let settings = app.global::(); if included_directories { - let included_model = settings.get_included_directories(); + let included_model = settings.get_included_directories_model(); let model_count = included_model.iter().count(); if model_count > current_index as usize { let mut included_model = included_model.iter().collect::>(); included_model.remove(current_index as usize); - settings.set_included_directories(ModelRc::new(VecModel::from(included_model))); + if current_index as usize != model_count - 1 { + included_model[current_index as usize].selected_row = true; + settings.set_included_directories_model_selected_idx(current_index); + } else { + settings.set_included_directories_model_selected_idx(-1); + } + settings.set_included_directories_model(ModelRc::new(VecModel::from(included_model))); } } else { - let excluded_model = settings.get_excluded_directories(); + let excluded_model = settings.get_excluded_directories_model(); let model_count = excluded_model.iter().count(); if model_count > current_index as usize { let mut excluded_model = excluded_model.iter().collect::>(); excluded_model.remove(current_index as usize); - settings.set_excluded_directories(ModelRc::new(VecModel::from(excluded_model))); + if current_index as usize != model_count - 1 { + excluded_model[current_index as usize].selected_row = true; + settings.set_excluded_directories_model_selected_idx(current_index); + } else { + settings.set_excluded_directories_model_selected_idx(-1); + } + settings.set_excluded_directories_model(ModelRc::new(VecModel::from(excluded_model))); } } }); @@ -89,33 +90,54 @@ fn connect_add_directories(app: &MainWindow) { let Some(folders) = file_dialog.pick_folders() else { return; }; + let folders = folders.iter().map(|x| x.to_string_lossy().to_string()).collect::>(); let settings = app.global::(); - let old_folders = if included_directories { - settings.get_included_directories() - } else { - settings.get_excluded_directories() - }; - - let mut new_folders = old_folders.iter().map(|x| x.text.to_string()).collect::>(); - new_folders.extend(folders.iter().map(|x| x.to_string_lossy().to_string())); - new_folders.sort(); - new_folders.dedup(); - - let new_folders_standard_list_view = new_folders - .iter() - .map(|x| { - let mut element = slint::StandardListViewItem::default(); - element.text = x.into(); - element - }) - .collect::>(); - let new_folders_model = ModelRc::new(VecModel::from(new_folders_standard_list_view)); - if included_directories { - settings.set_included_directories(new_folders_model); + add_included_directories(&settings, &folders); } else { - settings.set_excluded_directories(new_folders_model); + add_excluded_directories(&settings, &folders); } }); } + +fn add_included_directories(settings: &Settings, folders: &[String]) { + let old_folders = settings.get_included_directories_model(); + let old_folders_path = old_folders.iter().map(|x| x.path.to_string()).collect::>(); + let mut new_folders = old_folders.iter().collect::>(); + + let filtered_folders = folders.iter().filter(|x| !old_folders_path.contains(x)).collect::>(); + + new_folders.iter_mut().for_each(|x| x.selected_row = false); + + new_folders.extend(filtered_folders.iter().map(|path| IncludedDirectoriesModel { + path: (*path).into(), + referenced_folder: false, + selected_row: false, + })); + + new_folders.sort_by_key(|x| x.path.clone()); + + let new_folders_model = ModelRc::new(VecModel::from(new_folders)); + settings.set_included_directories_model(new_folders_model); +} + +fn add_excluded_directories(settings: &Settings, folders: &[String]) { + let old_folders = settings.get_excluded_directories_model(); + let old_folders_path = old_folders.iter().map(|x| x.path.to_string()).collect::>(); + let mut new_folders = old_folders.iter().collect::>(); + + let filtered_folders = folders.iter().filter(|x| !old_folders_path.contains(x)).collect::>(); + + new_folders.iter_mut().for_each(|x| x.selected_row = false); + + new_folders.extend(filtered_folders.iter().map(|path| ExcludedDirectoriesModel { + path: (*path).into(), + selected_row: false, + })); + + new_folders.sort_by_key(|x| x.path.clone()); + + let new_folders_model = ModelRc::new(VecModel::from(new_folders)); + settings.set_excluded_directories_model(new_folders_model); +} diff --git a/krokiet/src/connect_move.rs b/krokiet/src/connect_move.rs new file mode 100644 index 0000000..61196b5 --- /dev/null +++ b/krokiet/src/connect_move.rs @@ -0,0 +1,122 @@ +use crate::common::{get_is_header_mode, get_tool_model, set_tool_model}; +use crate::model_operations::{collect_path_name_from_model, deselect_all_items, filter_out_checked_items}; +use crate::{Callabler, CurrentTab, GuiState, MainListModel, MainWindow}; + +use czkawka_core::common_messages::Messages; +use rayon::prelude::*; +use rfd::FileDialog; +use slint::{ComponentHandle, ModelRc, VecModel}; +use std::path::{Path, PathBuf}; +use std::{fs, path}; + +pub fn connect_move(app: &MainWindow) { + let a = app.as_weak(); + app.on_folders_move_choose_requested(move || { + let app = a.upgrade().unwrap(); + + let file_dialog = FileDialog::new(); + let Some(folder) = file_dialog.pick_folder() else { + return; + }; + let folder_str = folder.to_string_lossy().to_string(); + + app.invoke_show_move_folders_dialog(folder_str.into()); + }); + + let a = app.as_weak(); + app.global::().on_move_items(move |preserve_structure, copy_mode, output_folder| { + let app = a.upgrade().unwrap(); + let active_tab = app.global::().get_active_tab(); + let current_model = get_tool_model(&app, active_tab); + + let (errors, new_model) = move_operation(¤t_model, preserve_structure, copy_mode, &output_folder, active_tab); + if let Some(new_model) = new_model { + set_tool_model(&app, active_tab, new_model); + } + app.global::().set_info_text(Messages::new_from_errors(errors).create_messages_text().into()); + }); +} + +fn move_operation( + items: &ModelRc, + preserve_structure: bool, + copy_mode: bool, + output_folder: &str, + active_tab: CurrentTab, +) -> (Vec, Option>) { + let (entries_to_move, mut entries_left) = filter_out_checked_items(items, get_is_header_mode(active_tab)); + + if !entries_to_move.is_empty() { + let vec_items_to_move = collect_path_name_from_model(&entries_to_move, active_tab); + let errors = move_selected_items(vec_items_to_move, preserve_structure, copy_mode, output_folder); + deselect_all_items(&mut entries_left); + + let r = ModelRc::new(VecModel::from(entries_left)); + return (errors, Some(r)); + } + (vec![], None) +} + +fn move_selected_items(items_to_move: Vec<(String, String)>, preserve_structure: bool, copy_mode: bool, output_folder: &str) -> Vec { + if let Err(err) = fs::create_dir_all(output_folder) { + return vec![format!("Error while creating folder: {err}")]; + } + + if copy_mode { + items_to_move + .into_par_iter() + .filter_map(|(path, name)| { + let (input_file, output_file) = collect_path_and_create_folders(&path, &name, output_folder, preserve_structure); + + if let Err(e) = fs::copy(&input_file, &output_file) { + return Some(format!("Error while copying file {input_file:?} to {output_file:?}, reason {e}")); + } + None + }) + .collect() + } else { + items_to_move + .into_par_iter() + .filter_map(|(path, name)| { + let (input_file, output_file) = collect_path_and_create_folders(&path, &name, output_folder, preserve_structure); + + if output_file.exists() { + return Some(format!("File {output_file:?} already exists")); + } + + // Try to rename file, may fail due various reasons + if fs::rename(&input_file, &output_file).is_ok() { + return None; + } + + // It is possible that this failed, because file is on different partition, so + // we need to copy file and then remove old + if let Err(e) = fs::copy(&input_file, &output_file) { + return Some(format!( + "Error while copying file {input_file:?} to {output_file:?}(moving into different partition), reason {e}" + )); + } + if let Err(e) = fs::remove_file(&input_file) { + return Some(format!("Error while removing file {input_file:?}(after copying into different partition), reason {e}")); + } + + None + }) + .collect() + } +} + +// Create input/output paths, and create output folder +fn collect_path_and_create_folders(input_path: &str, input_file: &str, output_path: &str, preserve_structure: bool) -> (PathBuf, PathBuf) { + let mut input_full_path = PathBuf::from(input_path); + input_full_path.push(input_file); + + let mut output_full_path = PathBuf::from(output_path); + if preserve_structure { + output_full_path.extend(Path::new(input_path).components().filter(|c| matches!(c, path::Component::Normal(_)))); + }; + let _ = fs::create_dir_all(&output_full_path); + output_full_path.push(input_file); + + (input_full_path, output_full_path) +} diff --git a/krokiet/src/connect_open.rs b/krokiet/src/connect_open.rs index d9e99b4..9165b97 100644 --- a/krokiet/src/connect_open.rs +++ b/krokiet/src/connect_open.rs @@ -1,8 +1,9 @@ -use crate::{Callabler, MainWindow}; use directories_next::ProjectDirs; use log::error; use slint::ComponentHandle; +use crate::{Callabler, MainWindow}; + pub fn connect_open_items(app: &MainWindow) { app.global::().on_item_opened(move |path| { match open::that(&*path) { diff --git a/krokiet/src/connect_progress_receiver.rs b/krokiet/src/connect_progress_receiver.rs index 376ec38..230e925 100644 --- a/krokiet/src/connect_progress_receiver.rs +++ b/krokiet/src/connect_progress_receiver.rs @@ -1,9 +1,11 @@ -use crate::{MainWindow, ProgressToSend}; +use std::thread; use crossbeam_channel::Receiver; -use czkawka_core::common_dir_traversal::{ProgressData, ToolType}; use slint::ComponentHandle; -use std::thread; + +use czkawka_core::common_dir_traversal::{ProgressData, ToolType}; + +use crate::{MainWindow, ProgressToSend}; pub fn connect_progress_gathering(app: &MainWindow, progress_receiver: Receiver) { let a = app.as_weak(); diff --git a/krokiet/src/connect_scan.rs b/krokiet/src/connect_scan.rs index 39649d9..3c68a3d 100644 --- a/krokiet/src/connect_scan.rs +++ b/krokiet/src/connect_scan.rs @@ -1,7 +1,12 @@ -use crate::settings::{collect_settings, SettingsCustom, ALLOWED_HASH_TYPE_VALUES, ALLOWED_RESIZE_ALGORITHM_VALUES}; -use crate::{CurrentTab, GuiState, MainListModel, MainWindow, ProgressToSend}; +use std::rc::Rc; +use std::thread; + use chrono::NaiveDateTime; use crossbeam_channel::{Receiver, Sender}; +use humansize::{format_size, BINARY}; +use rayon::prelude::*; +use slint::{ComponentHandle, ModelRc, SharedString, VecModel, Weak}; + use czkawka_core::common::{split_path, split_path_compare, DEFAULT_THREAD_SIZE}; use czkawka_core::common_dir_traversal::{FileEntry, ProgressData}; use czkawka_core::common_tool::CommonData; @@ -9,12 +14,11 @@ use czkawka_core::common_traits::ResultEntry; use czkawka_core::empty_files::EmptyFiles; use czkawka_core::empty_folder::{EmptyFolder, FolderEntry}; use czkawka_core::similar_images; -use czkawka_core::similar_images::SimilarImages; -use humansize::{format_size, BINARY}; -use rayon::prelude::*; -use slint::{ComponentHandle, ModelRc, SharedString, VecModel, Weak}; -use std::rc::Rc; -use std::thread; +use czkawka_core::similar_images::{ImagesEntry, SimilarImages}; + +use crate::common::split_u64_into_i32s; +use crate::settings::{collect_settings, SettingsCustom, ALLOWED_HASH_TYPE_VALUES, ALLOWED_RESIZE_ALGORITHM_VALUES}; +use crate::{CurrentTab, GuiState, MainListModel, MainWindow, ProgressToSend}; pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender, stop_receiver: Receiver<()>) { let a = app.as_weak(); @@ -47,7 +51,6 @@ pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) @@ -67,49 +70,88 @@ fn scan_similar_images(a: Weak, progress_sender: Sender>, messages: String, hash_size: u8) { +fn write_similar_images_results_referenced(app: &MainWindow, vector: Vec<(ImagesEntry, Vec)>, messages: String, hash_size: u8) { let items_found = vector.len(); let items = Rc::new(VecModel::default()); - for vec_fe in vector { - insert_data_to_model(&items, ModelRc::new(VecModel::default()), true); - for fe in vec_fe { - let (directory, file) = split_path(fe.get_path()); - let data_model = VecModel::from_slice(&[ - similar_images::get_string_from_similarity(&fe.similarity, hash_size).into(), - format_size(fe.size, BINARY).into(), - fe.dimensions.clone().into(), - file.into(), - directory.into(), - NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), - ]); + for (ref_fe, vec_fe) in vector { + let (data_model_str, data_model_int) = prepare_data_model_similar_images(&ref_fe, hash_size); + insert_data_to_model(&items, data_model_str, data_model_int, true); - insert_data_to_model(&items, data_model, false); + for fe in vec_fe { + let (data_model_str, data_model_int) = prepare_data_model_similar_images(&fe, hash_size); + insert_data_to_model(&items, data_model_str, data_model_int, false); } } app.set_similar_images_model(items.into()); app.invoke_scan_ended(format!("Found {items_found} similar images files").into()); app.global::().set_info_text(messages.into()); } +fn write_similar_images_results(app: &MainWindow, vector: Vec>, messages: String, hash_size: u8) { + let items_found = vector.len(); + let items = Rc::new(VecModel::default()); + for vec_fe in vector { + insert_data_to_model(&items, ModelRc::new(VecModel::default()), ModelRc::new(VecModel::default()), true); + for fe in vec_fe { + let (data_model_str, data_model_int) = prepare_data_model_similar_images(&fe, hash_size); + insert_data_to_model(&items, data_model_str, data_model_int, false); + } + } + app.set_similar_images_model(items.into()); + app.invoke_scan_ended(format!("Found {items_found} similar images files").into()); + app.global::().set_info_text(messages.into()); +} +fn prepare_data_model_similar_images(fe: &ImagesEntry, hash_size: u8) -> (ModelRc, ModelRc) { + let (directory, file) = split_path(fe.get_path()); + let data_model_str = VecModel::from_slice(&[ + similar_images::get_string_from_similarity(&fe.similarity, hash_size).into(), + format_size(fe.size, BINARY).into(), + format!("{}x{}", fe.width, fe.height).into(), + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), + ]); + let modification_split = split_u64_into_i32s(fe.get_modified_date()); + let size_split = split_u64_into_i32s(fe.size); + let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1, size_split.0, size_split.1, fe.width as i32, fe.height as i32]); + (data_model_str, data_model_int) +} +///////////////////////////////// Empty Files fn scan_empty_files(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) @@ -133,20 +175,28 @@ fn write_empty_files_results(app: &MainWindow, vector: Vec, messages: let items_found = vector.len(); let items = Rc::new(VecModel::default()); for fe in vector { - let (directory, file) = split_path(fe.get_path()); - let data_model = VecModel::from_slice(&[ - file.into(), - directory.into(), - NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), - ]); - - insert_data_to_model(&items, data_model, false); + let (data_model_str, data_model_int) = prepare_data_model_empty_files(&fe); + insert_data_to_model(&items, data_model_str, data_model_int, false); } app.set_empty_files_model(items.into()); app.invoke_scan_ended(format!("Found {items_found} empty files").into()); app.global::().set_info_text(messages.into()); } +fn prepare_data_model_empty_files(fe: &FileEntry) -> (ModelRc, ModelRc) { + let (directory, file) = split_path(fe.get_path()); + let data_model_str = VecModel::from_slice(&[ + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), + ]); + let modification_split = split_u64_into_i32s(fe.get_modified_date()); + let size_split = split_u64_into_i32s(fe.size); + let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1, size_split.0, size_split.1]); + (data_model_str, data_model_int) +} + +////////////////////////////////////////// Empty Folders fn scan_empty_folders(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { thread::Builder::new() .stack_size(DEFAULT_THREAD_SIZE) @@ -170,26 +220,34 @@ fn write_empty_folders_results(app: &MainWindow, vector: Vec, messa let items_found = vector.len(); let items = Rc::new(VecModel::default()); for fe in vector { - let (directory, file) = split_path(&fe.path); - let data_model = VecModel::from_slice(&[ - file.into(), - directory.into(), - NaiveDateTime::from_timestamp_opt(fe.modified_date as i64, 0).unwrap().to_string().into(), - ]); - - insert_data_to_model(&items, data_model, false); + let (data_model_str, data_model_int) = prepare_data_model_empty_folders(&fe); + insert_data_to_model(&items, data_model_str, data_model_int, false); } app.set_empty_folder_model(items.into()); app.invoke_scan_ended(format!("Found {items_found} empty folders").into()); app.global::().set_info_text(messages.into()); } -fn insert_data_to_model(items: &Rc>, data_model: ModelRc, header_row: bool) { +fn prepare_data_model_empty_folders(fe: &FolderEntry) -> (ModelRc, ModelRc) { + let (directory, file) = split_path(&fe.path); + let data_model_str = VecModel::from_slice(&[ + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.modified_date as i64, 0).unwrap().to_string().into(), + ]); + let modification_split = split_u64_into_i32s(fe.get_modified_date()); + let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1]); + (data_model_str, data_model_int) +} + +////////////////////////////////////////// Common +fn insert_data_to_model(items: &Rc>, data_model_str: ModelRc, data_model_int: ModelRc, header_row: bool) { let main = MainListModel { checked: false, header_row, selected_row: false, - val: ModelRc::new(data_model), + val_str: ModelRc::new(data_model_str), + val_int: ModelRc::new(data_model_int), }; items.push(main); } @@ -199,11 +257,13 @@ where T: CommonData, { component.set_included_directory(custom_settings.included_directories.clone()); + component.set_reference_directory(custom_settings.included_directories_referenced.clone()); component.set_excluded_directory(custom_settings.excluded_directories.clone()); component.set_recursive_search(custom_settings.recursive_search); component.set_minimal_file_size(custom_settings.minimum_file_size as u64 * 1024); component.set_maximal_file_size(custom_settings.maximum_file_size as u64 * 1024); component.set_allowed_extensions(custom_settings.allowed_extensions.clone()); + component.set_excluded_extensions(custom_settings.excluded_extensions.clone()); component.set_excluded_items(custom_settings.excluded_items.split(',').map(str::to_string).collect()); component.set_exclude_other_filesystems(custom_settings.ignore_other_file_systems); component.set_use_cache(custom_settings.use_cache); diff --git a/krokiet/src/connect_select.rs b/krokiet/src/connect_select.rs new file mode 100644 index 0000000..88aed10 --- /dev/null +++ b/krokiet/src/connect_select.rs @@ -0,0 +1,216 @@ +use crate::common::{ + connect_i32_into_u64, get_int_height_idx, get_int_modification_date_idx, get_int_size_idx, get_int_width_idx, get_is_header_mode, get_tool_model, set_tool_model, +}; +use crate::{Callabler, CurrentTab, GuiState, MainListModel, MainWindow, SelectMode, SelectModel}; +use slint::{ComponentHandle, Model, ModelRc, VecModel}; + +// TODO optimize this, not sure if it is possible to not copy entire model to just select item +// https://github.com/slint-ui/slint/discussions/4595 +pub fn connect_select(app: &MainWindow) { + let a = app.as_weak(); + app.global::().on_select_items(move |select_mode| { + let app = a.upgrade().unwrap(); + let active_tab = app.global::().get_active_tab(); + let current_model = get_tool_model(&app, active_tab); + + let new_model = match select_mode { + SelectMode::SelectAll => select_all(current_model), + SelectMode::UnselectAll => deselect_all(current_model), + SelectMode::InvertSelection => invert_selection(current_model), + SelectMode::SelectTheBiggestSize => select_by_size_date(current_model, active_tab, true, true), + SelectMode::SelectTheSmallestSize => select_by_size_date(current_model, active_tab, false, true), + SelectMode::SelectTheBiggestResolution => select_by_resolution(current_model, active_tab, true), + SelectMode::SelectTheSmallestResolution => select_by_resolution(current_model, active_tab, false), + SelectMode::SelectNewest => select_by_size_date(current_model, active_tab, true, false), + SelectMode::SelectOldest => select_by_size_date(current_model, active_tab, false, false), + }; + set_tool_model(&app, active_tab, new_model); + }); +} + +pub fn connect_showing_proper_select_buttons(app: &MainWindow) { + set_select_buttons(app); + let a = app.as_weak(); + app.global::().on_tab_changed(move || { + let app = a.upgrade().unwrap(); + set_select_buttons(&app); + }); +} + +fn set_select_buttons(app: &MainWindow) { + let active_tab = app.global::().get_active_tab(); + let mut base_buttons = vec![SelectMode::SelectAll, SelectMode::UnselectAll, SelectMode::InvertSelection]; + + let additional_buttons = match active_tab { + CurrentTab::SimilarImages => vec![ + SelectMode::SelectOldest, + SelectMode::SelectNewest, + SelectMode::SelectTheSmallestSize, + SelectMode::SelectTheBiggestSize, + SelectMode::SelectTheSmallestResolution, + SelectMode::SelectTheBiggestResolution, + ], + _ => vec![], + }; + + base_buttons.extend(additional_buttons); + base_buttons.reverse(); + + let new_select_model = base_buttons + .into_iter() + .map(|e| SelectModel { + name: translate_select_mode(e).into(), + data: e, + }) + .collect::>(); + + app.global::().set_select_results_list(ModelRc::new(VecModel::from(new_select_model))); +} + +fn translate_select_mode(select_mode: SelectMode) -> String { + match select_mode { + SelectMode::SelectAll => "Select all".into(), + SelectMode::UnselectAll => "Unselect all".into(), + SelectMode::InvertSelection => "Invert selection".into(), + SelectMode::SelectTheBiggestSize => "Select the biggest size".into(), + SelectMode::SelectTheBiggestResolution => "Select the biggest resolution".into(), + SelectMode::SelectTheSmallestSize => "Select the smallest size".into(), + SelectMode::SelectTheSmallestResolution => "Select the smallest resolution".into(), + SelectMode::SelectNewest => "Select newest".into(), + SelectMode::SelectOldest => "Select oldest".into(), + } +} + +// TODO, when model will be able to contain i64 instead two i32, this function could be merged with select_by_size_date +fn select_by_resolution(model: ModelRc, active_tab: CurrentTab, biggest: bool) -> ModelRc { + let is_header_mode = get_is_header_mode(active_tab); + assert!(is_header_mode); // non header modes not really have reason to use this function + + let mut old_data = model.iter().collect::>(); + let headers_idx = find_header_idx_and_deselect_all(&mut old_data); + let width_idx = get_int_width_idx(active_tab); + let height_idx = get_int_height_idx(active_tab); + + if biggest { + for i in 0..(headers_idx.len() - 1) { + let mut max_item = 0; + let mut max_item_idx = 0; + #[allow(clippy::needless_range_loop)] + for j in (headers_idx[i] + 1)..headers_idx[i + 1] { + let int_data = old_data[j].val_int.iter().collect::>(); + let item = int_data[width_idx] * int_data[height_idx]; + if item > max_item { + max_item = item; + max_item_idx = j; + } + } + old_data[max_item_idx].checked = true; + } + } else { + for i in 0..(headers_idx.len() - 1) { + let mut min_item = u64::MAX; + let mut min_item_idx = 0; + #[allow(clippy::needless_range_loop)] + for j in (headers_idx[i] + 1)..headers_idx[i + 1] { + let int_data = old_data[j].val_int.iter().collect::>(); + let item = (int_data[width_idx] * int_data[height_idx]) as u64; + if item < min_item { + min_item = item; + min_item_idx = j; + } + } + old_data[min_item_idx].checked = true; + } + } + + ModelRc::new(VecModel::from(old_data)) +} + +fn select_by_size_date(model: ModelRc, active_tab: CurrentTab, biggest_newest: bool, size: bool) -> ModelRc { + let is_header_mode = get_is_header_mode(active_tab); + assert!(is_header_mode); // non header modes not really have reason to use this function + + let mut old_data = model.iter().collect::>(); + let headers_idx = find_header_idx_and_deselect_all(&mut old_data); + let item_idx = if size { + get_int_size_idx(active_tab) + } else { + get_int_modification_date_idx(active_tab) + }; + + if biggest_newest { + for i in 0..(headers_idx.len() - 1) { + let mut max_item = 0; + let mut max_item_idx = 0; + #[allow(clippy::needless_range_loop)] + for j in (headers_idx[i] + 1)..headers_idx[i + 1] { + let int_data = old_data[j].val_int.iter().collect::>(); + let item = connect_i32_into_u64(int_data[item_idx], int_data[item_idx + 1]); + if item > max_item { + max_item = item; + max_item_idx = j; + } + } + old_data[max_item_idx].checked = true; + } + } else { + for i in 0..(headers_idx.len() - 1) { + let mut min_item = u64::MAX; + let mut min_item_idx = 0; + #[allow(clippy::needless_range_loop)] + for j in (headers_idx[i] + 1)..headers_idx[i + 1] { + let int_data = old_data[j].val_int.iter().collect::>(); + let item = connect_i32_into_u64(int_data[item_idx], int_data[item_idx + 1]); + if item < min_item { + min_item = item; + min_item_idx = j; + } + } + old_data[min_item_idx].checked = true; + } + } + + ModelRc::new(VecModel::from(old_data)) +} + +fn select_all(model: ModelRc) -> ModelRc { + let mut old_data = model.iter().collect::>(); + for x in &mut old_data { + if !x.header_row { + x.checked = true; + } + } + ModelRc::new(VecModel::from(old_data)) +} + +fn deselect_all(model: ModelRc) -> ModelRc { + let mut old_data = model.iter().collect::>(); + old_data.iter_mut().for_each(|x| x.checked = false); + ModelRc::new(VecModel::from(old_data)) +} + +fn invert_selection(model: ModelRc) -> ModelRc { + let mut old_data = model.iter().collect::>(); + for x in &mut old_data { + if !x.header_row { + x.checked = !x.checked; + } + } + ModelRc::new(VecModel::from(old_data)) +} + +fn find_header_idx_and_deselect_all(old_data: &mut [MainListModel]) -> Vec { + let mut header_idx = old_data + .iter() + .enumerate() + .filter_map(|(idx, m)| if m.header_row { Some(idx) } else { None }) + .collect::>(); + header_idx.push(old_data.len()); + + for x in old_data.iter_mut() { + if !x.header_row { + x.checked = false; + } + } + header_idx +} diff --git a/krokiet/src/connect_show_preview.rs b/krokiet/src/connect_show_preview.rs index dc4942b..b516709 100644 --- a/krokiet/src/connect_show_preview.rs +++ b/krokiet/src/connect_show_preview.rs @@ -1,10 +1,13 @@ -use crate::{Callabler, GuiState, MainWindow}; -use czkawka_core::common::{get_dynamic_image_from_raw_image, IMAGE_RS_EXTENSIONS, RAW_IMAGE_EXTENSIONS}; +use std::path::Path; +use std::time::{Duration, Instant}; + use image::DynamicImage; use log::{debug, error}; use slint::ComponentHandle; -use std::path::Path; -use std::time::{Duration, Instant}; + +use czkawka_core::common::{get_dynamic_image_from_raw_image, IMAGE_RS_EXTENSIONS, RAW_IMAGE_EXTENSIONS}; + +use crate::{Callabler, CurrentTab, GuiState, MainWindow, Settings}; pub type ImageBufferRgba = image::ImageBuffer, Vec>; @@ -13,6 +16,21 @@ pub fn connect_show_preview(app: &MainWindow) { app.global::().on_load_image_preview(move |image_path| { let app = a.upgrade().unwrap(); + let settings = app.global::(); + let gui_state = app.global::(); + + let active_tab = gui_state.get_active_tab(); + + if active_tab == CurrentTab::SimilarImages && !settings.get_similar_images_show_image_preview() { + set_preview_visible(&gui_state, None); + return; + } + + // Do not load the same image again + if image_path == gui_state.get_preview_image_path() { + return; + } + let path = Path::new(image_path.as_str()); let res = load_image(path); @@ -22,20 +40,30 @@ pub fn connect_show_preview(app: &MainWindow) { let convert_time = start_timer_convert_time.elapsed(); let start_set_time = Instant::now(); - app.global::().set_preview_image(slint_image); + gui_state.set_preview_image(slint_image); let set_time = start_set_time.elapsed(); debug!( "Loading image took: {:?}, converting image took: {:?}, setting image took: {:?}", load_time, convert_time, set_time ); - app.global::().set_preview_visible(true); + set_preview_visible(&gui_state, Some(image_path.as_str())); } else { - app.global::().set_preview_visible(false); + set_preview_visible(&gui_state, None); } }); } +fn set_preview_visible(gui_state: &GuiState, preview: Option<&str>) { + if let Some(preview) = preview { + gui_state.set_preview_image_path(preview.into()); + gui_state.set_preview_visible(true); + } else { + gui_state.set_preview_image_path("".into()); + gui_state.set_preview_visible(false); + } +} + fn convert_into_slint_image(img: DynamicImage) -> slint::Image { let image_buffer: ImageBufferRgba = img.to_rgba8(); let buffer = slint::SharedPixelBuffer::::clone_from_slice(image_buffer.as_raw(), image_buffer.width(), image_buffer.height()); diff --git a/krokiet/src/connect_stop.rs b/krokiet/src/connect_stop.rs index 8a1c7fd..94b5a60 100644 --- a/krokiet/src/connect_stop.rs +++ b/krokiet/src/connect_stop.rs @@ -1,6 +1,7 @@ -use crate::MainWindow; use crossbeam_channel::Sender; +use crate::MainWindow; + pub fn connect_stop_button(app: &MainWindow, stop_sender: Sender<()>) { app.on_scan_stopping(move || { stop_sender.send(()).unwrap(); diff --git a/krokiet/src/connect_translation.rs b/krokiet/src/connect_translation.rs index bfbd821..338f52c 100644 --- a/krokiet/src/connect_translation.rs +++ b/krokiet/src/connect_translation.rs @@ -1,7 +1,9 @@ +use std::collections::HashMap; + +use slint::{ComponentHandle, Model}; + use crate::localizer_krokiet::LANGUAGE_LOADER_GUI; use crate::{Callabler, MainWindow}; -use slint::{ComponentHandle, Model}; -use std::collections::HashMap; pub fn connect_translations(app: &MainWindow) { app.global::().on_translate(move |text_to_translate, args| { diff --git a/krokiet/src/main.rs b/krokiet/src/main.rs index dd2faee..b6fd86f 100644 --- a/krokiet/src/main.rs +++ b/krokiet/src/main.rs @@ -17,38 +17,42 @@ #![allow(clippy::items_after_statements)] // Generated code #![allow(clippy::match_same_arms)] // Generated code -mod common; -mod connect_delete; -mod connect_directories_changes; -mod connect_open; -mod connect_progress_receiver; -mod connect_scan; -mod connect_show_preview; -mod connect_stop; -mod connect_translation; -mod localizer_krokiet; -mod set_initial_gui_info; -mod settings; +use std::rc::Rc; use crossbeam_channel::{unbounded, Receiver, Sender}; use slint::VecModel; -use std::rc::Rc; -// use std::rc::Rc; + +use czkawka_core::common::{print_version_mode, setup_logger}; +use czkawka_core::common_dir_traversal::ProgressData; use crate::connect_delete::connect_delete_button; -use crate::connect_open::connect_open_items; -use crate::connect_scan::connect_scan_button; - use crate::connect_directories_changes::connect_add_remove_directories; +use crate::connect_move::connect_move; +use crate::connect_open::connect_open_items; use crate::connect_progress_receiver::connect_progress_gathering; +use crate::connect_scan::connect_scan_button; +use crate::connect_select::{connect_select, connect_showing_proper_select_buttons}; use crate::connect_show_preview::connect_show_preview; use crate::connect_stop::connect_stop_button; use crate::connect_translation::connect_translations; use crate::set_initial_gui_info::set_initial_gui_infos; use crate::settings::{connect_changing_settings_preset, create_default_settings_files, load_settings_from_file, save_all_settings_to_file}; -use czkawka_core::common::{print_version_mode, setup_logger}; -use czkawka_core::common_dir_traversal::ProgressData; -// use slint::{ModelRc, VecModel}; + +mod common; +mod connect_delete; +mod connect_directories_changes; +mod connect_move; +mod connect_open; +mod connect_progress_receiver; +mod connect_scan; +mod connect_select; +mod connect_show_preview; +mod connect_stop; +mod connect_translation; +mod localizer_krokiet; +mod model_operations; +mod set_initial_gui_info; +mod settings; slint::include_modules!(); fn main() { @@ -80,6 +84,9 @@ fn main() { connect_show_preview(&app); connect_translations(&app); connect_changing_settings_preset(&app); + connect_select(&app); + connect_showing_proper_select_buttons(&app); + connect_move(&app); app.run().unwrap(); diff --git a/krokiet/src/model_operations.rs b/krokiet/src/model_operations.rs new file mode 100644 index 0000000..724855c --- /dev/null +++ b/krokiet/src/model_operations.rs @@ -0,0 +1,238 @@ +use crate::common::{get_str_name_idx, get_str_path_idx}; +use crate::{CurrentTab, MainListModel}; +use slint::{Model, ModelRc}; +use std::path::MAIN_SEPARATOR; + +pub fn deselect_all_items(items: &mut [MainListModel]) { + for item in items { + item.selected_row = false; + } +} + +#[allow(unused)] +pub fn select_all_items(items: &mut [MainListModel]) { + for item in items { + item.selected_row = true; + } +} + +pub fn collect_full_path_from_model(items: &[MainListModel], active_tab: CurrentTab) -> Vec { + let path_idx = get_str_path_idx(active_tab); + let name_idx = get_str_name_idx(active_tab); + items + .iter() + .map(|item| { + let path = item.val_str.iter().nth(path_idx).unwrap(); + let name = item.val_str.iter().nth(name_idx).unwrap(); + format!("{}{}{}", path, MAIN_SEPARATOR, name) + }) + .collect::>() +} +pub fn collect_path_name_from_model(items: &[MainListModel], active_tab: CurrentTab) -> Vec<(String, String)> { + let path_idx = get_str_path_idx(active_tab); + let name_idx = get_str_name_idx(active_tab); + items + .iter() + .map(|item| { + dbg!(item.val_str.iter().nth(path_idx).unwrap().to_string()); + dbg!(item.val_str.iter().nth(name_idx).unwrap().to_string()); + ( + item.val_str.iter().nth(path_idx).unwrap().to_string(), + item.val_str.iter().nth(name_idx).unwrap().to_string(), + ) + }) + .collect::>() +} + +pub fn filter_out_checked_items(items: &ModelRc, have_header: bool) -> (Vec, Vec) { + if cfg!(debug_assertions) { + check_if_header_is_checked(items); + check_if_header_is_selected_but_should_not_be(items, have_header); + } + + let (entries_to_delete, mut entries_left): (Vec<_>, Vec<_>) = items.iter().partition(|item| item.checked); + + // When have header, we must also throw out orphaned items - this needs to be + if have_header && !entries_left.is_empty() { + // First row must be header + assert!(entries_left[0].header_row); + + if entries_left.len() == 3 { + // First row is header, so if second or third is also header, then there is no enough items to fill model + if entries_left[1].header_row || entries_left[2].header_row { + entries_left = Vec::new(); + } + } else if entries_left.len() < 3 { + // Not have enough items to fill model + entries_left = Vec::new(); + } else { + let mut last_header = 0; + let mut new_items: Vec = Vec::new(); + for i in 1..entries_left.len() { + if entries_left[i].header_row { + if i - last_header > 2 { + new_items.extend(entries_left[last_header..i].iter().cloned()); + } + last_header = i; + } + } + if entries_left.len() - last_header > 2 { + new_items.extend(entries_left[last_header..].iter().cloned()); + } + + entries_left = new_items; + } + } + + (entries_to_delete, entries_left) +} + +// Function to verify if really headers are not checked +// Checked header is big bug +fn check_if_header_is_checked(items: &ModelRc) { + if cfg!(debug_assertions) { + for item in items.iter() { + if item.header_row { + assert!(!item.checked); + } + } + } +} + +// In some modes header should not be visible, but if are, then it is a bug +fn check_if_header_is_selected_but_should_not_be(items: &ModelRc, can_have_header: bool) { + if cfg!(debug_assertions) { + if !can_have_header { + for item in items.iter() { + assert!(!item.header_row); + } + } + } +} + +#[cfg(test)] +mod tests { + use slint::{Model, ModelRc, SharedString, VecModel}; + + use crate::model_operations::filter_out_checked_items; + use crate::MainListModel; + + #[test] + fn test_filter_out_checked_items_empty() { + let items: ModelRc = create_new_model(vec![]); + + let (to_delete, left) = filter_out_checked_items(&items, false); + assert!(to_delete.is_empty()); + assert!(left.is_empty()); + let (to_delete, left) = filter_out_checked_items(&items, true); + assert!(to_delete.is_empty()); + assert!(left.is_empty()); + } + #[test] + fn test_filter_out_checked_items_one_element_valid_normal() { + let items = create_new_model(vec![(false, false, false, vec![])]); + let (to_delete, left) = filter_out_checked_items(&items, false); + assert!(to_delete.is_empty()); + assert_eq!(left.len(), items.iter().count()); + } + + #[test] + fn test_filter_out_checked_items_one_element_valid_header() { + let items = create_new_model(vec![(false, true, false, vec![])]); + let (to_delete, left) = filter_out_checked_items(&items, true); + assert!(to_delete.is_empty()); + assert!(left.is_empty()); + } + + #[test] + #[should_panic] + fn test_filter_out_checked_items_one_element_invalid_normal() { + let items = create_new_model(vec![(false, true, false, vec![])]); + filter_out_checked_items(&items, false); + } + #[test] + #[should_panic] + fn test_filter_out_checked_items_one_element_invalid_header() { + let items = create_new_model(vec![(false, false, false, vec![])]); + filter_out_checked_items(&items, true); + } + + #[test] + fn test_filter_out_checked_items_multiple_element_valid_normal() { + let items = create_new_model(vec![ + (false, false, false, vec!["1"]), + (false, false, false, vec!["2"]), + (true, false, false, vec!["3"]), + (true, false, false, vec!["4"]), + (false, false, false, vec!["5"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, false); + let to_delete_data = get_single_data_str_from_model(&to_delete); + let left_data = get_single_data_str_from_model(&left); + + assert_eq!(to_delete_data, vec!["3", "4"]); + assert_eq!(left_data, vec!["1", "2", "5"]); + } + + #[test] + fn test_filter_out_checked_items_multiple_element_valid_header() { + let items = create_new_model(vec![ + (false, true, false, vec!["1"]), + (false, false, false, vec!["2"]), + (true, false, false, vec!["3"]), + (false, true, false, vec!["4"]), + (false, false, false, vec!["5"]), + (false, true, false, vec!["6"]), + (false, false, false, vec!["7"]), + (false, false, false, vec!["8"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, true); + let to_delete_data = get_single_data_str_from_model(&to_delete); + let left_data = get_single_data_str_from_model(&left); + + assert_eq!(to_delete_data, vec!["3"]); + assert_eq!(left_data, vec!["6", "7", "8"]); + } + + #[test] + fn test_filter_out_checked_items_multiple2_element_valid_header() { + let items = create_new_model(vec![ + (false, true, false, vec!["1"]), + (false, false, false, vec!["2"]), + (true, false, false, vec!["3"]), + (false, false, false, vec!["4"]), + (false, false, false, vec!["5"]), + (false, false, false, vec!["6"]), + (false, true, false, vec!["7"]), + (false, false, false, vec!["8"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, true); + let to_delete_data = get_single_data_str_from_model(&to_delete); + let left_data = get_single_data_str_from_model(&left); + + assert_eq!(to_delete_data, vec!["3"]); + assert_eq!(left_data, vec!["1", "2", "4", "5", "6"]); + } + + fn get_single_data_str_from_model(model: &[MainListModel]) -> Vec { + let mut d = model.iter().map(|item| item.val_str.iter().next().unwrap().to_string()).collect::>(); + d.sort(); + d + } + + fn create_new_model(items: Vec<(bool, bool, bool, Vec<&'static str>)>) -> ModelRc { + let model = VecModel::default(); + for item in items { + let all_items: Vec = item.3.iter().map(|item| (*item).into()).collect::>(); + let all_items = VecModel::from(all_items); + model.push(MainListModel { + checked: item.0, + header_row: item.1, + selected_row: item.2, + val_str: ModelRc::new(all_items), + val_int: ModelRc::new(VecModel::default()), + }); + } + ModelRc::new(model) + } +} diff --git a/krokiet/src/set_initial_gui_info.rs b/krokiet/src/set_initial_gui_info.rs index 5694852..5543fe9 100644 --- a/krokiet/src/set_initial_gui_info.rs +++ b/krokiet/src/set_initial_gui_info.rs @@ -1,13 +1,14 @@ -use czkawka_core::common::get_available_threads; use slint::{ComponentHandle, SharedString, VecModel}; +use czkawka_core::common::get_all_available_threads; + use crate::settings::{ALLOWED_HASH_SIZE_VALUES, ALLOWED_HASH_TYPE_VALUES, ALLOWED_RESIZE_ALGORITHM_VALUES}; use crate::{GuiState, MainWindow, Settings}; // Some info needs to be send to gui at the start like available thread number in OS. // pub fn set_initial_gui_infos(app: &MainWindow) { - let threads = get_available_threads(); + let threads = get_all_available_threads(); let settings = app.global::(); app.global::().set_maximum_threads(threads as f32); diff --git a/krokiet/src/settings.rs b/krokiet/src/settings.rs index 81afbd8..7beb513 100644 --- a/krokiet/src/settings.rs +++ b/krokiet/src/settings.rs @@ -9,10 +9,10 @@ use log::{debug, error, info, warn}; use serde::{Deserialize, Serialize}; use slint::{ComponentHandle, Model, ModelRc}; -use czkawka_core::common::{get_available_threads, set_number_of_threads}; +use czkawka_core::common::{get_all_available_threads, set_number_of_threads}; use czkawka_core::common_items::{DEFAULT_EXCLUDED_DIRECTORIES, DEFAULT_EXCLUDED_ITEMS}; -use crate::common::{create_string_standard_list_view_from_pathbuf, create_vec_model_from_vec_string}; +use crate::common::{create_excluded_directories_model_from_pathbuf, create_included_directories_model_from_pathbuf, create_vec_model_from_vec_string}; use crate::{Callabler, GuiState, MainWindow, Settings}; pub const DEFAULT_MINIMUM_SIZE_KB: i32 = 16; @@ -44,12 +44,16 @@ pub const ALLOWED_HASH_TYPE_VALUES: &[(&str, &str, HashAlg)] = &[ pub struct SettingsCustom { #[serde(default = "default_included_directories")] pub included_directories: Vec, + #[serde(default)] + pub included_directories_referenced: Vec, #[serde(default = "default_excluded_directories")] pub excluded_directories: Vec, #[serde(default = "default_excluded_items")] pub excluded_items: String, #[serde(default)] pub allowed_extensions: String, + #[serde(default)] + pub excluded_extensions: String, #[serde(default = "minimum_file_size")] pub minimum_file_size: i32, #[serde(default = "maximum_file_size")] @@ -236,7 +240,7 @@ pub fn load_settings_from_file(app: &MainWindow) { } } base_settings.default_preset = max(min(base_settings.default_preset, 9), 0); - custom_settings.thread_number = max(min(custom_settings.thread_number, get_available_threads() as i32), 0); + custom_settings.thread_number = max(min(custom_settings.thread_number, get_all_available_threads() as i32), 0); // Ended validating set_settings_to_gui(app, &custom_settings); @@ -259,7 +263,7 @@ pub fn save_base_settings_to_file(app: &MainWindow) { pub fn save_custom_settings_to_file(app: &MainWindow) { let current_item = app.global::().get_settings_preset_idx(); - let result = save_data_to_file(get_config_file(current_item + 1), &collect_settings(app)); + let result = save_data_to_file(get_config_file(current_item), &collect_settings(app)); if let Err(e) = result { error!("{e}"); @@ -348,15 +352,16 @@ pub fn set_settings_to_gui(app: &MainWindow, custom_settings: &SettingsCustom) { let settings = app.global::(); // Included directories - let included_directories = create_string_standard_list_view_from_pathbuf(&custom_settings.included_directories); - settings.set_included_directories(included_directories); + let included_directories = create_included_directories_model_from_pathbuf(&custom_settings.included_directories, &custom_settings.included_directories_referenced); + settings.set_included_directories_model(included_directories); // Excluded directories - let excluded_directories = create_string_standard_list_view_from_pathbuf(&custom_settings.excluded_directories); - settings.set_excluded_directories(excluded_directories); + let excluded_directories = create_excluded_directories_model_from_pathbuf(&custom_settings.excluded_directories); + settings.set_excluded_directories_model(excluded_directories); settings.set_excluded_items(custom_settings.excluded_items.clone().into()); settings.set_allowed_extensions(custom_settings.allowed_extensions.clone().into()); + settings.set_excluded_extensions(custom_settings.excluded_extensions.clone().into()); settings.set_minimum_file_size(custom_settings.minimum_file_size.to_string().into()); settings.set_maximum_file_size(custom_settings.maximum_file_size.to_string().into()); settings.set_use_cache(custom_settings.use_cache); @@ -430,14 +435,20 @@ pub fn set_settings_to_gui(app: &MainWindow, custom_settings: &SettingsCustom) { pub fn collect_settings(app: &MainWindow) -> SettingsCustom { let settings = app.global::(); - let included_directories = settings.get_included_directories(); - let included_directories = included_directories.iter().map(|x| PathBuf::from(x.text.as_str())).collect::>(); + let included_directories_model = settings.get_included_directories_model(); + let included_directories = included_directories_model.iter().map(|model| PathBuf::from(model.path.as_str())).collect::>(); + let included_directories_referenced = included_directories_model + .iter() + .filter(|model| model.referenced_folder) + .map(|model| PathBuf::from(model.path.as_str())) + .collect::>(); - let excluded_directories = settings.get_excluded_directories(); - let excluded_directories = excluded_directories.iter().map(|x| PathBuf::from(x.text.as_str())).collect::>(); + let excluded_directories_model = settings.get_excluded_directories_model(); + let excluded_directories = excluded_directories_model.iter().map(|model| PathBuf::from(model.path.as_str())).collect::>(); let excluded_items = settings.get_excluded_items().to_string(); let allowed_extensions = settings.get_allowed_extensions().to_string(); + let excluded_extensions = settings.get_excluded_extensions().to_string(); let minimum_file_size = settings.get_minimum_file_size().parse::().unwrap_or(DEFAULT_MINIMUM_SIZE_KB); let maximum_file_size = settings.get_maximum_file_size().parse::().unwrap_or(DEFAULT_MAXIMUM_SIZE_KB); @@ -478,9 +489,11 @@ pub fn collect_settings(app: &MainWindow) -> SettingsCustom { let similar_images_sub_similarity = settings.get_similar_images_sub_current_similarity().round() as i32; SettingsCustom { included_directories, + included_directories_referenced, excluded_directories, excluded_items, allowed_extensions, + excluded_extensions, minimum_file_size, maximum_file_size, recursive_search, diff --git a/krokiet/ui/action_buttons.slint b/krokiet/ui/action_buttons.slint index 996f6cb..f00ed0c 100644 --- a/krokiet/ui/action_buttons.slint +++ b/krokiet/ui/action_buttons.slint @@ -20,11 +20,13 @@ export component VisibilityButton inherits Button { export component ActionButtons inherits HorizontalLayout { callback scan_stopping; callback scan_starting(CurrentTab); + callback show_select_popup(length, length); + callback show_remove_popup(); + callback request_folder_to_move(); in-out property bottom_panel_visibility: BottomPanelVisibility.Directories; in-out property stop_requested: false; in-out property scanning; in-out property lists_enabled: GuiState.active_tab != CurrentTab.Settings; - // in-out property <> out property name; height: 30px; spacing: 4px; @@ -57,35 +59,30 @@ export component ActionButtons inherits HorizontalLayout { horizontal-stretch: 0.5; } + move_button := Button { + height: parent.height; + enabled: !scanning && lists_enabled; + text: "Move"; + clicked => { + request_folder_to_move(); + } + } + + select_button := Button { + height: parent.height; + enabled: !scanning && lists_enabled; + text: "Select"; + clicked => { + show_select_popup(self.x + self.width / 2, self.y + parent.y); + } + } + delete_button := Button { height: parent.height; enabled: !scanning && lists_enabled; text: "Delete"; clicked => { - Callabler.delete_selected_items(); - } - } - - popup_item := PopupWindow { - height: root.height; - width: root.width; - close-on-click: true; - VerticalLayout { - for i[idx] in ["A","B","C"]: Rectangle { - background: red; - } - } - } - - select_button := Button { - visible: false; - height: parent.height; - enabled: !scanning && lists_enabled; - text: "Select"; - clicked => { - debug("Selected"); - popup_item.show(); - // Callabler.select_items(); + show_remove_popup(); } } diff --git a/krokiet/ui/bottom_panel.slint b/krokiet/ui/bottom_panel.slint index f4872f5..3e0bd6a 100644 --- a/krokiet/ui/bottom_panel.slint +++ b/krokiet/ui/bottom_panel.slint @@ -4,10 +4,13 @@ import {Settings} from "settings.slint"; import {BottomPanelVisibility} from "common.slint"; import {Callabler} from "callabler.slint"; import {GuiState} from "gui_state.slint"; +import {IncludedDirectories, ExcludedDirectories} from "included_directories.slint"; component DirectoriesPanel inherits HorizontalLayout { callback folder_choose_requested(bool); callback show_manual_add_dialog(bool); + + spacing: 5px; // Included directories VerticalLayout { horizontal-stretch: 0.0; @@ -22,7 +25,7 @@ component DirectoriesPanel inherits HorizontalLayout { Button { text: "Remove"; clicked => { - Callabler.remove_item_directories(true, included-list.current-item); + Callabler.remove_item_directories(true); } } @@ -46,9 +49,7 @@ component DirectoriesPanel inherits HorizontalLayout { } } - included_list := StandardListView { - model: Settings.included-directories; - } + included_list := IncludedDirectories { } } // Excluded directories @@ -65,7 +66,7 @@ component DirectoriesPanel inherits HorizontalLayout { Button { text: "Remove"; clicked => { - Callabler.remove_item_directories(false, excluded-list.current-item); + Callabler.remove_item_directories(false); } } @@ -89,9 +90,7 @@ component DirectoriesPanel inherits HorizontalLayout { } } - excluded_list := StandardListView { - model: Settings.excluded-directories; - } + excluded_list := ExcludedDirectories { } } } diff --git a/krokiet/ui/callabler.slint b/krokiet/ui/callabler.slint index 3d15168..33cd9a6 100644 --- a/krokiet/ui/callabler.slint +++ b/krokiet/ui/callabler.slint @@ -1,13 +1,15 @@ +import { SelectMode } from "common.slint"; + export global Callabler { // Bottom panel operations - callback remove_item_directories(bool, int); + callback remove_item_directories(bool); callback added_manual_directories(bool, string); // Right click or middle click opener callback item_opened(string); callback delete_selected_items(); - // callback (); + callback select_items(SelectMode); // Preview callback load_image_preview(string); @@ -18,6 +20,10 @@ export global Callabler { callback load_current_preset(); callback reset_current_preset(); + callback tab_changed(); + + callback move_items(bool, bool, string); + // Translations pure callback translate(string, [{key: string, value: string}]) -> string; diff --git a/krokiet/ui/common.slint b/krokiet/ui/common.slint index f9fa58d..97b41b4 100644 --- a/krokiet/ui/common.slint +++ b/krokiet/ui/common.slint @@ -20,11 +20,40 @@ export struct MainListModel { checked: bool, header_row: bool, selected_row: bool, - val: [string] + val_str: [string], + val_int: [int] } export enum BottomPanelVisibility { NotVisible, TextErrors, Directories +} + +export struct IncludedDirectoriesModel { + path: string, + referenced_folder: bool, + selected_row: bool, +} + +export struct ExcludedDirectoriesModel { + path: string, + selected_row: bool, +} + +export enum SelectMode { + SelectAll, + UnselectAll, + InvertSelection, + SelectTheBiggestSize, + SelectTheBiggestResolution, + SelectTheSmallestSize, + SelectTheSmallestResolution, + SelectNewest, + SelectOldest, +} + +export struct SelectModel { + data: SelectMode, + name: string } \ No newline at end of file diff --git a/krokiet/ui/gui_state.slint b/krokiet/ui/gui_state.slint index 97570ba..84d38eb 100644 --- a/krokiet/ui/gui_state.slint +++ b/krokiet/ui/gui_state.slint @@ -1,6 +1,8 @@ import {CurrentTab} from "common.slint"; +import {SelectModel, SelectMode} from "common.slint"; -// State to show +// State Gui state that shows the current state of the GUI +// It extends Settings global state with settings that are not saved to the settings file export global GuiState { in-out property app_width; in-out property app_height; @@ -8,6 +10,7 @@ export global GuiState { in-out property info_text: "Nothing to report"; in-out property preview_visible; in-out property preview_image; + in-out property preview_image_path; in-out property maximum_threads: 40; @@ -16,4 +19,5 @@ export global GuiState { in-out property available_subsettings: active_tab == CurrentTab.SimilarImages; in-out property active_tab: CurrentTab.EmptyFiles; + in-out property <[SelectModel]> select_results_list: [{data: SelectMode.SelectAll, name: "Select All"}, {data: SelectMode.UnselectAll, name: "Deselect All"}, {data: SelectMode.SelectTheSmallestResolution, name: "Select the smallest resolution"}]; } diff --git a/krokiet/ui/included_directories.slint b/krokiet/ui/included_directories.slint index d021879..5ee2881 100644 --- a/krokiet/ui/included_directories.slint +++ b/krokiet/ui/included_directories.slint @@ -1,25 +1,24 @@ import {Button, StandardListView, VerticalBox, ListView, ScrollView, TextEdit, CheckBox} from "std-widgets.slint"; import {Callabler} from "callabler.slint"; +import {IncludedDirectoriesModel, ExcludedDirectoriesModel} from "common.slint"; +import {ColorPalette} from "color_palette.slint"; +import {Settings} from "settings.slint"; -export struct IncludedDirectoriesModel { - path: string, - referended_folder: bool, -} +export component IncludedDirectories { + in-out property <[IncludedDirectoriesModel]> model <=> Settings.included_directories_model; + in-out property current_index <=> Settings.included_directories_model_selected_idx; -export component InlcudedDirectories { - in-out property <[IncludedDirectoriesModel]> model: [{path: "/home/path", referended_folder: false}]; - in-out property current_index: -1; - - in-out property size_referenced_folder: 40px; + in-out property size_referenced_folder: 33px; min-width: 50px; VerticalLayout { HorizontalLayout { spacing: 5px; Text { - text: "Referenced folder"; + text: "Ref"; width: size_referenced_folder; + horizontal-alignment: center; } Text{ horizontal-stretch: 1.0; @@ -27,61 +26,26 @@ export component InlcudedDirectories { } } ListView { - for data in model : Rectangle { - height: 30px; - border_radius: 5px; - width: parent.width; - HorizontalLayout { - spacing: 5px; - width: parent.width; - - CheckBox { - checked: data.referended_folder; - width: size_referenced_folder; - } - Text { - horizontal-stretch: 1.0; - text: data.path; - vertical-alignment: center; - } - } - } - } - } -} - -export component ExcludeDirectories { - in-out property <[string]> model: ["/home/path"]; - in-out property current_index: -1; - private property event; - - min-width: 50px; - VerticalLayout { - HorizontalLayout { - spacing: 5px; - Text { - text: "Path"; - } - } - ListView { - for data[idx] in model : Rectangle { + for r[idx] in model : Rectangle { height: 30px; border_radius: 5px; width: parent.width; + background: touch-area.has-hover ? (r.selected_row ? ColorPalette.list-view-normal-selected-header : ColorPalette.list_view_normal_color) : (r.selected_row ? ColorPalette.list-view-normal-selected-header : ColorPalette.list_view_normal_color); touch_area := TouchArea { clicked => { if (current_index == -1) { - + r.selected_row = true; + } else { + if (current_index != idx) { + model[current_index].selected_row = false; + } + r.selected_row = true; } + current_index = idx; } double-clicked => { - if (event.button == PointerEventButton.middle && event.kind == PointerEventKind.up) { - Callabler.item_opened(data) - } - } - pointer-event(event) => { - root.event = event; + Callabler.item_opened(r.path); } } @@ -89,9 +53,16 @@ export component ExcludeDirectories { spacing: 5px; width: parent.width; + CheckBox { + checked: r.referenced_folder; + toggled => { + model[idx].referenced_folder = self.checked; + } + width: size_referenced_folder; + } Text { horizontal-stretch: 1.0; - text: data; + text: r.path; vertical-alignment: center; } } @@ -99,3 +70,53 @@ export component ExcludeDirectories { } } } + +export component ExcludedDirectories { + in-out property <[ExcludedDirectoriesModel]> model <=> Settings.excluded_directories_model; + in-out property current_index <=> Settings.excluded_directories_model_selected_idx; + + min-width: 50px; + VerticalLayout { + HorizontalLayout { + spacing: 5px; + padding-left: 5px; + Text { + text: "Path"; + } + } + ListView { + for r[idx] in model : Rectangle { + height: 30px; + border_radius: 5px; + width: parent.width; + + background: touch-area.has-hover ? (r.selected_row ? ColorPalette.list-view-normal-selected-header : ColorPalette.list_view_normal_color) : (r.selected_row ? ColorPalette.list-view-normal-selected-header : ColorPalette.list_view_normal_color); + + touch_area := TouchArea { + clicked => { + if (current_index == -1) { + r.selected_row = true; + } else { + if (current_index != idx) { + model[current_index].selected_row = false; + } + r.selected_row = true; + } + current_index = idx; + } + double-clicked => { + Callabler.item_opened(r.path); + } + } + + Text { + x: 5px; + width: parent.width; + height: parent.height; + text: r.path; + vertical-alignment: center; + } + } + } + } +} diff --git a/krokiet/ui/left_side_panel.slint b/krokiet/ui/left_side_panel.slint index 9e92d2b..8d8e349 100644 --- a/krokiet/ui/left_side_panel.slint +++ b/krokiet/ui/left_side_panel.slint @@ -2,6 +2,7 @@ import { Button, VerticalBox , HorizontalBox, TabWidget, ListView, StandardListV import {CurrentTab} from "common.slint"; import {ColorPalette} from "color_palette.slint"; import {GuiState} from "gui_state.slint"; +import {Callabler} from "callabler.slint"; component TabItem { in property scanning; @@ -19,6 +20,7 @@ component TabItem { return; } GuiState.active_tab = root.curr-tab; + Callabler.tab_changed(); changed_current_tab(); } } @@ -130,6 +132,7 @@ export component LeftSidePanel { icon: @image-url("../icons/settings.svg"); clicked => { GuiState.active_tab = CurrentTab.Settings; + Callabler.tab_changed(); root.changed_current_tab(); } } diff --git a/krokiet/ui/main_lists.slint b/krokiet/ui/main_lists.slint index fd68a2f..6ce0d48 100644 --- a/krokiet/ui/main_lists.slint +++ b/krokiet/ui/main_lists.slint @@ -8,10 +8,10 @@ import {GuiState} from "gui_state.slint"; export component MainList { in-out property <[MainListModel]> empty_folder_model: [ - {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + {checked: false, selected_row: false, header_row: true, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: true, selected_row: false, header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[MainListModel]> empty_files_model; in-out property <[MainListModel]> similar_images_model; diff --git a/krokiet/ui/main_window.slint b/krokiet/ui/main_window.slint index c22e210..e825c34 100644 --- a/krokiet/ui/main_window.slint +++ b/krokiet/ui/main_window.slint @@ -13,7 +13,9 @@ import {ColorPalette} from "color_palette.slint"; import {GuiState} from "gui_state.slint"; import { Preview } from "preview.slint"; import {PopupNewDirectories} from "popup_new_directories.slint"; -import { PopupSelect } from "popup_select.slint"; +import {PopupDelete} from "popup_delete.slint"; +import {PopupMoveFolders} from "popup_move_folders.slint"; +import { PopupSelectResults } from "popup_select_results.slint"; import { ToolSettings } from "tool_settings.slint"; export {Settings, Callabler, GuiState} @@ -23,6 +25,8 @@ export component MainWindow inherits Window { callback scan_starting(CurrentTab); callback folder_choose_requested(bool); callback scan_ended(string); + callback show_move_folders_dialog(string); + callback folders_move_choose_requested(); min-width: 300px; preferred-width: 800px; @@ -38,19 +42,19 @@ export component MainWindow inherits Window { step_name: "Cache", }; in-out property <[MainListModel]> empty_folder_model: [ - {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + {checked: false, selected_row: false, header_row: true, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: true, selected_row: false, header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[MainListModel]> empty_files_model: [ - {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + {checked: false, selected_row: false, header_row: true, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: true, selected_row: false, header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[MainListModel]> similar_images_model: []; - + VerticalBox { HorizontalBox { vertical-stretch: 1.0; @@ -117,6 +121,17 @@ export component MainWindow inherits Window { text_summary_text = "Searching..."; root.scan_starting(item); } + show_select_popup(x_offset, y_offset) => { + select_popup_window.x_offset = x_offset; + select_popup_window.y_offset = y_offset; + select_popup_window.show_popup(); + } + request_folder_to_move => { + folders_move_choose_requested(); + } + show_remove_popup => { + delete_popup_window.show_popup(); + } } text_summary := LineEdit { @@ -142,11 +157,37 @@ export component MainWindow inherits Window { width: root.width; } - // select_popup_window := PopupSelect { - // height: root.height; - // width: root.width; - // } + select_popup_window := PopupSelectResults { + property x_offset: 0; + property y_offset: 0; + + x: parent.x + x_offset - self.item_width / 2.0; + y: parent.y + y_offset - self.all_items_height - 5px; + + height: root.height; + width: root.width; + } + delete_popup_window := PopupDelete { + height: root.height; + width: root.width; + + x: parent.x + (root.width - self.popup_width) / 2.0; + y: parent.y + (parent.height - self.popup_height) / 2.0; + } + + move_popup_window := PopupMoveFolders { + height: root.height; + width: root.width; + + x: parent.x + (root.width - self.popup_width) / 2.0; + y: parent.y + (parent.height - self.popup_height) / 2.0; + } + + show_move_folders_dialog(folder_name) => { + move_popup_window.folder_name = folder_name; + move_popup_window.show_popup(); + } scan_ended(scan_text) => { text_summary_text = scan_text; diff --git a/krokiet/ui/popup_delete.slint b/krokiet/ui/popup_delete.slint new file mode 100644 index 0000000..d4988d3 --- /dev/null +++ b/krokiet/ui/popup_delete.slint @@ -0,0 +1,73 @@ +import { Button, VerticalBox ,TextEdit, HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, LineEdit} from "std-widgets.slint"; +import {SelectableTableView} from "selectable_tree_view.slint"; +import {LeftSidePanel} from "left_side_panel.slint"; +import {MainList} from "main_lists.slint"; +import {CurrentTab, ProgressToSend} from "common.slint"; +import { ActionButtons } from "action_buttons.slint"; +import { Progress } from "progress.slint"; +import {MainListModel, SelectMode, SelectModel} from "common.slint"; +import {Settings} from "settings.slint"; +import {Callabler} from "callabler.slint"; +import { BottomPanel } from "bottom_panel.slint"; +import {ColorPalette} from "color_palette.slint"; +import {GuiState} from "gui_state.slint"; +import { Preview } from "preview.slint"; + +export component PopupDelete inherits Rectangle { + out property popup_width: 350px; + out property popup_height: 150px; + callback show_popup(); + + popup_window := PopupWindow { + width: popup_width; + height: popup_height; + + close-on-click: true; + Rectangle { + width: parent.width; + height: parent.height; + border-radius: 5px; + background: ColorPalette.popup_background; + VerticalLayout { + Text { + vertical-stretch: 0.0; + text: "Delete items"; + vertical-alignment: center; + horizontal-alignment: center; + font-size: 13px; + padding: 10px; + } + Text { + vertical-stretch: 1.0; + text: "Are you sure you want to delete the selected items?"; + vertical-alignment: center; + horizontal-alignment: center; + font-size: 13px; + padding: 10px; + } + HorizontalLayout { + Button { + text: "Yes"; + clicked => { + popup_window.close(); + Callabler.delete_selected_items(); + } + } + Rectangle { + + } + Button { + text: "No"; + clicked => { + popup_window.close(); + } + } + } + } + } + } + + show_popup() => { + popup_window.show(); + } +} \ No newline at end of file diff --git a/krokiet/ui/popup_move_folders.slint b/krokiet/ui/popup_move_folders.slint new file mode 100644 index 0000000..56861b2 --- /dev/null +++ b/krokiet/ui/popup_move_folders.slint @@ -0,0 +1,94 @@ +import { Button, VerticalBox ,TextEdit, HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, LineEdit} from "std-widgets.slint"; +import {SelectableTableView} from "selectable_tree_view.slint"; +import {LeftSidePanel} from "left_side_panel.slint"; +import {MainList} from "main_lists.slint"; +import {CurrentTab, ProgressToSend} from "common.slint"; +import { ActionButtons } from "action_buttons.slint"; +import { Progress } from "progress.slint"; +import {MainListModel, SelectMode, SelectModel} from "common.slint"; +import {Settings} from "settings.slint"; +import {Callabler} from "callabler.slint"; +import { BottomPanel } from "bottom_panel.slint"; +import {ColorPalette} from "color_palette.slint"; +import {GuiState} from "gui_state.slint"; +import { Preview } from "preview.slint"; + +export component PopupMoveFolders inherits Rectangle { + out property popup_width: 500px; + out property popup_height: 150px; + in-out property folder_name: ""; + callback show_popup(); + + popup_window := PopupWindow { + width: popup_width; + height: popup_height; + + close-on-click: false; + Rectangle { + width: parent.width; + height: parent.height; + border-radius: 5px; + background: ColorPalette.popup_background; + VerticalLayout { + Text { + vertical-stretch: 0.0; + min-height: 30px; + text: "Moving files"; + vertical-alignment: top; + horizontal-alignment: center; + font-size: 13px; + } + Text { + vertical-stretch: 1.0; + text: "Moving entries to folder\n" + folder_name + "\nAre you want to continue?"; + vertical-alignment: center; + horizontal-alignment: center; + font-size: 13px; + padding: 10px; + } + + VerticalLayout { + HorizontalLayout { + alignment: center; + copy_checkbox := CheckBox { + text: "Copy files instead of moving"; + } + } + HorizontalLayout { + alignment: center; + preserve_folder_checkbox := CheckBox { + text: "Preserve folder structure"; + } + } + } + + HorizontalLayout { + Button { + text: "Yes"; + clicked => { + popup_window.close(); + Callabler.move_items(preserve_folder_checkbox.checked, copy_checkbox.checked, folder_name); + } + } + Rectangle { + + } + Button { + text: "No"; + clicked => { + popup_window.close(); + } + } + } + } + } + } + + init => { + show_popup(); + } + + show_popup() => { + popup_window.show(); + } +} \ No newline at end of file diff --git a/krokiet/ui/popup_select.slint b/krokiet/ui/popup_select.slint deleted file mode 100644 index 394e9d4..0000000 --- a/krokiet/ui/popup_select.slint +++ /dev/null @@ -1,74 +0,0 @@ -import { Button, VerticalBox ,TextEdit, HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, LineEdit} from "std-widgets.slint"; -import {SelectableTableView} from "selectable_tree_view.slint"; -import {LeftSidePanel} from "left_side_panel.slint"; -import {MainList} from "main_lists.slint"; -import {CurrentTab, ProgressToSend} from "common.slint"; -import { ActionButtons } from "action_buttons.slint"; -import { Progress } from "progress.slint"; -import {MainListModel} from "common.slint"; -import {Settings} from "settings.slint"; -import {Callabler} from "callabler.slint"; -import { BottomPanel } from "bottom_panel.slint"; -import {ColorPalette} from "color_palette.slint"; -import {GuiState} from "gui_state.slint"; -import { Preview } from "preview.slint"; - -export component PopupSelect inherits Rectangle { - callback show_popup(); - - popup_window := PopupWindow { - width: root.width; - height: root.height; - - property included_directories; - private property text_data; - close-on-click: false; - HorizontalLayout { - alignment: LayoutAlignment.center; - VerticalLayout { - alignment: LayoutAlignment.center; - Rectangle { - clip: true; - width: root.width - 20px; - height: root.height - 20px; - border-radius: 20px; - background: ColorPalette.popup_background; - VerticalLayout { - Text { - text: "Please add directories one per line"; - horizontal-alignment: TextHorizontalAlignment.center; - } - - TextEdit { - vertical-stretch: 1.0; - text <=> text-data; - } - - HorizontalLayout { - min-height: 20px; - Button { - enabled: text-data != ""; - text: "OK"; - clicked => { - Callabler.added_manual_directories(GuiState.choosing_include_directories, text_data); - popup_window.close(); - } - } - - Button { - text: "Cancel"; - clicked => { - popup_window.close(); - } - } - } - } - } - } - } - } - - show_popup() => { - popup_window.show(); - } -} \ No newline at end of file diff --git a/krokiet/ui/popup_select_results.slint b/krokiet/ui/popup_select_results.slint new file mode 100644 index 0000000..35081b6 --- /dev/null +++ b/krokiet/ui/popup_select_results.slint @@ -0,0 +1,51 @@ +import { Button, VerticalBox ,TextEdit, HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, LineEdit} from "std-widgets.slint"; +import {SelectableTableView} from "selectable_tree_view.slint"; +import {LeftSidePanel} from "left_side_panel.slint"; +import {MainList} from "main_lists.slint"; +import {CurrentTab, ProgressToSend} from "common.slint"; +import { ActionButtons } from "action_buttons.slint"; +import { Progress } from "progress.slint"; +import {MainListModel, SelectMode, SelectModel} from "common.slint"; +import {Settings} from "settings.slint"; +import {Callabler} from "callabler.slint"; +import { BottomPanel } from "bottom_panel.slint"; +import {ColorPalette} from "color_palette.slint"; +import {GuiState} from "gui_state.slint"; +import { Preview } from "preview.slint"; + +export component PopupSelectResults inherits Rectangle { + callback show_popup(); + property <[SelectModel]> model: GuiState.select_results_list; + property item_height: 30px; + out property item_width: 200px; + out property all_items_height: item_height * model.length; + + popup_window := PopupWindow { + width: item_width; + height: all_items_height; + + close-on-click: true; + Rectangle { + width: parent.width; + height: parent.height; + border-radius: 5px; + background: ColorPalette.popup_background; + VerticalLayout { + for i in model: Button { + text: i.name; + height: item_height; + width: item_width; + + clicked => { + Callabler.select_items(i.data); + popup_window.close(); + } + } + } + } + } + + show_popup() => { + popup_window.show(); + } +} \ No newline at end of file diff --git a/krokiet/ui/selectable_tree_view.slint b/krokiet/ui/selectable_tree_view.slint index 08c1169..8a0c730 100644 --- a/krokiet/ui/selectable_tree_view.slint +++ b/krokiet/ui/selectable_tree_view.slint @@ -9,10 +9,10 @@ export component SelectableTableView inherits Rectangle { callback item_opened(string); in property <[string]> columns; in-out property <[MainListModel]> values: [ - {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , - {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + {checked: false, selected_row: false, header_row: true, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: false, selected_row: false, header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , + {checked: true, selected_row: false, header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[length]> column_sizes: [30px, 80px, 150px, 160px]; private property column_number: column-sizes.length + 1; @@ -89,24 +89,24 @@ export component SelectableTableView inherits Rectangle { } if (root.selected_item != -1) { - Callabler.load_image_preview(r.val[root.parentPathIdx - 1] + "/" + r.val[root.fileNameIdx - 1]); + Callabler.load_image_preview(r.val_str[root.parentPathIdx - 1] + "/" + r.val_str[root.fileNameIdx - 1]); } else { GuiState.preview_visible = false; } } } double-clicked => { - Callabler.item_opened(r.val[root.parentPathIdx - 1] + "/" + r.val[root.fileNameIdx - 1]) + Callabler.item_opened(r.val_str[root.parentPathIdx - 1] + "/" + r.val_str[root.fileNameIdx - 1]) } pointer-event(event) => { // TODO this should be clicked by double-click - https://github.com/slint-ui/slint/issues/4235 if (event.button == PointerEventButton.right && event.kind == PointerEventKind.up) { - Callabler.item_opened(r.val[root.parentPathIdx - 1]) + Callabler.item_opened(r.val_str[root.parentPathIdx - 1]) } else if (event.button == PointerEventButton.left && event.kind == PointerEventKind.up) { clicked_manual(); } //else if (event.button == PointerEventButton.middle && event.kind == PointerEventKind.up) { - // Callabler.item_opened(r.val[root.parentPathIdx - 1] + "/" + r.val[root.fileNameIdx - 1]) + // Callabler.item_opened(r.val_str[root.parentPathIdx - 1] + "/" + r.val_str[root.fileNameIdx - 1]) //} } } @@ -123,7 +123,7 @@ export component SelectableTableView inherits Rectangle { HorizontalLayout { spacing: 5px; - for f [idx] in r.val: Text { + for f [idx] in r.val_str: Text { width: root.column-sizes[idx + 1]; text: f; font-size: 12px; diff --git a/krokiet/ui/settings.slint b/krokiet/ui/settings.slint index 9a646f5..8634238 100644 --- a/krokiet/ui/settings.slint +++ b/krokiet/ui/settings.slint @@ -1,13 +1,19 @@ + +import {IncludedDirectoriesModel, ExcludedDirectoriesModel} from "common.slint"; + export global Settings { in-out property settings_preset_idx: 0; in-out property <[string]> settings_presets: ["Preset 1", "Preset 2"]; - - in-out property <[StandardListViewItem]> included_directories: [{text: "ABCD"}, {text: "BCDA"}]; - in-out property <[StandardListViewItem]> excluded_directories: [{text: "ABCD"}, {text: "BCDA"}, {text: "CDFFF"}]; + + in-out property <[IncludedDirectoriesModel]> included_directories_model: [{path: "/home/path", referenced_folder: false, selected_row: false}]; + in-out property included_directories_model_selected_idx: -1; + in-out property <[ExcludedDirectoriesModel]> excluded_directories_model: [{path:"/home/path", selected_row: false}, {path:"/home/a", selected_row: false}]; + in-out property excluded_directories_model_selected_idx: -1; // Settings in-out property excluded_items: "Excluded items"; in-out property allowed_extensions: "Allowed extensions"; + in-out property excluded_extensions: "Excluded extensions"; in-out property minimum_file_size: 0; in-out property maximum_file_size: 0; in-out property recursive_search: true; diff --git a/krokiet/ui/settings_list.slint b/krokiet/ui/settings_list.slint index c85c17b..d0352cc 100644 --- a/krokiet/ui/settings_list.slint +++ b/krokiet/ui/settings_list.slint @@ -69,7 +69,7 @@ component MinMaxSizeComponent inherits HorizontalLayout { spacing: 20px; Text { horizontal-stretch: 0.0; - text:"Items Size(Bytes)"; + text:"File Size(Kilobytes)"; vertical-alignment: TextVerticalAlignment.center; } HorizontalLayout { @@ -205,6 +205,10 @@ export component SettingsList inherits VerticalLayout { name: "Allowed extensions:"; model <=> Settings.allowed_extensions; } + TextComponent { + name: "Excluded extensions:"; + model <=> Settings.excluded_extensions; + } MinMaxSizeComponent { } @@ -263,7 +267,7 @@ export component SettingsList inherits VerticalLayout { model <=> Settings.duplicate_minimal_prehash_cache_size; } CheckBoxComponent { - name: "Delete outdated entries"; + name: "Delete automatically outdated entries"; model <=> Settings.duplicate_delete_outdated_entries; } HeaderText { @@ -274,21 +278,21 @@ export component SettingsList inherits VerticalLayout { model <=> Settings.similar_images_show_image_preview; } CheckBoxComponent { - name: "Delete outdated entries"; + name: "Delete automatically outdated entries"; model <=> Settings.similar_images_delete_outdated_entries; } HeaderText { text: "Similar Videos tool"; } CheckBoxComponent { - name: "Delete outdated entries"; + name: "Delete automatically outdated entries"; model <=> Settings.similar_videos_delete_outdated_entries; } HeaderText { text: "Similar Music tool"; } CheckBoxComponent { - name: "Delete outdated entries"; + name: "Delete automatically outdated entries"; model <=> Settings.similar_music_delete_outdated_entries; } ConfigCacheButtons { diff --git a/krokiet/ui/tool_settings.slint b/krokiet/ui/tool_settings.slint index 1f735dd..84b28ef 100644 --- a/krokiet/ui/tool_settings.slint +++ b/krokiet/ui/tool_settings.slint @@ -13,7 +13,6 @@ import {ColorPalette} from "color_palette.slint"; import {GuiState} from "gui_state.slint"; import { Preview } from "preview.slint"; import {PopupNewDirectories} from "popup_new_directories.slint"; -import { PopupSelect } from "popup_select.slint"; component ComboBoxWrapper inherits HorizontalLayout { in-out property text; @@ -70,21 +69,21 @@ export component ToolSettings { ComboBoxWrapper { text: "Hash size"; model: Settings.similar_images_sub_available_hash_size; - current_index: Settings.similar_images_sub_hash_size_index; + current_index <=> Settings.similar_images_sub_hash_size_index; } ComboBoxWrapper { text: "Resize Algorithm"; model: Settings.similar_images_sub_available_resize_algorithm; - current_index: Settings.similar_images_sub_resize_algorithm_index; + current_index <=> Settings.similar_images_sub_resize_algorithm_index; } ComboBoxWrapper { text: "Hash type"; model: Settings.similar_images_sub_available_hash_type; - current_index: Settings.similar_images_sub_hash_type_index; + current_index <=> Settings.similar_images_sub_hash_type_index; } CheckBoxWrapper { text: "Ignore same size"; - checked: Settings.similar_images_sub_ignore_same_size; + checked <=> Settings.similar_images_sub_ignore_same_size; } SliderWrapper { text: "Max difference";